在 Servlet3.0 之前,使用web的三大组件:servlet、filter、listener都需要在web.xml中进行注册配置。
在 Servlet3.0 标准发布之后,提供了注解的支持,异步处理的支持和可插拔的插件的支持。
Tomcat7.0以上的版本才支持Servlet3.0标准。
1 Servlet3.0中ServletContainerInitializer
Servlet3.0标准中引入了一个新的内容:
shared libraries
和runtimes pluggability
。
Servlet 容器启动时会扫描当前应用里面每一个jar包的ServletContainerInitializer
的实现类。 并且ServletContainerInitializer
的实现类,必须绑定在META-INF/services/javax.servlet.ServletContainerInitializer
。文件的内容就是ServletContainerInitializer
实现类的全类名。
简单说:容器在启动应用的时候,会扫描当前应用每一个jar包里面META-INF/services/javax.servlet.ServletContainerInitializer
文件中指定的实现类,启动并运行这个实现类中的方法onStartup
。
ServletContainerInitializer
的实现类是可以使用注解@HandlersTypes
注解,作用是指定需要处理类型,容器启动的时候 将@HandlersTypes
注解中指定的类型下的子类(实现类和子接口等)传递过来。
onStartup
方法有两个参数:
Set<Class<>> set
:需要处理的类型。是@HandlersTypes
注解指定的类型的子类或者子接口。ServletContext servletContext
:代表当前web应用的ServletContext
对象。(一个Web应用对应一个ServletContext
对象)。可以使用它来注册三大组件。
1 | (value = {PersonService.class}) |
2 整合Spring MVC
以前用xml的配置方式,需要在web.xml
中先配置ContextLoaderListener
监听器,来加载Spring的父容器。然后配置DispatcherServlet
来配置Spring MVC前端控制器,加载子容器。
2.1 整合分析
我们可以查看spring-web-4.x.x.RELEASE
包中META-INF/services/javax.servlet.ServletContainerInitializer
文件里的内容:
1 | org.springframework.web.SpringServletContainerInitializer |
这就意味着在web容器启动的时候,会加载SpringServletContainerInitializer
类:
该实现类上标注了@HandlesTypes({WebApplicationInitializer.class})
。则Spring应用一启动,会加载WebApplicationInitializer
接口下的子类和子接口。并且为不是接口和抽象类的WebApplicationInitializer
组件创建对象。
WebApplicationInitializer
接口有三个抽象实现:
AbstractContextLoaderInitializer
:第一层抽象类,创建RootApplicationContext
根容器。
AbstractDispatcherServletInitializer
:第二层抽象类,创建一个web的容器。并创建一个DispatcherServlet
,将创建的DispatcherServlet
添加到ServletContext
中。
AbstractAnnotationConfigDispatcherServletInitializer
:第三层抽象类。注解方式创建根容器、创建DispatcherServlet
。
【总结】:以注解方式来启动springmvc,我们自己的配置类继承AbstractAnnotationConfigDispatcherServletInitializer
,并实现抽象方法,指定DispatcherServlet
的配置信息。
附 spring官方文档中推荐的配置方式:
以父子容器的形式配置,web容器用来扫描Controller,视图解析器,映射等等web相关功能组件。根容器用来扫描业务逻辑组件、数据源、事务等等。
1 | public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { |
2.2 整合
一、根容器配置类:
1 | // Spring的根容器不扫描标注了@Controller注解的类。 |
二、子容器配置类:
1 | // spring mvc子容器只扫描标注了@Controller注解的类,警用默认的过滤规则。 |
三、创建自定义初始化类,继承AbstractAnnotationConfigDispatcherServletInitializer
抽象类:
1 | // web容器启动的时候创建对象,调用方法来初始化容器以及前端控制器 |
2.3 定制SpringMVC的配置
一、使用@EnableWebMvc
注解,开启SpringMVC定制配置。相当于<mvc:annotation-driven />
。
二、配置组件:视图解析器、视图映射、静态资源映射、拦截器等等。
配置类实现WebMvcConfigurer
接口,就可以配置以上提到的内容。
但实现WebMvcConfigurer
接口里的方法太多了,所以我们通常继承WebMvcConfigurerAdapter
抽象类,它是WebMvcConfigurer
的一个空实现。
示例:
1 | "com.enhao.spring.mvc.annotation", excludeFilters = { (value = |
3 异步请求
Spring MVC的异步处理是基于Servlet3.0的异步处理。
一、方式一:控制器返回Callable
:
- 控制器返回
Callable
。 - SpringMVC就会异步的将
Callable
提交到TaskExecutor
使用一个隔离的线程进行执行。 DispatcherServlet
和所有的Filter
退出web容器的线程,但response保持打开状态。Callable
返回结果,SpringMVC将请求重新派发给容器,恢复之前的请求。- 根据
DispatcherServlet
返回的结果,SpringMVC继续进行视图渲染流程等(收请求-视图渲染)。
1 |
|
二、方式二:返回DeferredResult
一旦启用了异步请求处理功能 ,控制器就可以将返回值包装在DeferredResult
,控制器可以从不同的线程异步产生返回值。优点就是可以实现两个完全不相干的线程间的通信。
以创建订单为例,客户端发起请求,应用A接受到这个请求,但应用A并不处理这个请求,通过消息中间件将这个请求交给应用B处理。
首先接受到请求后,创建一个DeferredResult
对象,将这个对象保存起来,并将这个对象返回,这个请求就在等待中。当另外一个线程,例如消息中间件,调用了这个对象的deferredResult.setResult()
方法。请求就会得到响应。