1、在传统的springMvc项目里面,我们会将DispatcherServlet配置在web.xml中,案例如下:
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
配置好servlet后,在DispatcherServlet的初始化方法中会构建SpringMVC的应用上下文并且初始化SpringMVC的九大组件如HandlerMapping、HandlerAdapter等。。。
除了在web.xml中配置servlet之外,配置自定义的过滤器Filter、还有web容器的监听器都是需要在web.xml中进行定义的,但是在springBoot中,这种配置方式以及被抛弃了,改为全自动配置,解析来我们来分析一下是springBoot中是如何自动配置SpringMVC的。
2、在springBoot中使用springMVC案例:
1、构建springBoot项目,且添加springMVC的web启动依赖,pom.xm中添加如下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2、编写Controller
@RestController
public class TestController {
@RequestMapping("test")
public String test(){
return "test success";
}
}
使用就是这么简单。
3、spring boot自动配置springMVC原理解析
在前面我们讲解的springBoot的核心原理的时候说过,在类路径下的spring.factories文件中定义了很多配置类,那么springMVC相关的配置类是否也在里面呢?我们去查看spring-boot-autoconfigure包下面的spring.factories文件,然后查找一下看看有没有webMvc相关的配置类,经过查找我们找到了如下配置类:
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
DispatcherServletAutoConfiguration:自动配置一个DispatcherServlet实例,然后将其添加到ServletContext web应用上下文中
ServletWebServerFactoryAutoConfiguration:装配一个ServletWeb服务工厂,用于根据服务配置来构建一个web服务,如构建Tomcat、jetty、Undertow等web服务。
ErrorMvcAutoConfiguration:自动配置一些默认的错误处理器以及错误页面解析
HttpEncodingAutoConfiguration:对http请求的编码自动配置
MultipartAutoConfiguration:文件上传解析器的自动配置
WebMvcAutoConfiguration:springMVC的自动配置,也是我们重点关注的对象。
3.1、WebMvcAutoConfiguration配置类详解
先看WebMvcAutoConfiguration这个类上的注解:
这个@Configuration注解这是一个配置类
@Configuration(proxyBeanMethods = false)
这个条件注解表示当前的应用类型是SERVLET的时候当前配置类才会被解析
@ConditionalOnWebApplication(type = Type.SERVLET)
这个条件注解表示当前的类装载器中需要有Servlet、DispatcherServlet、WebMvcConfigurer3个类的时候当前配置类才会被解析
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
这个条件注解表示当前不存在类型是WebMvcConfigurationSupport这个的一个bean的时候才会被解析
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
这个注解表示当前配置类被解析后立马就会解析DispatcherServletAutoConfiguration、TaskExecutionAutoConfiguration、ValidationAutoConfiguration三个配置类
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
有了上面这个解释之后我们来思考一个问题,为什么依赖spring-boot-starter-web这个包后,就能触发springMVC的自动配置呢?我们先看看spring-boot-starter-web这个包里面有什么东西,我们会发现spring-boot-starter-web这个包里面啥都没有,就只有一个pom.xml文件,文件中定义的依赖内容如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.3.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.3.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
可见一共依赖了5个包分别如下:
1、spring-boot-starter:spring boot的启动器,里面包含各种自动配置的配置类定义。
2、spring-boot-starter-json:依赖的json,这个项目里面默认依赖的是jackson相关依赖。
3、spring-boot-starter-tomcat:依赖tomcat封装相关,包含Servlet-api的依赖,以及一些Servlet东西相关依赖,比如注解@WebFilter、@WebListener、@WebServlet,还有tomcat容器相关的依赖。
4、spring-web:spring-web依赖
5、spring-webmvc:引入了spring-webmvc的依赖,那么DispatcherServlet就存在于当前的类装载器中了。
引入这些依赖后,WebMvcAutoConfiguration这个配置类的解析条件就会满足,那么就可以根据此类完成springMVC的相关配置,比如视图解析器InternalResourceViewResolver、本地换解析器LocaleResolver、RequestMappingHandlerAdapter、RequestMappingHandlerMapping、FormattingConversionService、ConfigurableWebBindingInitializer、Validator、ExceptionHandlerExceptionResolver、HttpMessageConverters等springMVC的核心组件,WebMvcAutoConfiguration这个类完成的功能跟springMVC注解驱动标签<mvc:annotation-driven/>是一样的。
3.2、根据需求自定义配置springMVC相关组件
有了3.1的内容后我们知道了在我们没有自己配置一个类型是WebMvcConfigurationSupport的时候,会默认使用WebMvcAutoConfiguration来进行springMVC配置,那么如果例如我们需要添加自己的拦截器的时候我们应该怎么配置????? 在spring-webmvc中为我们提供了WebMvcConfigurationSupport这个类,我们只需要继承WebMvcConfigurationSupport这个类,然后重写它的添加组件的方法,然后定义我们自己实现的这个类是一个配置类即可,因为在WebMvcConfigurationSupport这个类中也同样定义了springMVC核心组件组件的配置定义。
3.3、在springBoot中是如何配置DispatcherServlet的????
在讲解springMVC的自动配置类中我们发现了一个配置类就是DispatcherServletAutoConfiguration这个类就是完成前端控制器DispatcherServlet的自动配置的,在这个类中有如下bean定义:
配置一个类型是DispatcherServlet的bean且beanName=dispatcherServlet
@Bean(name = "dispatcherServlet")
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
这样定义了一个DispatcherServlet的bean在spring的应用上下文中。
3.4、如何将配置的DispatcherServlet实例bean添加到web容器如ServletContext中的呢???
同样在DispatcherServletAutoConfiguration的类中定义了一个静态内部类DispatcherServletRegistrationConfiguration这个类也是一个配置类,它的核心就是配置一个DispatcherServletRegistrationBean用于往web容器中添加DispatcherServlet实例,源码如下:
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
3.5、DispatcherServletRegistrationBean在什么阶段会向web容器中添加DispatcherServlet呢?????
我们先来查看DispatcherServletRegistrationBean类图关系:
那么在springBoot中web上下文是什么时候启动呢??????经过跟踪源码,我们发现在web类型是Servlet的时候,此时的spring的应用上下的类型是ServletWebServerApplicationContext注意在非springBoot的应用中次类型是WebServerApplicationContext,注意区分ServletWebServerApplicationContext这个类是在springBoot中定义的。在这个ServletWebServerApplicationContext实例中有一个onRefresh()方法来创建一个WebServer,这个onRefresh()方法实在什么时候调用的呢???其实在父类AbstratcApplicationContext的refresh()方法中,在实例化+初始化所有单例bean之前执行。
有了上面的知识,我们可以来查看创建WebServer的方法到底做了什么事情,源码如下:
@Override
protected void onRefresh() {
super.onRefresh();
try {
创建一个Web服务
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
创建Web服务实现:
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
1、先获取一个ServletWebServerFactory 服务工厂用于创建Web服务
ServletWebServerFactory factory = getWebServerFactory();
2、先找到容器中所有的ServletContextInitializer实例,调用其onStartup方法,然后在获
取一个Web服务。我们的DispatcherServelt、自定义Filter、Listener都在此步骤通过相关的注册器添加到
web容器中的。
this.webServer = factory.getWebServer(getSelfInitializer());
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
3.6、@WebFilter、@WebListener、@WebServlet注解的使用
定义一个Filter:其他的类似做法
@WebFilter(filterName = "helloFilter", urlPatterns = {"/*"})
@Order(Ordered.LOWEST_PRECEDENCE + 10)
public class HelloFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("【HelloFilter】 init . . . . .");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("【HelloFilter】 doFilter 被执行 . . . . .");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
System.out.println("【HelloFilter】 destroy . . . . .");
}
}
这样定义完成后,这个HelloFilter是不会被添加到web容器中的,还需要在启动类上添加@ServletComponentScan这个注解。表示扫描那些包路径下的Servlet相关的组件,这样HelloFilter才会有用。
当然处理使用注解的方式还可以使用在应用上下文中定义一个FilterRegistriationBean定义来进行Flter添加也是可以的。
4、本篇文章主要讲解的是springBoot中自动配置springMVC的原理,在学习之前需要掌握在非spring boot的环境下,spring mvc是如何配置的,这样有一个鲜明的对比才能有一个更好的理解。