赞
踩
SpringBoot2.6.6源码地址:SpringBoot-2.6.6: SpringBoot2.6.6
相当于@Component注解
如果解析当前bean的时候spring容器里没有User这个bean就符合条件,也就是如果程序员定义了User这个bean,那这个User就不生效了
可以通过@Import注解来导入配置类和spring.factories一个作用
讲道理按上面的说法,生效的应该是MyApplication这个bean,但实际上会报错。
原因:先处理的上面的@Import注解,此时容器里还没User,所以Bean注解在处理User的时候,已经有名字User的bean了,所以报错
假如我现在导入了实现ImportSelector的一个类,然后实现先扣的selectImports方法,然后返回一个数组,【可以返回很多的自动配置类】
还是会先解析这个自动配置类【ZhouyuAutoConfiguration】再去解析@Bean
成功把
DeferredImportSelector和ImportSelector的区别在于:
实现了Group接口
加了getImportGroup方法,selectImports方法就不执行了
如果上面的getImportGroup方法返回不为null,就会执行下面的process方法,然后在执行selectImports方法
里面就包含了
导入了AutoConfigurationImportSelector这个类,找自动配置类,过滤自动配置类,返回给spring容器
AutoConfigurationImportSelector
是 Spring Boot 的内部类,它是自动配置的核心组件之一。这个类的主要职责是在 Spring Boot 启动过程中,从 META-INF/spring.factories
文件中读取并加载自动配置类(Auto-configuration Classes)。
你提到的“找自动配置类,过滤自动配置类,返回给 Spring 容器”基本上是正确的,不过可能需要稍微详细说明一些。这个类的工作流程如下:
查找: AutoConfigurationImportSelector
使用 Spring Boot 的 SpringFactoriesLoader
机制来加载 spring.factories
文件中列出的所有自动配置类。这些类通常以 org.springframework.boot.autoconfigure.EnableAutoConfiguration
作为键。
过滤: 加载完所有自动配置类后,AutoConfigurationImportSelector
会对这些类进行过滤。这一步涉及到多种过滤机制:
@Conditional
注解修饰,例如 @ConditionalOnClass
、@ConditionalOnBean
等,这些注解决定了配置类是否应该被包含在上下文中。@SpringBootApplication
或 @EnableAutoConfiguration
注解中使用 exclude
属性来手动排除某些自动配置。application.properties
或 application.yml
中通过设置 spring.autoconfigure.exclude
属性来排除自动配置类。返回: 经过过滤后,剩下的自动配置类会由 AutoConfigurationImportSelector
返回,并最终由 Spring 容器进行加载和配置。
至于会过滤哪些类,这取决于上述提到的各种条件。比如,如果自动配置类要求某个类必须在类路径上存在,而实际上该类不存在,则这个自动配置类将被过滤掉不会被加载。类似地,如果某个自动配置类要求某个 Bean 必须不存在,而该 Bean 实际存在,则这个自动配置类也会被过滤掉。
AutoConfigurationImportSelector
通过这些过程,帮助 Spring Boot 构建一个“按需”配置的 ApplicationContext,从而简化了 Spring 应用的配置。
实现了DeferredImportSelector接口
也返回了AutoConfigurationGroup类【静态内部类】==》解析Spring.factories文件,拿到自动配置类的名字,去过滤,返回生效的自动配置类
也用了@Import注解,导入了Registrar
解析完这个注解会调用registerBeanDefinitions这个方法,拿到扫描路径,com.zkc,把包路径变成个bean注册到spring容器中【给Mybatis用的】
这里是Mybatis源码,也就是说设置Mybatis要扫描的路径
那为什么此时去掉@Mapper注解就扫描不到这个mapper了?【上面一定要加@Mapper才会扫描到。。Mybatis加的限定条件】
如果用的MapperScan注解,它扫描时候就不会限定上面要有@Mapper注解了,且重复扫描了。所以这个其实没必要用了,这个注解
排除过滤器TypeExcludeFilter和AutoConfigurationExcludeFilter这两个类
去springFactories找到所有的自动配置类,和加了@Configuration的配置类,都过滤掉了,因为前面自动配置类已经处理了,这边无需重复处理。
相当于扩展点,到spring容器中找到TypeExcludeFilter类型的bean,自定义排除逻辑。
可以自定义过滤逻辑,排除掉bean,想要生效,必须在扫描之前就注册进spring容器里
解决方案:
定义容器初始化器,实现initialize方法
注册排除过滤器到spring容器中
同时还要加到spring.factories中才能生效
SpringBoot中的常用条件注解有:
当然我们也可以利用@Conditional来自定义条件注解。
条件注解是可以写在类上和方法上的,如果某个条件注解写在了自动配置类上,那该自动配置类会不会生效就要看当前条件能不能符合,或者条件注解写在某个@Bean修饰的方法上,那这个Bean生不生效就看当前条件符不符合。
具体原理是:
我们可以发现,SpringBoot的自动配置,实际上就是SpringBoot的源码中预先写好了一些配置类,预先定义好了一些Bean,我们在用SpringBoot时,这些配置类就已经在我们项目的依赖中了,而这些自动配置类或自动配置Bean到底生不生效,就看具体所指定的条件了。
SpringBoot中众多的条件注解,都是基于Spring中的@Conditional来实现的,所以我们先来用一下@Conditional注解。
先来看下@Conditional注解的定义:
- @Target({ElementType.TYPE, ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface Conditional {
-
- /**
- * All {@link Condition} classes that must {@linkplain Condition#matches match}
- * in order for the component to be registered.
- */
- Class<? extends Condition>[] value();
-
- }
根据定义我们在用@Conditional注解时,需要指定一个或多个Condition的实现类,所以我们先来提供一个实现类:
- public class ZhouyuCondition implements Condition {
-
- @Override
- public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
- return false;
- }
-
- }
很明显,我们可以在matches方法中来定义条件逻辑:
先来看一个案例:
- @Configuration
- @ConditionalOnClass(name = "com.zhouyu.Jetty")
- @ConditionalOnMissingClass(value = "com.zhouyu.Tomcat")
- public class ZhouyuConfiguration {
-
-
- }
我们在ZhouyuConfiguration这个类上使用了两个条件注解:
这两个注解对应的都是@Conditional(OnClassCondition.class),那在OnClassCondition类中是如何对这两个注解进行区分的呢?
Spring在解析到ZhouyuConfiguration这个配置时,发现该类上用到了条件注解就会进行条件解析,相关源码如下:
-
- // 这是Spring中的源码,不是SpringBoot中的
- for (Condition condition : conditions) {
- ConfigurationPhase requiredPhase = null;
- if (condition instanceof ConfigurationCondition) {
- requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
- }
-
- // 重点在这
- if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
- return true;
- }
- }
conditions中保存了两个OnClassCondition对象,这段代码会依次调用OnClassCondition对象的matches方法进行条件匹配,一旦某一个条件不匹配就不会进行下一个条件的判断了,这里return的是true,但是这段代码所在的方法叫做shouldSkip,所以true表示忽略。
我们继续看OnClassCondition的matches()方法的实现。
OnClassCondition类继承了FilteringSpringBootCondition,FilteringSpringBootCondition类又继承了SpringBootCondition,而SpringBootCondition实现了Condition接口,matches()方法也是在SpringBootCondition这个类中实现的:
- @Override
- public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
- // 获取当前解析的类名或方法名
- String classOrMethodName = getClassOrMethodName(metadata);
- try {
- // 进行具体的条件匹配,ConditionOutcome表示匹配结果
- ConditionOutcome outcome = getMatchOutcome(context, metadata);
-
- // 日志记录匹配结果
- logOutcome(classOrMethodName, outcome);
- recordEvaluation(context, classOrMethodName, outcome);
-
- // 返回true或false
- return outcome.isMatch();
- }
- catch (NoClassDefFoundError ex) {
- // ...
- }
- catch (RuntimeException ex) {
- // ...
- }
- }
所以具体的条件匹配逻辑在getMatchOutcome方法中,而SpringBootCondition类中的getMatchOutcome方法是一个抽象方法,具体的实现逻辑就在子类OnClassCondition中:
- @Override
- public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
- ClassLoader classLoader = context.getClassLoader();
- ConditionMessage matchMessage = ConditionMessage.empty();
-
- // 拿到ConditionalOnClass注解中的value值,也就是要判断是否存在的类名
- List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
- if (onClasses != null) {
- // 判断onClasses中不存在的类
- List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);
- // 如果有缺失的类,那就表示不匹配
- if (!missing.isEmpty()) {
- return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
- .didNotFind("required class", "required classes").items(Style.QUOTE, missing));
- }
- // 否则就表示匹配
- matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
- .found("required class", "required classes")
- .items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));
- }
-
- // 和上面类似,只不过是判断onMissingClasses是不是全部缺失,如果是则表示匹配
- List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
- if (onMissingClasses != null) {
- List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);
- if (!present.isEmpty()) {
- return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class)
- .found("unwanted class", "unwanted classes").items(Style.QUOTE, present));
- }
- matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
- .didNotFind("unwanted class", "unwanted classes")
- .items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));
- }
- return ConditionOutcome.match(matchMessage);
- }
在getMatchOutcome方法中的逻辑为:
因为ConditionalOnClass注解和ConditionalOnMissingClass注解的逻辑是比较类似的,所以在源码中都是在OnClassCondition这个类中实现的,假如一个类上即有@ConditionalOnClass,也有@ConditionalOnMissingClass,比如以下代码:
- @Configuration
- @ConditionalOnClass(Tomcat.class)
- @ConditionalOnMissingClass(value = "com.zhouyu.Tomcat")
- public class ZhouyuConfiguration {
-
-
- }
上面提到的ClassNameFilter.MISSING和ClassNameFilter.PRESENT也比较简单,代码如下:
- protected enum ClassNameFilter {
-
- PRESENT {
-
- @Override
- public boolean matches(String className, ClassLoader classLoader) {
- return isPresent(className, classLoader);
- }
-
- },
-
- MISSING {
-
- @Override
- public boolean matches(String className, ClassLoader classLoader) {
- return !isPresent(className, classLoader);
- }
-
- };
-
- abstract boolean matches(String className, ClassLoader classLoader);
-
- static boolean isPresent(String className, ClassLoader classLoader) {
- if (classLoader == null) {
- classLoader = ClassUtils.getDefaultClassLoader();
- }
- try {
- resolve(className, classLoader);
- return true;
- }
- catch (Throwable ex) {
- return false;
- }
- }
-
- }
- protected static Class<?> resolve(String className, ClassLoader classLoader) throws ClassNotFoundException {
- if (classLoader != null) {
- return Class.forName(className, false, classLoader);
- }
- return Class.forName(className);
- }
主要就是用类加载器,来判断类是否存在。
@ConditionalOnBean和@ConditionalOnClass的底层实现应该是差不多的,一个是判断Bean存不存在,一个是判断类存不存在,事实上也确实差不多。
首先@ConditionalOnBean和@ConditionalOnMissingBean对应的都是OnBeanCondition类,OnBeanCondition类也是继承了SpringBootCondition,所以SpringBootCondition类中的getMatchOutcome方法才是匹配逻辑:
- @Override
- public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
- ConditionMessage matchMessage = ConditionMessage.empty();
- MergedAnnotations annotations = metadata.getAnnotations();
-
- // 如果存在ConditionalOnBean注解
- if (annotations.isPresent(ConditionalOnBean.class)) {
- Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations, ConditionalOnBean.class);
- MatchResult matchResult = getMatchingBeans(context, spec);
-
- // 如果某个Bean不存在
- if (!matchResult.isAllMatched()) {
- String reason = createOnBeanNoMatchReason(matchResult);
- return ConditionOutcome.noMatch(spec.message().because(reason));
- }
-
- // 所有Bean都存在
- matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
- matchResult.getNamesOfAllMatches());
- }
-
- // 如果存在ConditionalOnSingleCandidate注解
- if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
- Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations);
- MatchResult matchResult = getMatchingBeans(context, spec);
-
- // Bean不存在
- if (!matchResult.isAllMatched()) {
- return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
- }
-
- // Bean存在
- Set<String> allBeans = matchResult.getNamesOfAllMatches();
-
- // 如果只有一个
- if (allBeans.size() == 1) {
- matchMessage = spec.message(matchMessage).found("a single bean").items(Style.QUOTE, allBeans);
- }
- else {
- // 如果有多个
- List<String> primaryBeans = getPrimaryBeans(context.getBeanFactory(), allBeans,
- spec.getStrategy() == SearchStrategy.ALL);
-
- // 没有主Bean,那就不匹配
- if (primaryBeans.isEmpty()) {
- return ConditionOutcome.noMatch(
- spec.message().didNotFind("a primary bean from beans").items(Style.QUOTE, allBeans));
- }
- // 有多个主Bean,那就不匹配
- if (primaryBeans.size() > 1) {
- return ConditionOutcome
- .noMatch(spec.message().found("multiple primary beans").items(Style.QUOTE, primaryBeans));
- }
-
- // 只有一个主Bean
- matchMessage = spec.message(matchMessage)
- .found("a single primary bean '" + primaryBeans.get(0) + "' from beans")
- .items(Style.QUOTE, allBeans);
- }
- }
-
- // 存在ConditionalOnMissingBean注解
- if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
- Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
- ConditionalOnMissingBean.class);
- MatchResult matchResult = getMatchingBeans(context, spec);
-
- //有任意一个Bean存在,那就条件不匹配
- if (matchResult.isAnyMatched()) {
- String reason = createOnMissingBeanNoMatchReason(matchResult);
- return ConditionOutcome.noMatch(spec.message().because(reason));
- }
-
- // 都不存在在,则匹配
- matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
- }
- return ConditionOutcome.match(matchMessage);
- }
逻辑流程为:
getMatchingBeans方法中会利用BeanFactory去获取指定类型的Bean,如果没有指定类型的Bean,则会将该类型记录在MatchResult对象的unmatchedTypes集合中,如果有该类型的Bean,则会把该Bean的beanName记录在MatchResult对象的matchedNames集合中,所以MatchResult对象中记录了,哪些类没有对应的Bean,哪些类有对应的Bean。
@ConditionalOnClass和@ConditionalOnBean,这两个条件注解的工作原理就分析到这,总结以下流程就是:
源码会有点难,还希望大家耐点性子,多看多调试源码。
那SpringBoot中的Starter和自动配置又有什么关系呢?
其实首先要明白一个Starter,就是一个Maven依赖,当我们在项目的pom.xml文件中添加某个Starter依赖时,其实就是简单的添加了很多其他的依赖,比如:
如果硬要把Starter机制和自动配置联系起来,那就是通过@ConditionalOnClass这个条件注解,因为这个条件注解的作用就是用来判断当前应用的依赖中是否存在某个类或某些类,比如:
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
- @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
- static class EmbeddedTomcat {
-
- @Bean
- TomcatServletWebServerFactory 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这三个类,如果存在就满足当前条件,如果项目中引入了spring-boot-starter-tomcat,那就有这三个类,如果没有spring-boot-starter-tomcat那就可能没有这三个类(除非你自己单独引入了Tomcat相关的依赖)。
所以这就做到了,如果我们在项目中要用Tomcat,那就依赖spring-boot-starter-web就够了,因为它默认依赖了spring-boot-starter-tomcat,从而依赖了Tomcat,从而Tomcat相关的Bean能生效。
而如果不想用Tomcat,那就得这么写:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-tomcat</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-jetty</artifactId>
- </dependency>
得把spring-boot-starter-tomcat给排除掉,再添加上spring-boot-starter-jetty的依赖,这样Tomcat的Bean就不会生效,Jetty的Bean就能生效,从而项目中用的就是Jetty。
通过前面我们会SpringBoot的自动配置机制、Starter机制、启动过程的底层分析,我们拿一个实际的业务案例来串讲一下,那就是SpringBoot和Tomcat的整合。
我们知道,只要我们的项目添加的starter为:spring-boot-starter-web,那么我们启动项目时,SpringBoot就会自动启动一个Tomcat。
那么这是怎么做到的呢?
首先我们可以发现,在spring-boot-starter-web这个starter中,其实简介的引入了spring-boot-starter-tomcat这个starter,这个spring-boot-starter-tomcat又引入了tomcat-embed-core依赖,所以只要我们项目中依赖了spring-boot-starter-web就相当于依赖了Tomcat。
然后在SpringBoot众多的自动配置类中,有一个自动配置类叫做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 {
- // ...
- }
首先看这个自动配置类所需要的条件:
在上面提到的spring-boot-starter-web中,其实还间接的引入了spring-web、spring-webmvc等依赖,这就使得第二个条件满足,而对于第一个条件的ServletRequest类,虽然它是Servlet规范中的类,但是在我们所依赖的tomcat-embed-core这个jar包中是存在这个类的,这是因为Tomcat在自己的源码中把Servlet规范中的一些代码也包含进去了,比如:
这就使得ServletWebServerFactoryAutoConfiguration这个自动配置的两个条件都符合,那么Spring就能去解析它,一解析它就发现这个自动配置类Import进来了三个类:
很明显,Import进来的这三个类应该是差不多,我们看EmbeddedTomcat这个类:
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
- @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
- static class EmbeddedTomcat {
-
- @Bean
- TomcatServletWebServerFactory 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;
- }
-
- }
可以发现这个类是一个配置类,所以Spring也会来解析它,不过它也有两个条件:
所以,通常只要我们项目依赖中有Tomcat依赖,那就符合条件,那最终Spring容器中就会有TomcatServletWebServerFactory这个Bean。
对于另外的EmbeddedJetty和EmbeddedUndertow,也差不多,都是判断项目依赖中是否有Jetty和Undertow的依赖,如果有,那么对应在Spring容器中就会存在JettyServletWebServerFactory类型的Bean、或者存在UndertowServletWebServerFactory类型的Bean。
总结一下:
那么SpringBoot给我们配置的这几个Bean到底有什么用呢?
我们前面说到,TomcatServletWebServerFactory实现了ServletWebServerFactory这个接口,这个接口的定义为:
- public interface ServletWebServerFactory {
- WebServer getWebServer(ServletContextInitializer... initializers);
- }
- public interface WebServer {
- void start() throws WebServerException;
- void stop() throws WebServerException;
- int getPort();
- }
我们发现ServletWebServerFactory其实就是用来获得WebServer对象的,而WebServer拥有启动、停止、获取端口等方法,那么很自然,我们就发现WebServer其实指的就是Tomcat、Jetty、Undertow,而TomcatServletWebServerFactory就是用来生成Tomcat所对应的WebServer对象,具体一点就是TomcatWebServer对象,并且在生成TomcatWebServer对象时会把Tomcat给启动起来,在源码中,调用TomcatServletWebServerFactory对象的getWebServer()方法时就会启动Tomcat。
我们再来看TomcatServletWebServerFactory这个Bean的定义:
- @Bean
- TomcatServletWebServerFactory 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;
- }
要构造这个Bean,Spring会从Spring容器中获取到TomcatConnectorCustomizer、TomcatContextCustomizer、TomcatProtocolHandlerCustomizer这三个类型的Bean,然后把它们添加到TomcatServletWebServerFactory对象中去,很明显这三种Bean是用来配置Tomcat的,比如:
也就是我们可以通过定义TomcatConnectorCustomizer类型的Bean,来对Tomcat进行配置,比如:
- @SpringBootApplication
- public class MyApplication {
-
- @Bean
- public TomcatConnectorCustomizer tomcatConnectorCustomizer(){
- return new TomcatConnectorCustomizer() {
- @Override
- public void customize(Connector connector) {
- connector.setPort(8888);
- }
- };
- }
-
- public static void main(String[] args) {
- SpringApplication.run(MyApplication.class);
- }
-
- }
这样Tomcat就会绑定8888这个端口。
有了TomcatServletWebServerFactory这个Bean之后,在SpringBoot的启动过程中,会执行ServletWebServerApplicationContext的onRefresh()方法,而这个方法会调用createWebServer()方法,而这个方法中最为重要的两行代码为:
- ServletWebServerFactory factory = getWebServerFactory();
- this.webServer = factory.getWebServer(getSelfInitializer());
很明显,getWebServerFactory()负责获取具体的ServletWebServerFactory对象,要么是TomcatServletWebServerFactory对象,要么是JettyServletWebServerFactory对象,要么是UndertowServletWebServerFactory对象,注意只能获取到一个,然后调用该对象的getWebServer方法,启动对应的Tomcat、或者Jetty、或者Undertow。
getWebServerFactory方法中的逻辑比较简单,获取Spring容器中的ServletWebServerFactory类型的Bean对象,如果没有获取到则抛异常,如果找到多个也抛异常,也就是在Spring容器中只能有一个ServletWebServerFactory类型的Bean对象。
拿到TomcatServletWebServerFactory对象后,就调用它的getWebServer方法,而在这个方法中就会生成一个Tomcat对象,并且利用前面的TomcatConnectorCustomizer等等会Tomcat对象进行配置,最后启动Tomcat。
这样在启动应用时就完成了Tomcat的启动,到此我们通过这个案例也看到了具体的Starter机制、自动配置的具体使用。
自动配置类ServletWebServerFactoryAutoConfiguration中,还会定义一个ServletWebServerFactoryCustomizer类型的Bean,定义为:
- @Bean
- public 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会接收一个ServerProperties的Bean,ServerProperties的Bean对应的就是properties文件中前缀为server的配置,我们可以利用ServerProperties对象的getPort方法获取到我们所配置的server.port的值。
而ServletWebServerFactoryCustomizer是针对一个ServletWebServerFactory的自定义器,也就是用来配置TomcatServletWebServerFactory这个Bean的,到时候ServletWebServerFactoryCustomizer就会利用ServerProperties对象来对TomcatServletWebServerFactory对象进行设置。
在ServletWebServerFactoryAutoConfiguration这个自动配置上,除开Import了EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow这三个配置类,还Import了一个ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,这个BeanPostProcessorsRegistrar会向Spring容器中注册一个WebServerFactoryCustomizerBeanPostProcessor类型的Bean。
WebServerFactoryCustomizerBeanPostProcessor是一个BeanPosrtProcessor,它专门用来处理类型为WebServerFactory的Bean对象,而我们的TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory也都实现了这个接口,所以不管当前项目依赖的情况,只要在Spring在创建比如TomcatServletWebServerFactory这个Bean时,WebServerFactoryCustomizerBeanPostProcessor就会对它进行处理,处理的逻辑为:
比如:
这样当TomcatServletWebServerFactory这个Bean对象创建完成后,它里面的很多属性,比如port,就已经是程序员所配置的值了,后续执行getWebServer方法时,就直接获取自己的属性,比如port属性,设置给Tomcat,然后再利用TomcatConnectorCustomizer等进行处理,最后启动Tomcat。
到此,SpringBoot整合Tomcat的核心原理就分析完了,主要涉及的东西有:
- @Configuration(proxyBeanMethods = false)
-
- // spring.aop.auto=true时开启AOP,或者没有配置spring.aop.auto时默认也是开启
- @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
- public class AopAutoConfiguration {
-
- @Configuration(proxyBeanMethods = false)
- @ConditionalOnClass(Advice.class)
- static class AspectJAutoProxyingConfiguration {
-
- @Configuration(proxyBeanMethods = false)
- // 开启AOP的注解,使用JDK动态代理
- @EnableAspectJAutoProxy(proxyTargetClass = false)
- // spring.aop.proxy-target-class=false时才生效
- @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
- static class JdkDynamicAutoProxyConfiguration {
-
- }
-
-
- @Configuration(proxyBeanMethods = false)
- // 开启AOP的注解,使用CGLIB动态代理
- @EnableAspectJAutoProxy(proxyTargetClass = true)
- // spring.aop.proxy-target-class=true时生效,或者没有配置spring.aop.proxy-target-class时默认也生效
- @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
- matchIfMissing = true)
- static class CglibAutoProxyConfiguration {
-
- }
-
- }
-
- @Configuration(proxyBeanMethods = false)
- // 没有aspectj的依赖,但是又要使用cglib动态代理
- @ConditionalOnMissingClass("org.aspectj.weaver.Advice")
- @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
- matchIfMissing = true)
- static class ClassProxyingConfiguration {
-
- @Bean
- static BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying() {
- return (beanFactory) -> {
- if (beanFactory instanceof BeanDefinitionRegistry) {
- BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
- // 注册InfrastructureAdvisorAutoProxyCreator从而开启Spring AOP
- // @EnableAspectJAutoProxy会注册AnnotationAwareAspectJAutoProxyCreator,也会开启Spring AOP但是同时有用解析AspectJ注解的功能
- AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
- AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
- }
- };
- }
-
- }
-
- }
Mybatis的自动配置类为MybatisAutoConfiguration,该类中配置了一个SqlSessionFactory和AutoConfiguredMapperScannerRegistrar。
SqlSessionFactory这个Bean是Mybatis需要配置的,AutoConfiguredMapperScannerRegistrar会注册并配置一个MapperScannerConfigurer。
- public static class AutoConfiguredMapperScannerRegistrar
- implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {
-
- private BeanFactory beanFactory;
- private Environment environment;
-
- @Override
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
-
- if (!AutoConfigurationPackages.has(this.beanFactory)) {
- logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
- return;
- }
-
- logger.debug("Searching for mappers annotated with @Mapper");
-
- // 获取AutoConfigurationPackages Bean从而获取SpringBoot的扫描路径
- List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
- if (logger.isDebugEnabled()) {
- packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
- }
-
- BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
- builder.addPropertyValue("processPropertyPlaceHolders", true);
-
- // 限制了接口上得加Mapper注解
- builder.addPropertyValue("annotationClass", Mapper.class);
- builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
- BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
- Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
- .collect(Collectors.toSet());
- if (propertyNames.contains("lazyInitialization")) {
- // Need to mybatis-spring 2.0.2+
- builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
- }
- if (propertyNames.contains("defaultScope")) {
- // Need to mybatis-spring 2.0.6+
- builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
- }
-
- // for spring-native
- boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class,
- Boolean.TRUE);
- if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {
- ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
- Optional<String> sqlSessionTemplateBeanName = Optional
- .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));
- Optional<String> sqlSessionFactoryBeanName = Optional
- .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));
- if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {
- builder.addPropertyValue("sqlSessionTemplateBeanName",
- sqlSessionTemplateBeanName.orElse("sqlSessionTemplate"));
- } else {
- builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get());
- }
- }
- builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
-
- registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
- }
-
- @Override
- public void setBeanFactory(BeanFactory beanFactory) {
- this.beanFactory = beanFactory;
- }
-
- @Override
- public void setEnvironment(Environment environment) {
- this.environment = environment;
- }
-
- private String getBeanNameForType(Class<?> type, ListableBeanFactory factory) {
- String[] beanNames = factory.getBeanNamesForType(type);
- return beanNames.length > 0 ? beanNames[0] : null;
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。