Spring MVC

Spring MVC基础

MVC

1. MVC概念

MVC 是Model、View、Controller的缩写,分别代表Web应用程序的三种职责

  • 模型:用于存储数据以及处理用户请求的业务逻辑
  • 视图:向控制器提交数据,显示模型中的数据
  • 控制器:根据视图提出的请求,判断将请求和数据交给什么模型去处理,处理后将有关结果交给哪个视图更新显示

2. 基于Servlet的MVC模式

基于Servlet 的MVC 模式的具体实现如下:

  • 模型:一个或多个JavaBean 对象,用于存储数据(实体模型,由JavaBean类创建)和处理业务逻辑(业务模型,由一般的Java类创建)
  • 视图:一个或多个JSP页面,向控制器提交数据和模型提供数据显示,JSP页面主要使用HTML标记和JavaBean标记来显示数据
  • 控制器:一个或多个Servlet对象,根据视图提交的请求控制,即将请求数据转发给处理业务逻辑的JavaBean,并将处理结果存放到实体模型JavaBean中,输出给视图显示。

一、MVC的工作原理

Spring MVC框架主要由DispatcherServlet、处理器映射、控制器、视图解析器,视图组成。

Spring MVC的工作流程如下:

  1. 客户端提交请求到DispatcherServlet

  2. 由DispatcherServlet 控制器寻找一个或多个 HandlerMapping ,找到处理请求的 Controller

  3. DispatcherServlet 将请求提交到Controller

  4. @RunWith(SpringJUnit4ClassRunner.class)         //Spring整合JUnit专用的类加载器
    @ContextConfiguration(classes = SpringConfig.class)     //指定Spring上下文配置
    public class AccountServiceTest {
        @Autowired
        private MyService myservice;
        @Test9    
        public void myTestOne(){ /*函数实现*/ };
    }
    
  5. DispatcherServlet 寻找一个或多个 ViewResoler 视图解析器,找到ModelAndView 指定的视图

  6. 视图复杂将结果显示到客户端

二、Spring MVC 接口

pring MVC 的四个接口: DispatcherServletHandlerMappingControllerViewResoler

Spring MVC 的所有请求都经过DispatcherServlet统一分发。DispatcherServlet将请求发送给Controller之前,需要借助 Spring MVC、提供的HandlerMapping定位到具体的Controller

  • HandlerMapping 接口负责完成客户请求到Controller的映射

  • Controller 接口将处理用户请求,这和Java Servlet实现的作用是一致的。一旦Controller处理完用户请求,则返回 ModelAndView对象给DispatcherServlet 前端控制器,ModelAndView 中包含模型(Model)和视图(View)。

从宏观考虑,DispatcherServlet是整个Web应用的控制器;从微观上考虑Controller 是单个HTTP请求处理过程中的控制器,而ModelAndView是HTTP请求过程中返回的模型(Model)和视图(View)

  • ViewResoler接口(视图解析器)在Web应用中负责查找View对象,从而将相应结果渲染给客户
学习目标
  1. 掌握基于SpringMVC获取请求参数与响应json数据操作
  2. 熟练应用基于REST风格的请求路径设置与参数传递
  3. 能够根据实际业务建立前后端开发通信协议并进行实现
  4. 基于SSM整合技术开发任意业务模块功能

三、SpringMVC简介

  • SpringMVC是一种基于Java实现MVC模型的轻量级Web框架

  • 优点

    1. 使用简单,开发编辑(相比于Servlet)

    2. 灵活性比较强

四、SpringMVC入门案例

  1. 使用SpringMVC技术需要先导入SpringMVC坐标和Servlet坐标

    <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.10.RELEASE</version>
    </dependency>
    
  2. 创建SpringMVC控制器类(等同于Servlet功能)

    @Controller								//使用注解声明bean交给IOC管理
    public class UserController {
        @RequestMapping("/save")			//设置当前操作的访问路径
        @RequestBody						//设置当前返回值类型,将返回的数据转为JSON
        public String save() {
            System.out.println("user save...");
            return "{'code':200}";
        }
    }
    
  3. 初始化SpringMVC环境(同spring环境),设定SpringMVC加载对应的bean

    //创建SpringMvc的配置文件,加载controller对应的bean
    @Configuration
    @ComponentScan("com.itheima.controller")
    public class SpringMvcConfig {}
    
  4. 初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求

    public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
       	//加载SpringMvc容器配置
    	protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            ctx.register(SpringMvcConfig.class);
            return ctx;		//该方法告诉tomact容器加载spring的配置
        }
        //设置哪些请求归属springMVC处理 
        protected String[] getServletMappings() {s
            return new String[]{"/"};	//将哪些请求交给SpringMvc处理
        }
        //加载Spring容器配置
        protected WebApplicationContext createRootApplication() {
            return null;
        }
    }
    

