创建项目导入POM依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.willow</groupId>
<artifactId>springmvc-annotation</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0-alpha-1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- web项目去掉web.xml的验证功能-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
ServletContainerInitializer
实现此功能。每个框架要使用ServletContainerInitializer
就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer
的文件,文件内容指定具体的ServletContainerInitializer
实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。
一般伴随着ServletContainerInitializer
一起使用的还有HandlesTypes
注解,通过HandlesTypes
可以将感兴趣的一些类注入到ServletContainerInitializerde
的onStartup方法作为参数传入。
Tomcat容器的ServletContainerInitializer
机制的实现,主要交由Context容器和ContextConfig监听器共同实现,ContextConfig监听器负责在容器启动时读取每个web应用的WEB-INF/lib
目录下包含的jar包的META-INF/services/javax.servlet.ServletContainerInitializer
,以及web根目录下的META-INF/services/javax.servlet.ServletContainerInitializer
,通过反射完成这些ServletContainerInitializer
的实例化,然后再设置到Context容器中,最后Context容器启动时就会分别调用每个ServletContainerInitializer
的onStartup方法,并将感兴趣的类作为参数传入
首先通过ContextConfig监听器遍历每个jar包或web根目录的META-INF/services/javax.servlet.ServletContainerInitializer
文件,根据读到的类路径实例化每个ServletContainerInitializer
;然后再分别将实例化好的ServletContainerInitializer
设置进Context容器中;最后Context容器启动时分别调用所有ServletContainerInitializer
对象的onStartup方法。
假如读出来的内容为com.seaboat.mytomcat.CustomServletContainerInitializer
,则通过反射实例化一个CustomServletContainerInitializer
对象,这里涉及到一个@HandlesTypes
注解的处理,被它标明的类需要作为参数值传入到onStartup方法。
2、加载这个文件指定的类(SpringServletContainerInitializer)
在jarMaven: org.springframework:spring-web:4.3.11.RELEASE 有对应的文件,META-INF/services/javax.servlet.ServletContainerInitializer
中指定了org.springframework.web.SpringServletContainerInitializer这个类
3、Spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
WebApplicationInitializer.class)//加载这个接口的实现类,抽象类,extends(
4、并且为WebApplicationInitializer组件创建对象
前提:(这些组件不是接口,不是抽象类才创建对象)这个类的三个子组件
AbstractContextLoaderInitializer (org.springframework.web.context)
AbstractDispatcherServletInitializer (org.springframework.web.servlet.support)
AbstractAnnotationConfigDispatcherServletInitializer (org.springframework.web.servlet.support)
4.1 AbstractContextLoaderInitializer
//AbstractContextLoaderInitializer protected void registerContextLoaderListener(ServletContext servletContext) { //创建根容器 WebApplicationContext rootAppContext = createRootApplicationContext(); if (rootAppContext != null) { ContextLoaderListener listener = new ContextLoaderListener(rootAppContext); listener.setContextInitializers(getRootApplicationContextInitializers()); servletContext.addListener(listener); } else { logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not return an application context"); } }
4.2 AbstractDispatcherServletInitializer 配置DispatcherServlet初始化器
protected void registerDispatcherServlet(ServletContext servletContext) { String servletName = this.getServletName(); Assert.hasLength(servletName, "getServletName() must not return empty or null"); //创建一个web的IOC容器 WebApplicationContext servletAppContext = this.createServletApplicationContext(); Assert.notNull(servletAppContext, "createServletApplicationContext() did not return an application context for servlet [" + servletName + "]"); //创建了DispatcherServlet; FrameworkServlet dispatcherServlet = this.createDispatcherServlet(servletAppContext); dispatcherServlet.setContextInitializers(this.getServletApplicationContextInitializers()); //将创建的DispatcherServlet添加到ServletContext中; Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); Assert.notNull(registration, "Failed to register servlet with name \'" + servletName + "\'.Check if there is another servlet registered under the same name."); registration.setLoadOnStartup(1); //DispatcherServlet的映射, registration.addMapping(this.getServletMappings()); registration.setAsyncSupported(this.isAsyncSupported()); //添加过滤器 Filter[] filters = this.getServletFilters(); if(!ObjectUtils.isEmpty(filters)) { Filter[] var7 = filters; int var8 = filters.length; for(int var9 = 0; var9 < var8; ++var9) { Filter filter = var7[var9]; this.registerServletFilter(servletContext, filter); } } this.customizeRegistration(registration); }
4.3 AbstractAnnotationConfigDispatcherServletInitializer 注解方式配置的DispatcherServlet初始化器
createRootApplicationContext()
protected WebApplicationContext createRootApplicationContext() { Class<?>[] configClasses = getRootConfigClasses();//传入一个配置类,这个抽象的,留给我们自己写的 if (!ObjectUtils.isEmpty(configClasses)) { AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext(); rootAppContext.register(configClasses); //添加配置类到容器 return rootAppContext; } else { return null; } }
// 重写了AbstractDispatcherServletInitializer 的方法 protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext(); //创建web的ioc容器 Class<?>[] configClasses = getServletConfigClasses(); //获取配置类 if (!ObjectUtils.isEmpty(configClasses)) { servletAppContext.register(configClasses); } return servletAppContext; }
5.SpringMvc 注解版无xml web应用
DispatcherServlet VS ContextLoaderListener
在 Spring MVC 中存在两种应用上下文:DispatcherServlet
创建的和拦截器 ContextLoaderListener
创建的上下文:
DispatcherServlet
:加载包含 web 组件的 bean,比如 controllers,view resolvers 和 hanlder mappings。ContextLoaderListener
:加载其他 bean,通常是一些中间层和数据层的组件(比如数据库配置 bean 等)。
在 AbstractAnnotationConfigDispatcherServletInitializer
中 DispatcherServlet
和 ContextLoaderListener
都会被创建,而基类中的方法就可用来创建不同的应用上下文:
getServletConfigClasses()
:定义DispatcherServlet
应用上下文中的 beansgetRootConfigClasses()
:定义拦截器ContextLoaderListener
应用上下文中的 beans
Note:为了使用 AbstractAnnotationConfigDispatcherServletInitializer
必须保证 web 服务器支持 Servlet 3.0 标准(如 tomcat 7 或更高版本) 。
5.1 Spring Web MVC 注解配置
import com.willow.config.WebConfig; import com.willow.config.RootConfig; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; /** * Created by Administrator on 2018/6/24. */ //web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器 public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //获取根容器的配置类;(Spring的配置文件) 父容器; protected Class<?>[] getRootConfigClasses() { return new Class<?>[]{RootConfig.class}; } //获取web容器的配置类(SpringMVC配置文件) 子容器; protected Class<?>[] getServletConfigClasses() { return new Class<?>[]{WebConfig.class}; } //获取DispatcherServlet的映射信息 // /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp; // /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的; protected String[] getServletMappings() { return new String[]{"/"}; } }
5.1.1 配置父容器,除了扫描Controller,其他都扫描
扫描,service,数据源等组件
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; /** * Created by Administrator on 2018/6/24. */ //Spring的容器不扫描controller;父容器 (value = "com.willow",excludeFilters = { .Filter(type = FilterType.ANNOTATION,classes = {Controller.class})}) public class RootConfig { }
5.1.2 配置web容器,只扫描Controller
继承WebMvcConfigurerAdapter 或者实现WebMvcConfigurer 接口,重写相应的方法,修改相应的配置
Spring 使用如下方法开启 MVC 的支持:
@EnableWebMvc
注解(JavaConfig):和@Configuration
注解一起使用<mvc:annotation-driven />
元素(XML 配置)
开启 MVC 支持,它会从 WebMvcConfigurationSupport
导入 Spring MVC 的配置,会在处理请求时加入注解的支持(比如 @RequestMapping
,@ExceptionHandler
等注解)。
如果需要自定义配置,从 @EnableWebMvc
的文档上来看,需要继承 @WebMvcConfigurer
接口或者继承基类 WebMvcConfigurerAdapter
(它继承了 @WebMvcConfigurer
接口,但是用空方法实现)。所以,覆盖相应的方法就能实现 mvc 配置的自定义。
import com.willow.web.MyInterceptor; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.web.servlet.config.annotation.*; //SpringMVC只扫描Controller;子容器 ,web容器 //useDefaultFilters=false 禁用默认的过滤规则; //web容器 (value = "com.willow",includeFilters = { .Filter(type = FilterType.ANNOTATION,classes = {Controller.class})},useDefaultFilters = false) //相当于<!-- <mvc:annotation-driven /> public class WebConfig extends WebMvcConfigurerAdapter { //或者实现 WebMvcConfigurer //文档地址 : https://docs.spring.io/spring/docs/5.0.2.RELEASE/spring-framework-reference/web.html#mvc-config-customize //将SpringMVC处理不了的请求交给tomcat;静态资源 就可以访问 public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); //相当于xml文件配置<mvc:default-servlet-handler/> } //视图解析器 public void configureViewResolvers(ViewResolverRegistry registry) { // TODO Auto-generated method stub //默认所有的页面都从 /WEB-INF/ xxx .jsp //registry.jsp(); registry.jsp("/WEB-INF/views/", ".jsp"); } //注册拦截器 public void addInterceptors(InterceptorRegistry registry) { // /** 任意存路径 registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**"); } }
5.2 Spring web MVC xml配置(相当于5.1配置的内容)
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app1-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app1</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
代码地址:https://github.com/yangliuwilow/study/tree/master/springmvc-annotation