首页 > 技术文章 > Spring MVC

PxxxJ 2021-07-24 23:26 原文

一、SpringMVC简介

spring和springmvc的区别:

  • Spring是IOC和AOP的容器框架;

  • 而SpringMVC是基于Spring功能的Web框架,想用SpringMVC必须先依赖Spring;SpringMVC是一个MVC模式的WEB开发框架。

    Spring容器是一个父容器,SpringMVC容器是一个子容器,它继承自Spring容器。因此,在SpringMVC容器中,可以访问到Spring容器中定义的Bean,而在Spring容器中,无法访问SpringMVC容器中定义的Bean。在Web开发中,Controller全部在SpringMVC中扫描,除了Controller之外的Bean,全部在Spring容器中扫描(Service、Dao),按这种方式扫描,扫描完完成后,Controller可以访问到Service。

二、执行流程和原理

01、用户发送出请求被前端控制器DispatcherServlet拦截进行处理。
02、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
03、HandlerMapping找到具体的处理器(查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
04、DispatcherServlet调用HandlerAdapter(处理器适配器)。
05、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
06、Controller执行完成返回ModelAndView对象。
07、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
08、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
09、ViewReslover解析ModelAndView后返回具体View(视图)给DispatcherServlet。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应View给用户。

涉及组件分析:

  1. 前端控制器DispatcherServlet(不需要程序员开发)由框架提供,在web.xml中配置。
    作用:接收请求,响应结果,相当于转发器,中央处理器。
  2. 处理器映射器HandlerMapping(不需要程序员开发)由框架提供。
    作用:根据请求的url查找Handler(处理器/Controller),可以通过XML和注解方式来映射。
  3. 处理器适配器HandlerAdapter(不需要程序员开发)由框架提供。
    作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler中的方法。
  4. 处理器Handler(也称之为Controller,需要程序员开发)
    注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
    作用:接受用户请求信息,调用业务方法处理请求,也称之为后端控制器。
  5. 视图解析器ViewResolver(不需要程序员开发)由框架提供。
    作用:进行视图解析,把逻辑视图解析成真正的物理视图。
    SpringMVC框架支持多种View视图技术,包括:jstlView、freemarkerView、ThymeleafView等。
  6. 视图View(需要工程师开发)
    作用:把数据展现给用户的页面
    View是一个接口,实现类支持不同的View技术(jsp、freemarker、pdf等)

根据以上分析,SpringMVC需要程序员完成的工作有三个:

【1】配置前端控制器DispatcherServlet。

【2】开发后端控制器Handler/Controller。

【3】开发视图View。

三、springmvc原始代码

controller

package com.X;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mv = new ModelAndView();

        //业务代码
        String result = "HelloSpringMVC";
        mv.addObject("msg",result);
        //视图跳转
        mv.setViewName("test");

        return mv;
    }
}

spring-servlet

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd ">

    <!--处理器映射器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!--处理器适配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <bean id="/hello" class="com.X.HelloController"/>
</beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置dispatchServlet:前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--dispatchServlet要绑定Spring的配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--启动级别:1-->
        <load-on-startup>1</load-on-startup>
    </servlet>

   <servlet-mapping>
       <servlet-name>springmvc</servlet-name>
       <url-pattern>/</url-pattern>
   </servlet-mapping>

</web-app>

四、用注解开发

springmvc-servlet

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.x.controller"/>
    <mvc:default-servlet-handler/>
    <mvc:annotation-driven/>
    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

controller

package com.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController{
    @RequestMapping("/hello")
    public String hello(Model model){
        //封装数据
        model.addAttribute("msg","helloMVC");
        return "hello";//会被视图解析器处理
    }
}

五、restful风格

package com.x.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class RestFulController {
    //原来:http://localhost:8080/hello?a=2&b=3
    //restful:http://localhost:8080/x/2/3
    
    @RequestMapping(value = "/x/{a}/{b}",method = RequestMethod.DELETE)
    public String hello(@PathVariable int a,@PathVariable int b, Model model){
        int res = a+b;
        //封装数据
        model.addAttribute("msg",res);
        return "hello";//会被视图解析器处理
    }
    
    @GetMapping("/x/{a}/{b}")
    public String hello2(@PathVariable int a,@PathVariable int b, Model model){
        int res = a+b;
        //封装数据
        model.addAttribute("msg",res);
        return "hello";//会被视图解析器处理
    }
}
@GetMapping:扮演的是@RequestMapping(method =RequestMethod.GET) 的快捷方式。
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

六、请求的转发与重定向

通过springmvc来实现转发和重定向:

若不在springmvc-servlet里配置视图解析器

在controller中:

请求转发

@Controller
public class RestFulController {

    @GetMapping("/x")
    public String hello(Model model){
        model.addAttribute("msg","heavenly side");
       return "forward:/WEB-INF/jsp/hello.jsp";
    }
}

重定向:

return "redirect:index.jsp";

七、数据的处理

7.1 处理提交数据

提交的域名称和处理方法的参数名一致:

提交数据: http://localhost:8080/hello?name=kennys

