三、SpringMVC核心概念

102 阅读9分钟

一,Spring MVC简介

Spring MVC 是 Spring 框架中用于构建 Web 应用程序的核心模块,它基于 MVC(Model-View-Controller)设计模式,提供了灵活、解耦的架构,适合开发动态网页或 RESTful API。


核心概念

  1. MVC 模式
    • Model(模型):封装业务数据和业务逻辑(如 Java 对象、Service 层)。
    • View(视图):负责数据展示(如 JSP、Thymeleaf、HTML)。
    • Controller(控制器):处理用户请求,协调 Model 和 View(如 @Controller 类)。
  2. 核心组件
    • DispatcherServlet:前端控制器,统一处理请求和响应。
    • HandlerMapping:将请求映射到对应的 Controller 方法。
    • Controller:处理具体业务逻辑。
    • ViewResolver:解析视图名称到具体的视图技术(如 JSP)。
    • ModelAndView:封装模型数据和视图信息。

二,快速入门

  1. 导包

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.12.RELEASE</version>
    </dependency>
    
  2. 创建SpringMVC控制类(等同于Servlet功能)

    @Controller
    public class DemoController {
        
        @RequestMapping("/save")
        @ResponseBody
        public String save(){
            System.out.println("user save...");
            return "{'info':'springmvc'}";
        }
        
    }
    
  3. 初始化SpringMVC环境(同Spring环境)设定SpringMVC加载对应的bean

    @Configuration
    @ComponentScan("com.springmvc.controller")
    public class SpringMvcConfig {
    }
    
  4. 初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理请求

    public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
        @Override
        protected WebApplicationContext createServletApplicationContext() {
            //注册springmvc的配置
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            ctx.register(SpringMvcConfig.class);
            return ctx;
        }
    
        @Override
        protected String[] getServletMappings() {
            //设定哪些请求交给SpringMVC处理
            return new String[]{"/"};
        }
    
        @Override
        protected WebApplicationContext createRootApplicationContext() {
            return null;
        }
    }
    

    简化写法:

    public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    
        @Override
        protected Class<?>[] getRootConfigClasses() {
            //设置Spring配置类
            return new Class[]{SpringConfig.class};
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses() {
            //设置SpringMvc配置类
            return new Class[]{SpringMvcConfig.class};
        }
    
        @Override
        protected String[] getServletMappings() {
           	//设定哪些请求交给SpringMVC处理
            return new String[]{"/"};
        }
    }
    

几个注解:

  1. @Controller 注解
    • 类型:类注解
    • 位置:SpringMVC控制器类定义上方
    • 作用:设定SpringMVC的核心控制器
  2. @RequestMapping 注解
    • 类型:方法/类注解
    • 位置:SpringMVC控制器类/方法定义上方
    • 作用:设定当前控制器类/方法的请求路径,设定在类上面就相当于这个类的所有方法的前缀路径
  3. @ResponseBody 注解
    • 类型:方法注解
    • 位置:SpringMVC控制器方法定义上方
    • 作用:设置当前控制器方法响应内容为当前返回值,无需解析

入门案例工作流程分析:

  • 启动服务器初始化过程
    1. 服务器启动,执行ServletContainerInitconfig类,初始化web容器。
    2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象
    3. 加载SpringMvcConfig
    4. 执行@ComponentScan加载对应的bean
    5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法
    6. 执行getServletMapping方法,定义所有请求都通过SpringMVC image
  • 单次请求过程
    1. 发送请求localhost/save
    2. web容器发现所有的请求都经过SpringMvc,将请求交给SpringMVC处理
    3. 解析请求路径/save
    4. 由/save匹配执行对应的方法save()
    5. 执行save()
    6. 检测到有@RequestBody直接将save()方法的返回值作为响应体返回给请求方。

三,bean加载控制

在上述案例中,SpringMVC有SpringMVC相关的bean,Spring有他相关的bean这就会出现,SpringMVC可能加载到Spring相关的bean

