一、注解配置Spring MVC
1.1 初始化类
🔑注解配置的原理
- 在servlet3.0之后的环境中,服务器(或者说容器)会在类路径(src目录下)中查询
javax.servlet.ServletContainerInitializer
接口的实现类,如果找到该实现类就用他来配置服务器 - 而Spring中就提供了
ServletContainerInitializer
接口的一个实现类——SpringServletContainerInitializer
,该实现类中又会查询WebApplicationInitializer
接口的实现类,在Spring3.2之后,对WebApplicationInitializer
创建了一个便利的基础实现类——AbstractAnnotationConfigDispatcherServletInitializer
,因此我们创建的初始化配置类WebInit只需继承AbstractAnnotationConfigDispatcherServletInitializer
并重写其方法,此时服务器会自动查找到我们创建的初始化类WebInit,然后用它来配置服务器
🔑初始化类需要重写的几个方法
方法 | 方法返回值类型 | 解释 |
---|---|---|
getRootConfigClasses() | Class<?>[] | 指定Spring配置类;将Spring配置类的class对象添加到Class数组中返回即可;可以指定多个Spring配置类,也可以不指定(因为方法的默认返回值是长度为0的数组) |
getServletConfigClasses() | Class<?>[] | 指定Spring MVC配置类;将Spring MVC配置类的class对象添加到Class数组中返回即可;同样可以指定多个Spring MVC配置类 |
getServletMappigs() | String[] | 指定前端控制器DispatcherServlet 的映射规则(资源路径);将资源路径添加到String数组中返回即可;相当于web.xml中<url-pattern> 标签的作用,可以设置多个资源路径 |
getServletFilters() | Filter[] | 配置过滤器;将过滤器类的class对象添加到Class数组中返回即可,过滤器的class在数组中位置决定了过滤器的配置顺序;也可以配置多个过滤器 |
❓ 关于Spring配置类
- 在Spring的学习中,可以创建一个类,类上添加
@Configuration
、@ComponnetScan
等注解,将类标识为配置类,然后在类中通过@Bean
注解添加其他配置,用于代替Spring的配置文件,Spring MVC配置类与之类似- 在Spring MVC中添加Spring配置类属于SSM整合部分,这里只是简单创建一个被
@Configuration
标识的类作为Spring配置类,不添加其他功能实现;或者不添加也可以
1.2 Spring MVC配置类
💬概述:Spring MVC配置类命名为WebConfig,用于代替Spring MVC的核心配置文件——springmvc.xml,springmvc.xml中配置过的功能就是WebConfig类中需要实现的功能
🔑WebConfig的具体实现
💡 前5个必须配置,后面的选择配置
-
将WebConfig标识为配置类:在类上添加
@Conguration
注解 -
开启组件扫描:在WebConfig配置类上添加
@ComponnetScan
注解,注解的basePackages
属性中添加需要扫描的包或类(basePackages
是数组,可以添加多个扫描包或类) -
开启注解驱动:在WebConfig类上添加
@EnableWebMvc
注解,表示开启注解驱动的意思,无需添加属性 -
开放对静态资源的访问(配置默认servlet):在springmvc.xml中开放对静态资源的访问使用的是
<mvc:default-servlet-handler>
标签,不是简单的<bean>
标签,所以在配置类中实现该功能需要先实现WebMvcConfigurer
接口,然后实现接口的configureDefaultServletHandling()
方法,在方法中直接通过形参configurer
调用enable()
方法即可/** * 1. 开放对静态资源的访问(配置默认servlet) */ @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); }
-
配置thymeleaf视图解析器(了解):在springmvc.xml文件配置thymeleaf时需要添加两个属性内部Bean,所以在配置类配置thymeleaf需要先创建出两个内部Bean,然后给thymeleafView视图解析器的属性赋值
/** * 2.1 配置模板解析器 */ @Bean public ITemplateResolver templateResolver() { WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext(); // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得 ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver( webApplicationContext.getServletContext()); // 设置视图前缀 templateResolver.setPrefix("/WEB-INF/templates/"); // 设置视图后缀 templateResolver.setSuffix(".html"); // 设置编码方式 templateResolver.setCharacterEncoding("UTF-8"); // 设置模板 templateResolver.setTemplateMode(TemplateMode.HTML); return templateResolver; } /** * 2.2 配置模板引擎,并为模板引擎注入模板解析器属性 */ @Bean public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver); return templateEngine; } /** * 2.3 配置thymeleaf视图解析器,并为解析器注入模板引擎属性 */ @Bean public ViewResolver getThymeleafView(SpringTemplateEngine templateEngine) { // 先创建一个thymeleaf视图解析器对象 ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); // 设置编码方式 viewResolver.setCharacterEncoding("utf-8"); // 注入模板引擎属性 viewResolver.setTemplateEngine(templateEngine); return viewResolver; }
-
配置拦截器:与开放对静态资源的访问类似,在springmvc.xml文件中配置拦截器使用的同样是mvc名称空间中的标签
<mvc:interceptors>
,所以配置拦截器同样需要实现WebMvcConfigurer
接口,然后实现addInterceptors()
方法,配置拦截器需要创建拦截器对象以及设置拦截路径-
创建拦截器对象:先创建好拦截器类
TestInterceptor
,然后在方法中直接new一个拦截器类对象即可 -
设置拦截路径:调用形参
registry
的addInterceptor()
方法,并将拦截器对象TestInterceptor
传入,然后在addInterceptor()
方法后通过addPathPatterns()
方法设置拦截路径
-
-
配置异常处理器
-
配置异常处理器有两种方式
① 异常处理器在springmvc.xml中是通过
<bean>
标签配置的,所以在配置类中需要使用@Bean
标识对应方法即可,然后在方法中直接new一个异常处理器对象(即SimpleMappingExceptionResolver
类型的对象)
② 异常处理器的配置还有另外一种方法——实现WebMvcConfigurer
接口的方法configureHandlerExceptionResolvers()
或者extendedHandlerExceptionResolvers()
,同样需要在方法中new一个SimpleMappingExceptionResolver
类型的对象,该方法的形参就是一个异常处理器集合resolvers
,通过异常处理器对象对属性赋值后,最后将该对象添加到resolvers
集合中,由此可见,该方法可以配置多个异常处理器 -
配置异常处理器时需要实现的两个功能,即给异常处理器对象的两个属性(
exceptionMappings
、exceptionAttribute
)赋值① 创建异常与视图名的映射关系:先创建一个
Properties
类型的对象prop
,然后通过prop
对象调用setProperty()
方法,将异常对应的全类名与视图名建立映射关系(形成键值对),然后调用异常处理器对象中exceptionMappings
属性对应的set方法,即setExceptionMappongs()
,将prop
传入即可❓ 关于
Properties
类:Properties
类继承Hashtable
类,而Hashtable
类实现了Map
接口,所以Properties
类也是通过键值对的形式对数据进行存储,一般用于操作.properties文件(也是键值对形式存储数据)② 设置request域中异常信息的数据名(键):直接调用
exceptionAttribute
属性对应的set方法——setExceptionAttribute()
方法给属性赋值即可,属性值就对应request域中异常信息的键
/** * 4. 方式一配置异常处理器 */ @Bean public SimpleMappingExceptionResolver getExResolver() { // 创建异常处理器对象 SimpleMappingExceptionResolver exResolver = new SimpleMappingExceptionResolver(); // 创建一个Properties对象,用于建立异常与视图名的映射关系 Properties prop = new Properties(); // 将数学异常与视图名error进行绑定 prop.setProperty("java.lang.ArithmeticException", "error"); // 给异常处理器的exceptionMappings属性赋值 exResolver.setExceptionMappings(prop); // 给exceptionAttribute赋值,设置request域中异常信息的键 exResolver.setExceptionAttribute("ex"); return exResolver; } /*+---------------------------------------------------------------------------------+*/ /** * 4. 方式二配置异常处理器 */ @Override public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { // 创建异常处理器对象 SimpleMappingExceptionResolver exResolver = new SimpleMappingExceptionResolver(); // 创建一个Properties对象,用于建立异常与视图名的映射关系 Properties prop = new Properties(); // 将数学异常与视图名error进行绑定 prop.setProperty("java.lang.ArithmeticException", "error"); // 给异常处理器的exceptionMappings属性赋值 exResolver.setExceptionMappings(prop); // 给exceptionAttribute赋值,设置request域中异常信息的键 exResolver.setExceptionAttribute("ex"); resolvers.add(exResolver); }
-
-
使用视图控制器:视图控制器同样需要实现
WebMvcConfigurer
接口的方法addViewControllers()
,方法中直接通过形参registry
调用方法addViewController()
设置请求路径,然后在addViewController()
方法后直接调用setViewName()
方法设置请求路径对应的视图名@Override public void addViewControllers(ViewControllerRegistry registry) { // 首页 registry.addViewController("/").setViewName("index"); }
1.3 完整配置过程
-
先删掉web.xml配置文件和springmvc.xml核心配置文件
-
创建出初始化类WebInit
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer { /** * 指定Spring配置类 * @return 返回Class数组,数组元素为配置类的class对象 */ @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } /** * 指定Spring MVC配置类 * @return 返回Class数组,数组元素为配置类的class对象 */ @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; } /** * 指定DispatcherServlet的映射规则,即资源路径 * @return 返回资源路径的字符串数组 */ @Override protected String[] getServletMappings() { return new String[]{"/"}; } /** * 配置过滤器 * @return 返回过滤器数组 */ @Override protected Filter[] getServletFilters() { // 1. 创建字符集过滤器CharacterEncodingFilter CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter(); // 1.1 设置encoding属性为utf-8 encodingFilter.setEncoding("utf-8"); // 2. 创建请求方式过滤器 HiddenHttpMethodFilter httpMethodFilter = new HiddenHttpMethodFilter(); // 3. 创建过滤器数组并返回 return new Filter[]{encodingFilter, httpMethodFilter}; } }
-
创建Spring配置类SpringConfig(这里只是简单的创建,并无具体实现)
@Configuration public class SpringConfig { // coding.. }
-
创建Spring MVC配置类WebConfig
@Configuration @ComponentScan(basePackages = "com.key.mvc") @EnableWebMvc public class WebConfig implements WebMvcConfigurer { /** * 1. 开放对静态资源的访问(配置默认servlet) */ @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } /* 2. 配置thymeleaf视图解析器 */ /** * 2.1 配置模板解析器 */ @Bean public ITemplateResolver templateResolver() { WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext(); // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得 ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(webApplicationContext.getServletContext()); // 设置视图前缀 templateResolver.setPrefix("/WEB-INF/templates/"); // 设置视图后缀 templateResolver.setSuffix(".html"); // 设置编码方式 templateResolver.setCharacterEncoding("UTF-8"); // 设置模板 templateResolver.setTemplateMode(TemplateMode.HTML); return templateResolver; } /** * 2.2 配置模板引擎,并为模板引擎注入模板解析器属性 */ @Bean public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver); return templateEngine; } /** * 2.3 配置thymeleaf视图解析器,并为解析器注入模板引擎属性 */ @Bean public ViewResolver getThymeleafView(SpringTemplateEngine templateEngine) { // 先创建一个thymeleaf视图解析器对象 ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); // 设置编码方式 viewResolver.setCharacterEncoding("utf-8"); // 注入模板引擎属性 viewResolver.setTemplateEngine(templateEngine); return viewResolver; } /** * 3. 配置拦截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { // 创建拦截器对象 TestInterceptor testInterceptor = new TestInterceptor(); // 添加拦截器对象,并设置拦截路径 registry.addInterceptor(testInterceptor).addPathPatterns("/**"); } /** * 4. 配置异常处理器 */ @Bean public SimpleMappingExceptionResolver getExResolver() { // 创建异常处理器对象 SimpleMappingExceptionResolver exResolver = new SimpleMappingExceptionResolver(); // 创建一个Properties对象,用于建立异常与视图名的映射关系 Properties prop = new Properties(); // 将数学异常与视图名error进行绑定 prop.setProperty("java.lang.ArithmeticException", "error"); // 给异常处理器的exceptionMappings属性赋值 exResolver.setExceptionMappings(prop); // 给exceptionAttribute赋值,设置request域中异常信息的键 exResolver.setExceptionAttribute("ex"); return exResolver; } /** * 5. 配置视图控制器 */ @Override public void addViewControllers(ViewControllerRegistry registry) { // 首页 registry.addViewController("/").setViewName("index"); // 用户测试页面 registry.addViewController("/testUser").setViewName("test-user"); // 测试成功页面 registry.addViewController("/success").setViewName("success"); // 测试报文信息转换页面 registry.addViewController("/testHttpMsg").setViewName("test-httpMsg"); } }
-
对xml文件测试过的功能进行再测试一次:异常处理、拦截器、报文信息转换、域对象共享数据等
二、总结
2.1 常用组件
常用组件 | 组件的实现和使用 | 作用 |
---|---|---|
前端控制器(中心控制器) DispatcherServlet | 由框架提供类型,开发人员只需在配置文件或配置类进行配置 | 对请求和相应进行统一处理,是整个控制层的中心,调用其他组件对请求进行处理 |
处理器映射器 HandlerMapping | 直接由框架提供,无需创建和配置 | 根据请求路径、请求方式等信息查找对应的处理器Handler ,即控制器方法,然后获取控制器方法中所有相关对象(包括控制器对象及其对应的拦截器对象等),最后返回一个HandlerExecutionChain 对象(处理执行链) |
处理器 Handler | 直接由框架提供,无需创建和配置 | 相当于控制器方法,对用户的请求做具体的处理,并将视图名交给视图解析器 |
处理器适配器 HandlerAdapter | 直接由框架提供,无需创建和配置 | 执行Handler (控制器方法),并返回一个ModelAndView 对象 |
视图解析器 ViewResolver/ThymeleafView | 由框架或其他依赖jar包提供类型,开发人员只需在配置文件或配置类进行配置 | 根据视图名对视图进行解析,返回解析后的视图对象 |
视图 View | 框架提供视图类View ,开发人员只需设置视图名 | 将视图数据展示到页面 |
2.2 执行流程
📚常见八股文:Spring MVC执行流程
🔑文字描述
-
浏览器向服务器发起请求,交给前端控制器
DispatcherServlet
统一处理,DispatcherServlet
对获取的请求路径进行解析,然后根据请求路径查找对应的请求映射(与@RequestMapping
的value
值进行匹配) -
如果匹配失败,即没有找到对应的请求映射,则
DispatcherServlet
将当前请求路径(或资源)交给服务器默认servletDefaultServlet
处理,如果请求是一些静态资源(.css、.js等),则默认servlet能够处理,并成功发送给浏览器;如果请求是其他资源或路径,则默认servlet也无法处理,最终页面可能报404错误 -
如果匹配成功,此时
DispatcherServlet
会调用处理器映射器HandlerMapping
对象进一步根据请求路径查找到对应的处理器Handler
(即控制器方法),然后把与Handler
相关的对象(包括控制器对象及其拦截器对象等)封装成一个HandlerExecutionChain
对象(处理执行链,用于调用拦截器相关方法)并返回 -
在查找到请求路径对应的
Handler
(控制器方法)后,DispatcherServlet
再根据Handler
查找合适的处理器适配器HandlerAdapter
来执行Handler
-
如果配置了拦截器,则在执行
Handler
前,会前执行拦截器的preHandle()
方法,如果方法返回值为true,则继续执行Handler
-
处理器适配器
HandlerAdapter
调用handle()
方法来间接执行Handler
,并将Handler
中所设置的模型数据和视图数据封装成一个ModelAndView
对象返回。在这个过程中,除了完成请求的具体处理,Spring MVC还会对控制器方法中已有的配置(添加的形参、其他的注解)进行额外的处理❓ 额外的处理
- 报文信息转换(HttpMessageConveter):将请求报文信息(Json数据、xml等)转换成Java对象,将Java对象转换成响应报文信息
- 数据类型转换:如果控制器方法中与请求参数对应的形参类型不是String,则Spring MVC会将String类型的请求参数转换成对应的形参类型
- 数据格式化:对请求消息进行数据格式化,如将字符串转换成格式化数字或格式化日期等
- 数据校验: 验证数据的有效性(长度、格式等)
-
如果配置了拦截器,则执行完
Handler
后会执行拦截器的postHandle()
方法 -
执行完
Handler
后会获取到一个ModelAndView
对象,DispatcherServlet
会先判断此时是否出现异常,如果出现异常,会调用异常处理器改变此时的视图名,根据新的视图名更新ModelAndView
对象 -
获取到最终的
ModelAndView
对象后,DispatcherServlet
会将该对象传给processDispatchResult()
方法,对模型数据和视图数据做进一步处理 -
在
processDispatchResult()
方法中调用render()
方法,根据视图名采用对应的视图解析器,对视图进行解析和渲染,最后返回一个视图对象view
-
如果配置了拦截器,则在渲染视图后会执行拦截器的
afterCompletion()
方法 -
最后根据视图对象
view
将视图数据展示到页面上
🔑图解