4.1 工作流程分析

  • 启动服务器初始化过程
    1. 服务器启动,执行ServletContainsInitConfig类,初始化web容器
    2. 执行createServletApplicationContext方法,创建WebApplicationContext对象
    3. 加载SpringMvcConfig
    4. 执行@ComponentScan加载对应的bean
    5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法
    6. 执行getServletMappings方法,定义所有的请求都通过SpringMVC

4.2 SpringMVC案例总结

  • SpringMVC入门程序开发总结(1+N)
  • 一次性工作
    • 创建工程,设置服务器,加载工程
    • 导入坐标
    • 创建web容器启动类,架子啊SpringMVC配置,并设置SpringMVC请求拦截路径
    • SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
  • 多次工作
    • 定义处理请求的控制器类
    • 定义处理请求的控制器方法,并设置映射路径(@RequestMapping)与返回json数据(@ResponseBody

4.3 注解@Controller

  • 名称:@Controller

  • 类型:类注解

  • 位置:SpringMVC控制器类定义上方

  • 作用:设定SpringMVC的核心控制器Bean

  • 范例

    @Controller
    public class UserController{}
    

4.4 注解@RequestMapping

  • 名称:@RequestMapping

  • 类型:方法注解

  • 位置:SpringMVC控制器方法定义上方

  • 作用:设置当前控制器方法请求访问路径

  • 范例

    @RequestMapping("/save")
    public void save(){
        System.out.println("user save ...");
    }
    
  • 相关属性:value(默认):请求访问路径

4.5 注解@ResponseBody

  • 名称:@ResponseBody

  • 类型:方法注解

  • 位置:SpringMVC控制器方法定义上方

  • 作用:设置当前控制器方法响应内容为当前返回值,无需解析

  • 范例:

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

五、bean的加载控制

创建的包目录一般含有如下确定的包:configcontrollerservicedao

  • SpringMVV相关的bean(表现层bean)
  • Spring控制的bean
    • 业务bean(Service)
    • 功能bean(DataSource等)

因为功能不同,如何避免Spring错误的加载到SpringMVC的bean?

加载Spring控制的bean的时候,排除掉SpringMVC控制的bean

  • SpringMVC相关bean加载控制
    • SpringMVC加载的bean对应的包均在controller包内
  • Spring相关bean的加载控制
    1. 方式一:Spring加载的bean设定扫描范围大一些,然后排除掉controller包内的bean
    2. 方式二:Spring加载的bean设定扫描范围为精确范围,例如service包、dao包等
    3. 方式三:不区分Spring和SpringMVC的环境,加载到同一个环境中

5.1 注解@ComponentScan

  • 名称:@ComponentScan

  • 类型:类注解

  • 范例:

    @Configuration
    @ComponentScan(Value = "com.itheima",
    	excludeFilter @ComponentScan.Filter(
    		type = FilterType.ANNOTATION,
        	classes = Controller.class
        )
    )
    public class SpringConfig {}
    
  • 属性

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

5.2 Controller加载控制与业务bean加载控制

  • bean的加载配置

    public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
        protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            ctx.register(SpringMvcConfig.class);
            return ctx;
        }
        
        protected WebApplicationContext createRootApplicationContext() {
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            ctx.register(SpringConfig.class);
            return ctx
        }
        
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    }
    
  • 简化开发

    public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
        protected Class<?>[] getServletConfigClasses(){
            return new Class[]{SpringMvcConfig.class};
        }
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
        protected Class<?>[] getRootConfigClasses(){
            return new Class[]{SpringConfig.class};
        }
    }
    

六、请求与响应

6.1 注解@RequestMapping

  • 名称:@RequestMapping

  • 类型:方法注解 类注解

  • 位置:SpringMVC控制器方法定义上方

  • 作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器请求访问路径前缀

  • 范例

    @Controller
    @RequestMapping("/user")
    public class UserController {
        @RequestMapping("/save")
        @ResponseBody
        public String save(){
            System.out.println("user save ...");
            return "{'module' : 'user save'}";
        }
    }
    

6.2 普通参数的传递