解决方法:

  • SpringMVC相关bean加载控制

    • SpringMVC加载的bean对应的包均在com.xxx.controller包内
  • Spring相关bena加载控制:

    1. 方法一:Spring加载的bean设定范围为com.xxx排除controller里面的bean

      @Configuration
      @ComponentScan(value = "com.springmvc",
          excludeFilters = @ComponentScan.Filter(
                  type = FilterType.ANNOTATION,//按照注解过滤
                  classes = Controller.class
          )
      )
      public class SpringConfig {
      }
      
    2. 方法二:Spring加载的bean设定扫描范围为精度范围,例如service包,dao包等。

      @Configuration
      @ComponentScan({"com.springmvc.service","com.springmvc.dao"})
      public class SpringConfig {
      }
      
    3. 方法三:不区分Spring和SpringMVC环境,统一加载到同一环境。


@ComponentScan注解:

  • 类型:类注解
  • 属性:
    • excludeFilters:排除扫描路径中加载的bean,需要指定类别type与具体项classes
    • includeFilters:指定加载的bean,需要指定类别type与具体项classes

四,请求

4.1 Get请求和Post请求发起方式

Get请求传参是直接在url里面传递的:

传参方式举例:

hppt://localhost/getParam?key1=value1&key2=value2

控制层代码:

@RequestMapping("/getParam")
@ResponseBody
public String save(String key1,String key2){
    System.out.println("user save...");
    return "{'info':'springmvc'}";
}

Post请求参数是写在body里面的,控制层的接受和get请求是一样的。


中文乱码处理:在Servlet配置类中配置过滤器

public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
    //配置过滤器
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        return new Filter[]{filter};
    }
}

4.2 五种类型参数传递

4.2.1 普通参数传递

image 如果请求参数名字和形参名字**不一致解决方法:** image

4.2.2 实体类参数传递

image

4.2.3 嵌套实体类参数传递

image

4.2.4 数组参数传递

image

4.2.5 集合参数传递

image

4.3 json数据传递

大部分情况下我们前后端都是通过json来交互的,故json数据传递,如下:

  1. 导入jason坐标

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.0</version>
    </dependency>
    
  2. 在SpringMVC配置文件中开启json转化

    @Configuration
    @ComponentScan("com.springmvc.controller")
    @EnableWebMvc//开启json转化
    public class SpringMvcConfig {
    }
    
  3. 在控制层加入注解@RequestBody

    @RequestMapping("/save")
    @ResponseBody
    public String save(@RequestBody User user){
        System.out.println("user save...");
        return "{'info':'springmvc'}";
    }
    

    @RequestBody注解:

    • 类型**:形参注解**

    • 位置:SpringMVC控制器方法形参定义前面

    • 作用:将请求体中所包含的数据传递给请求对象,此注解一个处理器方法只能使用一次。

4.4 日期类型参数传递

  • 日期类型数据基于系统不同,格式也是不同的,例如:
    • 2088-08-18
    • 2088/8/18
    • 08/18/2088

这个String转化为Data底层采用的是类型转化器如下image

4.5 路径参数传递

image

@PathVarible注解:

  • 类型:形参注解
  • 位置:SpringMVC控制器方法形参定义签名
  • 作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应。

五,响应

5.1 响应页面

响应页面只需要返回String字符串,返回这个页面的路径即可,这个路径就是从resource目录开始走的路径。 image

5.2 响应数据

5.2.1 文本数据

文本数据的返回只需要添加注解@ReponseBody在方法上即可 image

5.2.2 json数据

返回Json数据就是在方法上返回对象,然后加上注解@ReponseBody在方法上即可 image


@ReponseBody注解:

  • 类型:方法/类注解注解
  • 位置:SpringMVC控制器方法/类上方定义
  • 作用:设置当前控制器方法响应内容为当前返回值,无需解析
  • 如果定义在类上就代表这个类所有方法响应内容为当前返回值,无需解析

这个转换底层采用了如下接口: image

5.3 统一响应格式

我们在正常的请求中需要按照格式返回统一的json格式,格式如下:

@Data
public class ApiResponse {
    private int status;
    private String message;
    private Object data;

    public ApiResponse() {}

    public ApiResponse(int status, String message, Object data) {
        this.status = status;
        this.message = message;
        this.data = data;
    }