@RequestMapping("/hello")
public String hello(String name){
    System.out.println(name);
    return hello;
}

提交的域名称和处理方法的参数名不一致

提交数据: http://localhost:8080/hello?username=kennys

@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
    System.out.println(name);
    return hello;
}

从前端接受的数据参数最好都加上@RequestParam(),更加规范易懂

提交的是一个对象

要求提交的表单域和对象的属性名一致

实体类:

public class User{
	private int id;
    private String name;
    private int age;
    //构造
    //get/set
    //toString
}

提交数据:http://localhost:8080/user?name=kennys&id=1&age=15

@RequestMapping("/user")
public String user(User user){
    System.out.println(user);
    return hello;
}

7.2 乱码问题

使用过滤器来解决页面显示乱码问题

package com.x.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")
public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

八、JSON

8.1 基本用法

  • JSON 是纯文本

JSON (JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

JSON 数据的书写格式是:

key : value

JSON 对象在大括号 {} 中书写:

{key1 : value1, key2 : value2, ... keyN : valueN }

对象可以包含多个名称/值对:

{ "name":"xxx" , "url":"xxx" }

JSON 数组

JSON 数组在中括号 [] 中书写:

数组可包含多个对象:

[
    { key1 : value1-1 , key2:value1-2 }, 
    { key1 : value2-1 , key2:value2-2 }, 
    { key1 : value3-1 , key2:value3-2 }, 
    ...
    { keyN : valueN-1 , keyN:valueN-2 }, 
]

因为 JSON 使用 JavaScript 语法,所以无需额外的软件就能处理 JavaScript 中的 JSON。

通过 JavaScript,您可以创建一个对象数组,并像这样进行赋值:

var sites = [
    { "name":"runoob" , "url":"www.runoob.com" }, 
    { "name":"google" , "url":"www.google.com" }, 
    { "name":"微博" , "url":"www.weibo.com" }
];

JSON和JavaScript对象互转

JSON--->JavaScript:

var obj = JSON.parse('{"a":"Hello","b":"world"}')

JavaScript--->JSON:

var json = JSON.stringify({a:'hello',b:'world'})

8.2 jackson

pom

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.3</version>
</dependency>
package com.x.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.x.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class UserController {

    @RequestMapping(value = "/x")
    @ResponseBody //不会走视图解析器,直接返回字符串
    public String json() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        User user = new User("kennyS",7,"boy");
        String s = mapper.writeValueAsString(user);
        return s;
    }

}

在controller类上加@RestController也会使所有方法只返回字符串

乱码问题配置:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

8.3 fastjson

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>

package com.x.controller;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.x.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
@Controller
public class UserController {

    @RequestMapping(value = "/x")
    public String json() throws JsonProcessingException {
        List<User> list = new ArrayList<>();
        User user1 = new User("kenny1", 3, "boy");
        User user2 = new User("kenny2", 3, "boy");
        User user3 = new User("kenny3", 3, "boy");
        User user4 = new User("kenny4", 3, "boy");
        list.add(user1);
        list.add(user2);
        list.add(user3);
        list.add(user4);
        String s = JSON.toJSONString(list);
        return s;
    }
}

JSON字符串--->Java:

JSON.parseObject()

Java--->JSON:

JSON.toJSON()

JSON--->Java:

JSON.toJavaObject()

九、拦截器

拦截器配置:

<!-- 配置拦截器 -->
<mvc:interceptors>
    <!-- 一个拦截器 -->
    <mvc:interceptor>
        <!-- 配置此拦截器所拦截的路径
            拦截所有请求:/** 表示所有进入springmvc的请求
        -->
        <mvc:mapping path="/**"/>
        <!-- 注册拦截器对象 -->
        <bean class="com.kennyX.config.MyIntercepter"/>
    </mvc:interceptor>
</mvc:interceptors>

拦截器类

/**
 * springmvc中的拦截器对象
 * 1.编写代码:实现拦截器接口 HandlerInterceptor
 * 2.配置拦截器
 */
public class MyInterceptor implements HandlerInterceptor {

    /**
     * preHandle方法,预处理
     * 作用:在请求进入指定Controller方法之前,执行的方法(对请求进行验证)
     * @param handler 请求将要执行的Controller对象
     * @return 布尔类型:true表示请求放行,继续向后执行(有可能进入下一个拦截器,也有可能进入Controller)
     * false表示拦截请求,请求不能继续向后执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor拦截器中的preHandle方法执行了...");
        return true;
    }

    /**
     * postHandle 后处理
     * @param handler 正在执行的Controller对象
     * @param modelAndView 模型和视图数据,Controller方法执行完成之后的返回值
     *
     * 注意:此方法必须在请求进入Controller之后才会执行,如果没有进入Controller是不会执行的
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor拦截器中的postHandle方法执行了...");
    }

    /**
     * afterCompletion 请求处理完成之后
     * @param handler 已经执行过的Controller对象
     * @param ex Controller方法抛出的异常对象
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor拦截器中的afterCompletion方法执行了...");
    }
}

推荐阅读