使用get请求并携带参数的时候

  • 携带参数的url路径:http://localhost/commonParam?name=itcast&age=15

  • 普通参数:url地址传参,地址参数名与形参名相同,定义形参即可接收参数

    @RequestMapping("/commonParam")
    @ResponseBody
    public String commonParam(String name,int age){
        System.out.println("参数name ==> " + name);
        System.out.println("参数age ==> " + age);
        return "{'module' : 'common param'}"
    }	//直接定义参数接收即可
    
  • 普通参数:请求参数名与形参变量名不同,使用@RequestParam绑定参数关系

    @RequestMapping("/commonParam")
    @ResponseBody
    public String commonParamDifferentName(@RequestParam("name") String userName,int age){
        System.out.println("参数userName ==> " + userName);
        System.out.println("参数age ==> " + age);
        return "{'module' : 'common param different name'}"
    }	//直接定义参数接收即可
    

使用post请求传递参数的时候

  • 普通参数:form表单post请求传参,表单参数名与形参变量名相同,定义形参即可接收
    @RequestMapping("/commonParam")
    @ResponseBody
    public String commonParam(String name,int age){
    	System.out.println("参数name ==> " + name);
        System.out.println("参数age ==> " + age);
        return "{'module' : 'common param'}"
    }	//直接定义参数接收即可
    
image-20230805145601246

6.3 注解@RequestParam

  • 名称:@RequestParam

  • 类型:形参注解

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

  • 作用:绑定请求参数与处理器方法形参间的关系

  • 范例:

    @RequestMapping("/commonParamDifferentName")
    @ResponseBody
    public String commonParamDifferentName(@RequestParam("name")String userName,int age){
        System.out.println("参数userName ==> " + userName);
        System.out.println("参数age ==> " + age);
        return "{'module' : 'common param different name'}" 
    }
    
  • 参数:

    • required:是否为必传参数
    • defaultValue:参数默认值

6.4 Post请求中文乱码处理

  • 为Web容器添加过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器

    public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
        //配字符编码过滤器
        protected Filter[] getServletFilters(){
            CharacterEncodingFilter filter = new CharacterEncodingFilter();
            filter.setEncoding("utf-8");
            return new Filter[]{filter};
        }
    }
    

6.5 POJO参数传递

  • POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数

image-20230814131902721

@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
    System.out.println("pojo参数传递 user ==> "+ user);
    return "{'module':'pojo param'}";
}

6.6 嵌套的POJO参数传递

  • 嵌套POJO:POJO对象中包含POJO对象
  • 参数接收:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
public class User{
    private String name;
    private int age;
    private Address address;
}

public class Address{
    private String province;
    private String city;
}

image-20230814132551606

@ResquestMapping("/pojoContainPojoParam")
@ResponseBody
public String pojoContainPojoParam(User user){
    System.out.println("pojo嵌套pojo参数传递 user ==> " + user);
    return "{'module' : 'pojo contain pojo param'}";
}

6.7 数组参数传递

  • 数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型形参即可接收参数

image-20230814133946331

@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
    System.out.println("数组参数传递 likes ==>" + Arrays.toString(likes));
    return "{'module' : 'array Param'}"
}

6.8 集合参数传递

  • 集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系

image-20230820181506172

@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
    System.out.println("集合参数传递 likes ==> " + likes);
    return "{'module' : 'list param'}";
}

6.9 JSON数据传递参数

  • JSON数据的转换需要使用到一些jar包,此处使用【jackson】
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>
  • 开启自动转换JSON数据的功能【注解@EnableWebMvc
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMvcConfig {
}

@EnableWebMvc注解功能强大,该注解整合多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换

6.10 @EnableWebMvc

  • 名称:@EnableWebMvc

  • 类型:配置类注解

  • 位置:SpringMvc配置类定义上方

  • 作用:开启SpringMVC多项辅助功能

  • 范例

    @Configuration
    @ComponentScan("com.itheima.controller")
    @EnableWebMvc
    public class SpringMvcConfig {}
    

6.11 @RequestBody

  • 名称:@RequestBody

  • 类型:形参注解

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

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

  • 范例

    @RequestMapping("/listParamForJson")
    @ResponseBody
    public String listParamForJson(@RequestBody List<String> likes){
        System.out.println("list common(json) 参数传递 list ==> " + likes);
        return "{'module' : 'list common for json param'}";
    }
    

6.12 @RequestBody和@RequestParam区别

  • 区别
    • @RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
    • @RequestBody用于接收json数据【application/json】
  • 应用
    • 后期开发中,发送json格式数据为主,@RequestBody应用较广
    • 如果发送非json格式数据,选用@RequestParam接收请求参数