    public static ApiResponse success(Object data) {
        return new ApiResponse(200, "Success", data);
    }

    public static  ApiResponse success(String message, Object data) {
        return new ApiResponse(200, message, data);
    }

    public static ApiResponse error(int status, String message) {
        return new ApiResponse(status, message, null);
    }

    public static ApiResponse error(int status, String message, Object data) {
        return new ApiResponse(status, message, data);
    }

    @Override
    public String toString() {
        return "ApiResponse{" +
                "status=" + status +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}

六,RESTFUL风格

  • REST(Representational State Transfer):表现形式状态转换。
    • 传统风格资源描述形式:
      • http://localhost/user/getById?id=1
      • http://localhost/user/getById?id=1
    • REST风格描述形式:
      • http://localhost/user/1
      • http://localhost/user
  • 优点:
    • 隐藏资源的访问行为,无法通过地址得知对资源进行何种操作
    • 书写简化

image


@RequestMapping添加对应属性

  • @RequestMapping(value="/user",method=RequestMethod.GET)对应GET方法
  • @RequestMapping(value="/user",method=RequestMethod.PUT)对应PUT方法
  • @RequestMapping(value="/user",method=RequestMethod.DELETE)对应DELETE方法
  • @RequestMapping(value="/user",method=RequestMethod.POST)对应POST方法

也可以通过如下方法

  • @GetMapping("")对应GET方法
  • @PutMapping("")对应PUT方法
  • @DeleteMapping("")对应DELETE方法
  • @PostMapping("")对应POST方法

举例:

@RestController
public class Demo{
    @GetMapping("/save")
	@ResponseBody
	public String save(@RequestBody User user){
    	System.out.println("user save...");
    	return "{'info':'springmvc'}";
	}
}

@RestController注解:

  • 类型:类注解
  • 位置:基于SpringMVC的RESTful开发控制器类上方
  • 作用:设置当前控制器类为RESTful风格,等同于@Controller和@ResponseBody两个注解组合

七,拦截器

image 拦截器:一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。

作用

  • 在指定的方法调用前后执行预先设定的代码。
  • 阻止原始方法的执行。

拦截器与过滤器的区别:

  • 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
  • 拦截内容不同:Filter对所有访问进行增强,Interceptor只对SpringMVC的访问进行增强。

7.1 快速入门

  1. 声明拦截器bean,并实现HandlerInterceptor接口(注意加载bean扫描)

    @Component
    public class ProjectInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //方法执行之前运行
            
            //true代表放行,false代表拦截不放行
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            //方法执行后运行
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            //postHandle方法执行后运行
        }
    }
    
  2. 定义配置类,继承WebMvcConfiguartionSupport实现addInterceptor方法(注意扫码配置)

    @Configuration
    public class SpringMvcSupport extends WebMvcConfigurationSupport {
        @Override
        protected void addInterceptors(InterceptorRegistry registry) {
            
        }
    }
    
  3. 添加拦截器并设定拦截器的访问路径,路径可以通过可变参数设置多个

    @Configuration
    public class SpringMvcSupport extends WebMvcConfigurationSupport {
        @Autowired
        private ProjectInterceptor projectInterceptor;
        @Override
        protected void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(projectInterceptor)
                    .addPathPatterns("/book","/users");
        }
    }
    

    单个拦截器执行流程 image

7.2 拦截器参数

  • 前置处理

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //方法执行之前运行
    
        //true代表放行,false代表拦截不放行
        return true;
    }
    
    • 参数:
      • request:请求对象
      • response:响应对象
      • jamdr:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再封装。
    • 返回值:
      • 返回值为false:被拦截的处理器不执行。
      • 返回值为true:放行。
  • 后置处理器

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //方法执行后运行
    }
    
    • 参数:
      • modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息并进行调整。
  • 完成后处理:

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //postHandle方法执行后运行
    }
    
    • 参数:
      • ex:如果处理器执行过程中出现异常,可以针对异常情况单独处理。

7.3 拦截路径

  • 拦截器可以根据需求,配置不同的拦截路径 image-20231103225004853

    image-20231103225019712

7.4 拦截器链配置

image