springboot7--->springboot是如何自动配置springmvc的原理分析

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是如何配置的,这样有一个鲜明的对比才能有一个更好的理解。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的springmvc-config.xml配置文件的例子: ```xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 开启注解扫描 --> <context:component-scan base-package="com.example.controller"/> <!-- 配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 配置静态资源处理 --> <mvc:resources mapping="/static/**" location="/static/"/> <!-- 配置RequestMappingHandlerAdapter --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> <value>text/html;charset=UTF-8</value> </list> </property> </bean> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> </list> </property> </bean> </list> </property> </bean> <!-- 配置RequestMappingHandlerMapping --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> </beans> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值