6.13 日期参数传递

  • 日期类型数据基于系统不同格式也不同

    • 2088-08-18
    • 2088/08/18
    • 08/18/2088
  • 接收形参时,根据不同的日期格式设置不同的接收方式

    @RequestMapping("dataParam")
    @ResponseBody
    public String dataParam(Data data,
                           @DateTimeFormat(pattern = "yyyy-MM-dd")Date date1,
                           @DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss")Date date2){
        System.out.println("参数传递 date ==> " + date);
        System.out.println("参数传递 date(yyyy-MM-dd) ==> " + date1);
        System.out.println("参数传递 date(yyyy/MM/dd HH:mm:ss) ==> " + date2);
        return "{'module':'data param'}";
    }
    

6.14 @DateTimeFormat

  • 名称:@DateTimeFormat

  • 类型:形参注解

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

  • 作用:设定日期时间型数据格式

  • 范例

    @RequestMapping("/dataParam")
    @ResponseBody
    public String dataParam(Date date){
    	System.out.println("参数传递 date ==> " + date);
        return "{'module' : 'data param'}";
    }
    
  • 属性:pattern:日期时间格式字符串

七、异常处理器

  • 异常处理器
    • 集中的、统一的处理项目中出现的异常
@RestControllerAdvice
public class ProjectExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public Result doException(Exception ex){
        return new Result(666,null);
    }
}

7.1 @RestControllerAdvice

  • 名称:@RestControllerAdvice

  • 类型:类注解

  • 位置:Rest风格开发的控制器增强类定义上方

  • 作用:为Rest风格开发的控制器类做增强

  • 范例:

    @RestControllerAdvice
    public class ProjectExceptionAdvice {   
    }
    
  • 说明:

    • 此注解自带@ResponseBody注解与@Component注解,具备对应的功能

7.2 @ExceptionHandler

  • 名称:@ExceptionHandler

  • 类型:方法注解

  • 位置:专用于异常处理的控制器方法上面

  • 作用:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行

  • 范例:

    @RestControllerAdvice
    public class ProjectExceptionAdvice {
        @ExceptionHandler(Exception.class)
        public Result doException(Exception ex){
            return new Result(404,null);
        }
    }
    

说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常

八、拦截器

拦截器概念

  • 拦截器是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
  • 作用:
    1. 在指定的方法调用前后执行预先设定的代码
    2. 组织原始方法的执行

拦截器和过滤器区别

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

8.1 拦截器入门案例

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

    @Component
    public class ProjectInterceptor implements HandlerInterceptor {
        public boolean preHandle(..) throws Exception {
            System.out.println("preHanle...");
            return true;
        }
        public void postHanle(..) throws Exception {
            System.out.println("postHandle..");
        }
        public void afterCompletion(..) throws Exception {
            System.out.println("afterCompletion...");
        }
    }
    
  2. 定义配置类,继承WebMVCConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置)

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

    @Conxfuguration
    public class SpringMvcSupport extends WebMvcConfigurationSupport {
        @Autowired
        private ProjectInterceptor projectInterceptor;
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(projectInterceptor).addPathPatterns("/books");
        }
    }
    
  • 上述操作可以在SpringMVC配置类上实现【使用标准接口WebMvcConfigurer简化开发(注:侵入式较强)】

    @Configuration
    @CompoentScan("com.itheima.controller")
    @EnableWebMvc
    public class SpringMvcConfig implements WebMvcConfigurer{
        @Autowired
        private ProjectInterceptor projectInterceptor;
        
        public void addInterceptor(InterceptorRegistry registry) {
            registry.addInterceptor(projectInterceptor).addPathPatterns("/books",'/books/*');
        }
    }
    
    
    
    

8.2 拦截器执行流程

image-20230904105749071

8.3 拦截器参数配置

前置处理
public boolean preHandle(HttpServletRequest request,
                        HttpServletResponse response,
                        Object handler)throws Exception {
    System.out.println("preHandle...");
    return true;
}
  • 参数
    1. request:请求对象
    2. response:响应对象
    3. handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
  • 返回值
    1. 返回值为false,被拦截的处理器将不执行

完成后处理

public void afterCompletion(HttpServletRequest request,
                        	HttpServletResponse response,
                        	Object handler,
                           Exception ex) throws Exception {
    System.out.println("afterCompletion...");
}
  • 参数
    1. ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理

后置处理

public void postHandle(HttpServletRequest request,
                       HttpServletResponse response,
                       Object handler,
                      ModelAndView modelAndView) throws Exception {
    System.out.println("postHandle...");
}
  • 参数
    1. modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_橙留香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值