文章目录
- 1. 什么是SpringBoot的自动装配?
- 2. SpringBoot自动装配的底层原理
1. 什么是SpringBoot的自动装配?
Spring Boot的自动配置是一种机制,它使得开发者能够快速地开始构建Spring应用,而不需要手动编写大量的样板代码。Spring Boot的自动配置是通过@EnableAutoConfiguration注解触发的,它会自动根据类路径下的jar包依赖来启用相应的Spring应用上下文配置。要自定义Spring Boot的自动配置,你可以通过以下方式:
- 创建自己的@Configuration类,并通过@Configuration注解标记它。
- 使用@Conditional注解来限制配置类的条件激活,例如@ConditionalOnClass,@ConditionalOnMissingBean等。
- 使用@EnableAutoConfiguration注解中的exclude或excludeName属性来排除不需要的自动配置类。
- 在META-INF/spring.factories文件中配置自定义的自动配置类。
下面是一个简单的自定义自动配置的例子:
// 自定义配置类
@Configuration
public class MyCustomAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic MyService myService() {return new MyServiceImpl();}
}// 在spring.factories中添加自动配置的声明
// 在src/main/resources/META-INF/spring.factories文件中
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.example.MyCustomAutoConfiguration
在这个例子中,MyCustomAutoConfiguration是一个自定义的配置类,它提供了一个名为myService的bean,类型为MyService。@ConditionalOnMissingBean注解确保只有在应用上下文中没有提供MyService类型的bean时,才会创建这个bean。spring.factories文件中的条目告诉Spring Boot在启动时自动配置这个类。
2. SpringBoot自动装配的底层原理
我们先从SpringBoot的启动类开始:
@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}}
run
方法将当前类和一些系统参数作为参数调用,我们进入run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}
该方法是SpringApplication
的一个静态方法,然后 new SpringApplication(primarySources).run(args);
创建了一个新的SpringApplication
对象调用run方法。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {// 构造SpringApplication对象return new SpringApplication(primarySources).run(args);}
我们继续进入run方法:
public ConfigurableApplicationContext run(String... args) {long startTime = System.nanoTime();// 1、创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;configureHeadlessProperty();// 2、从spring.factories中获取SpringApplicationRunListener对象// 默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件SpringApplicationRunListeners listeners = getRunListeners(args);// 3、发布ApplicationStartingEventlisteners.starting(bootstrapContext, this.mainApplicationClass);try {// 4、将run()的参数封装为DefaultApplicationArguments对象ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 5、准备Environment// 包括操作系统,JVM、ServletContext、properties、yaml等等配置// 会发布一个ApplicationEnvironmentPreparedEventConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);// 默认spring.beaninfo.ignore=true,表示不需要jdk缓存beanInfo信息,Spring自己会缓存configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);// 6、根据应用类型创建Spring容器context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);// 7、利用ApplicationContextInitializer初始化Spring容器// 8、发布ApplicationContextInitializedEvent// 9、关闭DefaultBootstrapContext// 10、注册primarySources类,就是run方法存入进来的配置类// 11、发布ApplicationPreparedEvent事件prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 12、刷新Spring容器,会解析配置类、扫描、启动WebServerrefreshContext(context);// 空方法afterRefresh(context, applicationArguments);// 启动时间Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);}// 13、发布ApplicationStartedEvent事件,表示Spring容器已经启动listeners.started(context, timeTakenToStartup);// 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()callRunners(context, applicationArguments);}catch (Throwable ex) {// 15、发布ApplicationFailedEvent事件handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);// 16、发布ApplicationReadyEvent事件,表示Spring容器已经准备好了listeners.ready(context, timeTakenToReady);}catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;}
上面就是SpringBoot的启动流程的核心源码,这一部分我们先不详细分析,后面再分析,我只需要知道这个函数会创建一个Spring的容器,然后调用refreshContext(context);
方法,我们进入该方法:
private void refreshContext(ConfigurableApplicationContext context) {if (this.registerShutdownHook) {shutdownHook.registerApplicationContext(context);}refresh(context);}@Overridepublic final void refresh() throws BeansException, IllegalStateException {try {super.refresh();}catch (RuntimeException ex) {WebServer webServer = this.webServer;if (webServer != null) {webServer.stop();}throw ex;}}
然后在super.refresh
底层会调用一个onrefresh
方法,我们进入方法:
@Overrideprotected void onRefresh() {super.onRefresh();try {// 启动Tomcat 非web应用不用部署在Tomcat容器里吗createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}
在调用onRefresh
方法之前我们只需要知道一件事,springBoot此时将我们的Spring容器创建好了,并启动起来了。然后onRefresh
方法中调用了一个createWebServer();
方法,这个就是用来创建一个Web容器的,例如Tomcat和Jetty等。我们进入该方法:
private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();//如果web容器为空,且servelet容器为空,此时我们就需要去创建if (webServer == null && servletContext == null) {//ServletWebServerFactory就是一个创建Web服务器的工厂ServletWebServerFactory factory = getWebServerFactory();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();}
ServletWebServerFactory factory = getWebServerFactory();
上面代码就是用来创建Tomcat、Jetty等Web服务器的。我们进入这个工厂:
@FunctionalInterface
public interface ServletWebServerFactory {WebServer getWebServer(ServletContextInitializer... initializers);
}
这个工厂是一个函数式接口,然后就只有一个方法,就是getWebServer
意思就是获取我们的web服务器。然后我们看看这个工厂的实现类:
在这么多实现类中我们可以看到我们熟悉的两个实现类JettyServletWebServerFactory
和TomcatServletWebServerFactory
。我们看看Tomcat实现类:
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactoryimplements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {....@Overridepublic WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}//创建一个Tomcat服务器Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());//注册Tomcat生命周期的监听器for (LifecycleListener listener : this.serverLifecycleListeners) {tomcat.getServer().addLifecycleListener(listener);}//创建Tomcat的连接器组件Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);tomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);return getTomcatWebServer(tomcat);}
}
执行上面代码(内嵌Tomcat的实现方法)我们就可以获取一个Tomcat的实例,并启动起来。
那么我们现在思考一个核心的问题:Springboot怎么判断我们什么时候使用Tomcat什么时候使用Jetty呢?
下面我们分析一下上面的问题,首先我们回到createWebServer
方法:
private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();//如果web容器为空,且servelet容器为空,此时我们就需要去创建if (webServer == null && servletContext == null) {//ServletWebServerFactory就是一个创建Web服务器的工厂ServletWebServerFactory factory = getWebServerFactory();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();}
前面说到ServletWebServerFactory factory = getWebServerFactory();
真正会返回一个ServletWebServerFactory
具体的实现类的一个对象,然后调用具体工厂对象的getWebServer方法来获取真正的Web服务器实例。我们进入getWebServerFactory
方法。
protected ServletWebServerFactory getWebServerFactory() {//从工厂中获取所有的bean,这里是按照类型获取bean,类型就是ServletWebServerFactory,也就是获得所有的ServletWebServerFactory接口实现类的beanString[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);//如果当前没有拿到一个web服务器工厂,就抛出异常if (beanNames.length == 0) {throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "+ "ServletWebServerFactory bean.");}//如果获得多个web服务器工厂也需要抛出异常if (beanNames.length > 1) {throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));}//然后就真正获取bean(getBeanFactory()获取bean工厂,调用getBean创建或获取bean,Spring源码分析中有讲) return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);}
上面代码的逻辑就是从spring容器中拿到实现ServletWebServerFactory
接口的bean,如果有多个或者一个都没有SpringBoot就会抛出相应的异常。那么新的问题来了,从上面代码我们可以看到,创建Web服务器的工厂都会被注入到Spring容器中,那么这些bean是如何被注入到Spring容器中的?
,而这个注入bean的过程就是SpringBoot的自动配置帮我们做的!
。在SpringBoot中这些事都是自动配置类完成的,而对于Web服务器的自动配置类就是ServletWebServerFactoryAutoConfiguration
,我们进入该类。
@Configuration(proxyBeanMethods = false)
@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 {@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,ObjectProvider<WebListenerRegistrar> webListenerRegistrars,ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {return new ServletWebServerFactoryCustomizer(serverProperties,webListenerRegistrars.orderedStream().collect(Collectors.toList()),cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));}@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}@Configuration(proxyBeanMethods = false)@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)static class ForwardedHeaderFilterConfiguration {@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")ForwardedHeaderFilterCustomizer tomcatForwardedHeaderFilterCustomizer(ServerProperties serverProperties) {return (filter) -> filter.setRelativeRedirects(serverProperties.getTomcat().isUseRelativeRedirects());}@BeanFilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter(ObjectProvider<ForwardedHeaderFilterCustomizer> customizerProvider) {ForwardedHeaderFilter filter = new ForwardedHeaderFilter();customizerProvider.ifAvailable((customizer) -> customizer.customize(filter));FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);registration.setOrder(Ordered.HIGHEST_PRECEDENCE);return registration;}}interface ForwardedHeaderFilterCustomizer {void customize(ForwardedHeaderFilter filter);}/*** Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via* {@link ImportBeanDefinitionRegistrar} for early registration.*/public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {private ConfigurableListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}}@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}// 往Spring容器中注册一个WebServerFactoryCustomizerBeanPostProcessorregisterSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class,WebServerFactoryCustomizerBeanPostProcessor::new);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);}private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,Class<T> beanClass, Supplier<T> instanceSupplier) {if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass, instanceSupplier);beanDefinition.setSynthetic(true);registry.registerBeanDefinition(name, beanDefinition);}}}}
首先是@Configuration(proxyBeanMethods = false)
注解,说明在Spring容器的启动过程中,这个类会作为一个配置类被处理。@ConditionalOnClass(ServletRequest.class)
只有满足这个条件这个配置类才会被处理。@ConditionalOnWebApplication(type = Type.SERVLET)
判断当前是一个web应用且是SERVLET
类型这个配置类才会生效。只有满足上面两个条件,这个配置类才会被处理,然后处理@Import
注解,这个注解在分析Spring源码的时候详细分析过,它可以帮我们额外导入一些配置类或者Bean。我们看看导入的四个类是什么:
ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class
第一个我们不重点分析,重点是后面是那个,从名字我们也可以看出来,是有关Web服务器的一些类,即内嵌的Tomcat、内嵌的Jetty和内嵌的Undertow。我们进入ServletWebServerFactoryConfiguration
。
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();// orderedStream()调用时会去Spring容器中找到TomcatConnectorCustomizer类型的Bean,默认是没有的,程序员可以自己定义factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}/*** Nested configuration if Jetty is being used.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedJetty {@BeanJettyServletWebServerFactory JettyServletWebServerFactory(ObjectProvider<JettyServerCustomizer> serverCustomizers) {JettyServletWebServerFactory factory = new JettyServletWebServerFactory();factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}/*** Nested configuration if Undertow is being used.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedUndertow {@BeanUndertowServletWebServerFactory undertowServletWebServerFactory(ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();factory.getDeploymentInfoCustomizers().addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));return factory;}@BeanUndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new UndertowServletWebServerFactoryCustomizer(serverProperties);}}}
首先一眼丁真,这是一个配置类,自然也会被Spring处理,然后这个类里面有有三个静态内部类,分别是EmbeddedTomcat
、EmbeddedJetty
和EmbeddedUndertow
。然后这三个静态内部类也同样是配置类,同样会被Spring处理,然后每个静态内部类都会注入相应web服务器需要的Bean,我们这里拿Tomcat的分析一下:
@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();// orderedStream()调用时会去Spring容器中找到TomcatConnectorCustomizer类型的Bean,默认是没有的,程序员可以自己定义factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}
首先看下面的注解:
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
上面的两个注解就是Spring中的两个注解,它在这里的作用就是判断下面的配置类会不会被解析,例如只有满足Servlet.class, Tomcat.class, UpgradeProtocol.class
这三个类都存在,且项目中没有ServletWebServerFactory
这个bean才会被解析。如果上面的条件的满足,就会处理静态内部类的@Bean
注解,来注入相应web服务器的工厂bean。上面这两个注解是很重要的,它的SpringBoot在判断选择使用哪个Web服务器的核心逻辑。然后我们看一下具体的方法:
@Bean//返回值是一个TomcatServletWebServerFactory对象TomcatServletWebServerFactory tomcatServletWebServerFactory(//Tomcat需要的一些依赖,加了ObjectProvider就表示可以为空,为空就会使用默认的Tomcat配置,有了这三个bean,我们就可以设置一些tomcat的常用的属性ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {//创建一个TomcatServletWebServerFactory对象TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();// orderedStream()调用时会去Spring容器中找到TomcatConnectorCustomizer类型的Bean,默认是没有的,程序员可以自己定义factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory;}//返回的this.tomcatConnectorCustomizers一个connectorCustomizers的集合
public Collection<TomcatConnectorCustomizer> getTomcatConnectorCustomizers() {return this.tomcatConnectorCustomizers;}
如果注入实例为空 , 则使用ObjectProvider可以避免强依赖导致的依赖不存在问题
上面代码核心就是创建了一个TomcatServletWebServerFactory
的实例,然后设置了一些属性,它的参数是三个对象,分别是TomcatConnectorCustomizer
、TomcatContextCustomizer
和TomcatProtocolHandlerCustomizer
这意味着我们同样可以自己注入这些bean,来完成Tomcat的一些配置,例如:
@Bean
public TomcatConnectorCustomizer tomcatConnectorCustomizer{return new TomcatConnectorCustomizer(){@Overridepublic void customize(Connector connetor){connector.setPort(8888);}}
}
上面代码我们就注入了一个TomcatConnectorCustomizer
的bean,修改了Tomcat的默认监听端口8080。那么现在思考新的问题:Tomcat是如何将上面案例我们设置的端口号设置到我们的Tomcat实例中的呢
?我们进入TomcatServletWebServerFactory的getServer方法,它里面有一句代码:
customizeConnector(connector);protected void customizeConnector(Connector connector) {//private int port = 8080; getport方法返回值就是8080,也就是Tomcat的默认监听端口int port = Math.max(getPort(), 0);//设置监听的端口connector.setPort(port);if (StringUtils.hasText(getServerHeader())) {connector.setProperty("server", getServerHeader());}if (connector.getProtocolHandler() instanceof AbstractProtocol) {customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());}invokeProtocolHandlerCustomizers(connector.getProtocolHandler());if (getUriEncoding() != null) {connector.setURIEncoding(getUriEncoding().name());}// Don't bind to the socket prematurely if ApplicationContext is slow to startconnector.setProperty("bindOnInit", "false");if (getHttp2() != null && getHttp2().isEnabled()) {connector.addUpgradeProtocol(new Http2Protocol());}if (getSsl() != null && getSsl().isEnabled()) {customizeSsl(connector);}TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());compression.customize(connector);for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {customizer.customize(connector);}}
上面代码核心的几十下面这一句:
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {customizer.customize(connector);}
前面我们知道,在创建TomcatServletWebServerFactory
的时候会将TomcatConnectorCustomizer
都注入到this.tomcatConnectorCustomizers
集合中,然后这里就拿到了所有的TomcatConnectorCustomizer
然后遍历处理,也就是在这里前面我们案例设置的端口号就生效了。
使用过SpringBoot都知道我们的端口号同样可以在
application.properties中配置,那么这种配置形式又是如何生效的呢?
我们接着分析,首先我们回到自动Web服务器的自动配置类:
@Configuration(proxyBeanMethods = false)
@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
注解中导入的EmbeddedTomcat
、EmbeddedJetty
和EmbeddedUndertow
这三个类。然而还没有分析BeanPostProcessorsRegistrar
这个类,我们这里分析一下。
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {private ConfigurableListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}}@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}// 往Spring容器中注册一个WebServerFactoryCustomizerBeanPostProcessorregisterSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class,WebServerFactoryCustomizerBeanPostProcessor::new);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);}private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,Class<T> beanClass, Supplier<T> instanceSupplier) {if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass, instanceSupplier);beanDefinition.setSynthetic(true);registry.registerBeanDefinition(name, beanDefinition);}}}
BeanPostProcessorsRegistrar
是ServletWebServerFactoryAutoConfiguration
内部定义的一个内部类,首先这个类有个鲜明的特征就是实现了ImportBeanDefinitionRegistrar
接口,那么在Spring启动过程中会自动执行实现了这个接口的类的registerBeanDefinitions
方法,然后就是一个回调接口BeanFactoryAware
,有了这个回调接口我们就可以拿到bean工厂。下面我们看一下registerBeanDefinitions
方法:
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}// 往Spring容器中注册一个WebServerFactoryCustomizerBeanPostProcessorregisterSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class,WebServerFactoryCustomizerBeanPostProcessor::new);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);}
上面代码就是向Spring容器中注入了一个核心的beanWebServerFactoryCustomizerBeanPostProcessor
我们进入该类:
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {private ListableBeanFactory beanFactory;private List<WebServerFactoryCustomizer<?>> customizers;@Overridepublic void setBeanFactory(BeanFactory beanFactory) {Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,"WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory");this.beanFactory = (ListableBeanFactory) beanFactory;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof WebServerFactory) {postProcessBeforeInitialization((WebServerFactory) bean);}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}@SuppressWarnings("unchecked")private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {// 获取WebServerFactoryCustomizer对WebServerFactory进行自定义,默认会拿到5个,其中就包括ServletWebServerFactoryCustomizerLambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class).invoke((customizer) -> customizer.customize(webServerFactory));}private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {if (this.customizers == null) {// Look up does not include the parent contextthis.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);this.customizers = Collections.unmodifiableList(this.customizers);}return this.customizers;}@SuppressWarnings({ "unchecked", "rawtypes" })private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();}}
首先这个类实现了BeanPostProcessor
,这个接口在介绍Spring的时候详细分析过(bean的后置处理器)。我们看看它的一个核心方法:
@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {//判断bean的类型是否是WebServerFactory类型if (bean instanceof WebServerFactory) {//如果是调用postProcessBeforeInitialization((WebServerFactory) bean);方法postProcessBeforeInitialization((WebServerFactory) bean);}return bean;}
这里重写了BeanPostProcessor
的postProcessBeforeInitialization
方法,即在bean初始化前调用的方法,上面判断 此时创建的bean是否是WebServerFactory
类型,即web服务器类型,如果是就调用postProcessBeforeInitialization
方法,我们进入该方法:
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {// 获取WebServerFactoryCustomizer对WebServerFactory进行自定义,默认会拿到5个,其中就包括ServletWebServerFactoryCustomizerLambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class).invoke((customizer) -> customizer.customize(webServerFactory));}
上面代码核心就是调用了 customizer.customize(webServerFactory)
,即WebServerFactoryCustomizer
的customize
方法,那么这个WebServerFactoryCustomizer
类型的bean是什么时候创建的?细心的可以发现,这个bean就是在我们Web服务器的自动配置类里面创建了。
@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,ObjectProvider<WebListenerRegistrar> webListenerRegistrars,ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {return new ServletWebServerFactoryCustomizer(serverProperties,webListenerRegistrars.orderedStream().collect(Collectors.toList()),cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));}
我们进入该类的customize
方法:
注意这里的customize和前面介绍的customize是不同的,前面是处理Connecter对象的,这里是处理WebFactory对象的,这一点要做区分
@Overridepublic void customize(ConfigurableServletWebServerFactory factory) {PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();map.from(this.serverProperties::getPort).to(factory::setPort);map.from(this.serverProperties::getAddress).to(factory::setAddress);map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);map.from(this.serverProperties::getSsl).to(factory::setSsl);map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);map.from(this.serverProperties::getCompression).to(factory::setCompression);map.from(this.serverProperties::getHttp2).to(factory::setHttp2);map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);for (WebListenerRegistrar registrar : this.webListenerRegistrars) {registrar.register(factory);}if (!CollectionUtils.isEmpty(this.cookieSameSiteSuppliers)) {factory.setCookieSameSiteSuppliers(this.cookieSameSiteSuppliers);}}
上面方法的参数就是我们正在创建的ConfigurableServletWebServerFactory
即web服务器工厂对象。然后下面就是很多map.from
来设置web服务器工厂的一些属性,我们拿下面这一句分析:
map.from(this.serverProperties::getPort).to(factory::setPort);
首先this.serverProperties.getPort()
就可以拿到我们设置的端口的属性,然后调用setPort
方法,来设置端口。我们看一下this.serverProperties
这个类:
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
......
}
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
就是取application.proporties
文件中server
开头的属性,所以我们可以拿到application.proporties
中我们配置的server.port
属性,然后自动配置类上有这个注解@EnableConfigurationProperties(ServerProperties.class)
,就会将ServerProperties.class
这个类注册为Spring的一个bean,这样就可以拿到application.properties
中的相关属性并设置到web服务器工厂中了。
上面就拿SpringBoot对web服务器的自动配置过程,分析了一下Spring的自动配置大致原理。在介绍前面的过程中,我们分析过SpringBoot提供的条件注解,条件注解对于SpringBoot来说是很重要的,它自己实现了很多条件注解,大致有下面这些:
我们就拿ConditionalOnClass
来分析SpringBoot是如何处理这些条件注解的:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {Class<?>[] value() default {};String[] name() default {};}@Overridepublic final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 针对每个条件注解进行条件判断// 条件注解写在了哪个类上,或哪个方法上String classOrMethodName = getClassOrMethodName(metadata);try {// 条件的判断结果(ConditionOutcome就是条件判断得结果)ConditionOutcome outcome = getMatchOutcome(context, metadata);// 如果log的日志级别为trace,那就直接记录当前条件的判断结果logOutcome(classOrMethodName, outcome);// 将判断结果记录到ConditionEvaluationReport中//ConditionEvaluationReportLoggingListener会在收到ContextRefreshedEvent事件后把判断结果用日志的方式打印出来recordEvaluation(context, classOrMethodName, outcome);return outcome.isMatch();}catch (NoClassDefFoundError ex) {throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "+ ex.getMessage() + " not found. Make sure your own configuration does not rely on "+ "that class. This can also happen if you are "+ "@ComponentScanning a springframework package (e.g. if you "+ "put a @ComponentScan in the default package by mistake)", ex);}catch (RuntimeException ex) {throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);}}
任何SpringBoot自己实现的条件注解内部都有@Conditional(OnClassCondition.class)
只是OnClassCondition.class
指定的内不同,@Conditional
注解会被Spring识别并调用OnClassCondition.class
的matches
方法,而matches
是Conditon接口提供的,意味着OnClassCondition.class
底层必定实现了Conditon
接口。这也是Springboot条件注解的底层实现原理(就是Spring条件注解底层的实现原理),解析条件注解底层太过细节了这里就不详细分析了。继续回到SpringBoot的启动类。
@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}}
run方法的参数就是MyApplication
,这个类上面有一个@SpringBootApplication
注解,我们进入该注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
// AutoConfigurationExcludeFilter的作用是扫描到的配置类名字如果在自动配置类名集合中,就不解析
public @interface SpringBootApplication {
}
首先有一个@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
这个在Spring里面详细介绍过。然后我们看@EnableAutoConfiguration
注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
这个注解中又包含一个@Import
注解,导入了一个AutoConfigurationImportSelector
类,而这个类实现了DeferredImportSelector
接口,在Spring解析配置类的时候会自动调用这个类的SelectImports方法:
@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// 会在所有@Configuration都解析完了之后才执行if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}// 获取自动配置类(spring.factories中所导入的)AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
上面方法就会将获取所有的配置类spring.factories
中定义的,底层用的SPI机制。 我们进入getAutoConfigurationEntry
方法。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 获取@EnableAutoConfiguration的属性AnnotationAttributes attributes = getAttributes(annotationMetadata);// 获取spring.factories中所有的AutoConfigurationList<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 去重(也就是按类名去重)configurations = removeDuplicates(configurations);// 获取需要排除的AutoConfiguration,可以通过@EnableAutoConfiguration注解的exclude属性,或者spring.autoconfigure.exclude来配置Set<String> exclusions = getExclusions(annotationMetadata, attributes);// 排除checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);// 获取spring.factories中的AutoConfigurationImportFilter对AutoConfiguration进行过滤// 默认会拿到OnBeanCondition、OnClassCondition、OnWebApplicationCondition// 这三个会去判断上面的AutoConfiguration是否符合它们自身所要求的条件,不符合的会过滤掉,表示不会进行解析了// 会利用spring-autoconfigure-metadata.properties中的配置来进行过滤// spring-autoconfigure-metadata.properties文件中的内容是利用Java中的AbstractProcessor技术在编译时生成出来的configurations = getConfigurationClassFilter().filter(configurations);// configurations表示合格的,exclusions表示被排除的,把它们记录在ConditionEvaluationReportAutoConfigurationImportListener中fireAutoConfigurationImportEvents(configurations, exclusions);// 最后返回的AutoConfiguration都是符合条件的return new AutoConfigurationEntry(configurations, exclusions);}
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
就获取了spring.factories中所有的AutoConfiguration
所有的AutoConfiguration
。我们进入该方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;}
首先调用了loadFactoryNames
方法,这个是SpringFactoriesLoader
类中的方法,我们看一下这个类:
public final class SpringFactoriesLoader {//这里就定义了spring.factories文件的路径public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());}
}
然后loadFactoryNames
又调用了loadSpringFactories
方法,然后调用getOrDefault
方法,我们首先进入loadSpringFactories
方法
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {//cache就是缓存的spring.factories中的内容Map<String, List<String>> result = cache.get(classLoader);//如果缓存不为空,直接返回if (result != null) {return result;}result = new HashMap<>();try {//解析SPring.factoriesEnumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();String[] factoryImplementationNames =StringUtils.commaDelimitedListToStringArray((String) entry.getValue());for (String factoryImplementationName : factoryImplementationNames) {result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());}}}// Replace all lists with unmodifiable lists containing unique elementsresult.replaceAll((factoryType, implementations) -> implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));cache.put(classLoader, result);}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}return result;}
上面代码就是在解析spring.factories
文件,并将其放入了一个Map<String, List<String>>
集合中,并返回。然后调用getOrDefault方法,这个是map集合的方法。参数为factoryTypeName,即获得所有的自动配置类。上面的逻辑SpringBoot就获得了所有的自动配置类。回到selectImports
,就返回了所有的自动配置类的集合AutoConfigurationEntry
,而这些自动配置类就会被注册为Spring的bean。