springBoot中springmvc源码分析

我们在分析一个Springboot的SpringMvc时,首先我们先从简单的项目聊起

一个简单的例子
@SpringBootApplication
public class DemoProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoProviderApplication.class, args);
    }
}

@RestController
@RequestMapping("/test")
public class TestController {

    @PostMapping("/demo")
    public User demo(@RequestBody @Valid UserParams userParams){
        User user = new User();
        user.setAge(10);
        user.setName("demo");
        user.setId(1);
        return user;
    }
}
SpringApplication.run方法

一定大多数人有个疑问,Springboot是如何将Spring,SpringMvc,Tomcat联系起来的,接下来我们以项目启动的角度了解下(以下我们做核心代码分析)

	public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext(); // 1>
            context.setApplicationStartup(this.applicationStartup);
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context); // 2>
            this.afterRefresh(context, applicationArguments);
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }

            listeners.started(context, timeTakenToStartup);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            listeners.ready(context, timeTakenToReady);
            return context;
        } catch (Throwable var11) {
            this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var11);
        }
    }

我们仔细分析下这个run方法:

  1. 第一步创建了createApplicationContext,这个会根据依赖的类动态的创建实现类,如果是web,那么创建的是AnnotationConfigServletWebServerApplicationContext,因为一般我们依赖的是spring-boot-starter-web
  2. 第二部执行refreshContext,最终调用的是AbstractApplicationContext的refresh方法
public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();  // 3>
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var10) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
                }

                this.destroyBeans();
                this.cancelRefresh(var10);
                throw var10;
            } finally {
                this.resetCommonCaches();
                contextRefresh.end();
            }

        }
    }

这里调用的就是spring当中的方法了,我们只关注springmvc和Tomcat两部分
3. 我们直接看onRefresh方法,因为AnnotationConfigServletWebServerApplicationContext是ServletWebServerApplicationContext的子类,所以流程进入ServletWebServerApplicationContext的onRefresh()方法

 protected void onRefresh() {
        super.onRefresh();

        try {
            this.createWebServer(); // 4>
        } catch (Throwable var2) {
            throw new ApplicationContextException("Unable to start web server", var2);
        }
    }
    
 private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = this.getServletContext();
        if (webServer == null && servletContext == null) {
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            ServletWebServerFactory factory = this.getWebServerFactory(); // 5>
            createWebServer.tag("factory", factory.getClass().toString());
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); // 6> 
            createWebServer.end();
            this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
            this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
        } else if (servletContext != null) {
            try {
                this.getSelfInitializer().onStartup(servletContext);
            } catch (ServletException var5) {
                throw new ApplicationContextException("Cannot initialize servlet context", var5);
            }
        }

        this.initPropertySources();
    }
  1. 其中this.createWebServer();是关键方法
  2. getWebServerFactory()方法获取一个WebServer工厂类,因为我们默认用的tomcat,所以会获取到TomcatServletWebServerFactory
  3. factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); 我们再具体看下这个方法getSelfInitializer
	private ServletContextInitializer getSelfInitializer() {
        return this::selfInitialize;
    }

    private void selfInitialize(ServletContext servletContext) throws ServletException {
        this.prepareWebApplicationContext(servletContext);
        this.registerApplicationScope(servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(this.getBeanFactory(), servletContext);
        Iterator var2 = this.getServletContextInitializerBeans().iterator();

        while(var2.hasNext()) {
            ServletContextInitializer beans = (ServletContextInitializer)var2.next();
            beans.onStartup(servletContext);
        }

    }

这里返回的是一个ServletContextInitializer ,通过源码我们可以发现ServletContextInitializer 是@FunctionalInterface ,java8提供的lambda的一种函数式接口

@FunctionalInterface
public interface ServletContextInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;
}

继续回到第6部那factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()})的getWebServer方法

public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }

        Tomcat tomcat = new Tomcat();
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Iterator var4 = this.serverLifecycleListeners.iterator();

        while(var4.hasNext()) {
            LifecycleListener listener = (LifecycleListener)var4.next();
            tomcat.getServer().addLifecycleListener(listener);
        }

        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        this.customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        this.configureEngine(tomcat.getEngine());
        Iterator var8 = this.additionalTomcatConnectors.iterator();

        while(var8.hasNext()) {
            Connector additionalConnector = (Connector)var8.next();
            tomcat.getService().addConnector(additionalConnector);
        }

        this.prepareContext(tomcat.getHost(), initializers); // 7>
        return this.getTomcatWebServer(tomcat);
    }

可以看到tomcat的实现,向tomcat的容器当中注入Connector ,然后设置默认Host容器的AutoDeploy属性及其他的Tomcat初始化工作,最重要的一行是7)

    protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
        File documentRoot = this.getValidDocumentRoot();
        TomcatEmbeddedContext context = new TomcatEmbeddedContext();
        if (documentRoot != null) {
            context.setResources(new LoaderHidingResourceRoot(context));
        }

       	//省略部分代码
        ServletContextInitializer[] initializersToUse = this.mergeInitializers(initializers); //8>
        //将context作为host的子容器
        host.addChild(context);
        this.configureContext(context, initializersToUse); // 9>
        this.postProcessContext(context);
    }

第8步合并所有的ServletContextInitializer,然后调用configureContext方法

protected void configureContext(Context context, ServletContextInitializer[] initializers) {
        TomcatStarter starter = new TomcatStarter(initializers); //10>
        if (context instanceof TomcatEmbeddedContext) {
            TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext)context;
            embeddedContext.setStarter(starter);
            embeddedContext.setFailCtxIfServletStartFails(true);
        }

        context.addServletContainerInitializer(starter, NO_CLASSES); //11>
        //省略代码
    }

10)创建了TomcatStarter对象,并将starter加入context的conainerInitializer列表,见11),这样在tomcat的容器启动过程中就会调用到这个TomcatStarter实例。
我们来看下TomcatStarter做了什么


class TomcatStarter implements ServletContainerInitializer {
    private static final Log logger = LogFactory.getLog(TomcatStarter.class);
    private final ServletContextInitializer[] initializers;
    private volatile Exception startUpException;

    TomcatStarter(ServletContextInitializer[] initializers) {
        this.initializers = initializers;
    }

    public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
        try {
            ServletContextInitializer[] var3 = this.initializers;
            int var4 = var3.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                ServletContextInitializer initializer = var3[var5];
                initializer.onStartup(servletContext);
            }
        } catch (Exception var7) {
            this.startUpException = var7;
            if (logger.isErrorEnabled()) {
                logger.error("Error starting Tomcat context. Exception: " + var7.getClass().getName() + ". Message: " + var7.getMessage());
            }
        }

    }

    Exception getStartUpException() {
        return this.startUpException;
    }
}

可以看到TomcatStarter相当于hook了context启动的事件,然后调用所有注入的initializers的onStartup方法,似曾相识是吗?这就是前面说的@FunctionalInterface函数接口,接下来我们就深入看下前面提到的那个initializer的onStartup的具体内容

 private void selfInitialize(ServletContext servletContext) throws ServletException {
        this.prepareWebApplicationContext(servletContext);
        this.registerApplicationScope(servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(this.getBeanFactory(), servletContext);
        Iterator var2 = this.getServletContextInitializerBeans().iterator();

        while(var2.hasNext()) {
            ServletContextInitializer beans = (ServletContextInitializer)var2.next();
            beans.onStartup(servletContext);
        }

    }

可以看到对getServletContextInitializerBeans每个ServletContextInitializer执行了onStartup方法

    protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
        return new ServletContextInitializerBeans(this.getBeanFactory(), new Class[0]);
    }

看看new ServletContextInitializerBeans(getBeanFactory())做了什么

 @SafeVarargs
    public ServletContextInitializerBeans(ListableBeanFactory beanFactory, Class<? extends ServletContextInitializer>... initializerTypes) {
        this.initializerTypes = initializerTypes.length != 0 ? Arrays.asList(initializerTypes) : Collections.singletonList(ServletContextInitializer.class);
        this.addServletContextInitializerBeans(beanFactory);
        this.addAdaptableBeans(beanFactory);
        List<ServletContextInitializer> sortedInitializers = (List)this.initializers.values().stream().flatMap((value) -> {
            return value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE);
        }).collect(Collectors.toList());
        this.sortedList = Collections.unmodifiableList(sortedInitializers);
        this.logMappings(this.initializers);
    }

可以看到其从beanFactory中获取spring容器中所有的ServletContextInitializer实现,这里关于集成的部分在ServletRegistrationBean中,ServletRegistrationBean的注入过程参考

然后我们就可以理顺了, 我们会调用到ServletRegistrationBean的onStartup方法,最终会调用到servletContext.addServlet的Servlet3.0的标准将DispatchServlet注入到servlet容器中拦截所有的请求。

//RegistrationBean
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
    String description = getDescription();
    if (!isEnabled()) {
        logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
        return;
    }
    register(description, servletContext);
}
//DynamicRegistrationBean
@Override
protected final void register(String description, ServletContext servletContext) {
    D registration = addRegistration(description, servletContext);
    if (registration == null) {
        logger.info(
                StringUtils.capitalize(description) + " was not registered " + "(possibly already registered?)");
        return;
    }
    configure(registration);
}
//ServletRegistrationBean
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
    String name = getServletName();
    return servletContext.addServlet(name, this.servlet);
}

至此所有集成完毕,启动过程交给tomcat完成。

TomcatServletWebServerFactory

spring-boot-autoconfigure/META-INF/spring.factories中有一段配置:

...
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
...

然后我们来看下ServletWebServerFactoryAutoConfiguration类

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    ...
}

其中@Import部分引入了ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,深入看一下

@Configuration
class ServletWebServerFactoryConfiguration {
	@Configuration
	@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {
		@Bean
		public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
			return new TomcatServletWebServerFactory();
		}
	}
	...
}

这块Spring Boot根据@ConditionalOnClass判断当前运行时环境是否符合条件,即包含了tomcat的jar包,如果满足则创建TomcatServletWebServerFactory的Bean实例加入spring容器管理,后面有用

ServletWebServerApplicationContext

实际启动时,启动的是其子类AnnotationConfigServletWebServerApplicationContext,我们来看下SpringApplication类,实际上SpringApplication在运行时根据情况决定使用哪种ApplicationContext
在这里插入图片描述
在run方法时,创建applicationContext时
在这里插入图片描述
在这里插入图片描述
会根据传入的webApplicationType来创建applicationContext

DispatcherServletRegistrationBean

DispatcherServletRegistrationBean是保证我们的DispatcherServlet被注入到Servlet容器并生效的关键,我们来看下它是如何初始化的
spring-boot-autoconfigure/META-INF/spring.factories中有一段配置

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
	@Configuration
	@Conditional(DispatcherServletRegistrationCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {

		private final WebMvcProperties webMvcProperties;

		private final MultipartConfigElement multipartConfig;

		public DispatcherServletRegistrationConfiguration(WebMvcProperties webMvcProperties,
				ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
			this.webMvcProperties = webMvcProperties;
			this.multipartConfig = multipartConfigProvider.getIfAvailable();
		}

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
					this.webMvcProperties.getServlet().getPath());
			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
			registration.setLoadOnStartup(this.webMvcProperties.getServlet().getLoadOnStartup());
			if (this.multipartConfig != null) {
				registration.setMultipartConfig(this.multipartConfig);
			}
			return registration;
		}

	}
}

可以看到,其像spring容器注册了DispatcherServletRegistrationBean的Bean实例,看一下它的继承关系:
在这里插入图片描述
其父类ServletRegistrationBean类有如下方法:

@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
    String name = getServletName();
    return servletContext.addServlet(name, this.servlet);
}

其调用了ServletContext.addServlet方法将DispatchServlet加入到Servlet容器,这是Servlet3.0中注册servlet的方法。
那么你也许会问,addRegistration又是什么时机调用的呢?
根据继承关系,查看其父类的父类RegistrationBean,其有一个

@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
    String description = getDescription();
    if (!isEnabled()) {
        logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
        return;
    }
    register(description, servletContext);
}

register方法是一个模板方法,调用子类DynamicRegistrationBean的实现

@Override
protected final void register(String description, ServletContext servletContext) {
    D registration = addRegistration(description, servletContext);
    if (registration == null) {
        logger.info(StringUtils.capitalize(description) + " was not registered " + "(possibly already registered?)");
        return;
    }
    configure(registration);
}

addRegistration方法又是一个模板方法,实现就是前面ServletRegistrationBean的addRegistration实现,而onStartup方法会在SpringApplication.run()方法的流程中被调用到,讲主流程的时候已经讲到,这里不再赘述
这样就将DispatchServlet与Tomcat进行了集成,DispatchServlet使用模板方法设计模式,将具体的请求分配给不同的handler处理,这个后面会讲到,本篇就主要专注在Spring Boot与Spring MVC及Tomcat的集成原理部分

  • 40
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值