情景1 引入第三方starter
背景
为什么会有上面这个疑问呢?其实还是自己几年前看springboot的源码,那时候只是单纯的跟踪代码去看,完全没有太过去了解本质导致的。比如看到自动配置发现mybatis的自动配置类
public class MybatisAutoConfiguration implements InitializingBean {...@Bean@ConditionalOnMissingBeanpublic SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {ExecutorType executorType = this.properties.getExecutorType();if (executorType != null) {return new SqlSessionTemplate(sqlSessionFactory, executorType);} else {return new SqlSessionTemplate(sqlSessionFactory);}}...}
那时候我就在想,假如我没的项目没有引入mybatis的相关jar包,岂不是项目编译都不通过?毕竟没有毕竟这个SqlSessionTemplate是在org.mybatis.spring下面!
然后困扰了我很久,知道我再一次去了解SPI机制!
原理
原来,MybatisAutoConfiguration这个类,根本就不是在springboot
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
而是在你要使用的mybatis功能的starter里面的。
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency>
springboot的SPI机制,会去扫描每个自动装配组件下面的spring.factories

也就是你只有引入了mybatis-spring-boot-starter这个依赖,才会有这个org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration自动配置类,才会去初始化SqlSessionTemplate这个bean。如果你不引入依赖,那么也就不会有这段代码,这就是SPI的好处。加入依赖就可以用,不加入就不能用!
情景2 springboot里面的类
背景
还有一种情况,我们看springboot代码的时候,当我们引入
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
会看到这个web项目里面有代码
package org.springframework.boot.autoconfigure.web.servlet;import org.eclipse.jetty.server.Server;import org.eclipse.jetty.util.Loader;import org.eclipse.jetty.webapp.WebAppContext;import org.apache.catalina.startup.Tomcat ;...@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 {}...
可以看到代码里面直接就有Tomcat.class,这些都是tomcat里面的包,但是问题来了,我们的项目中,假设用另一个容器,不用
原理
1、编译会不会报错
我们的项目引入了spring-boot-starter-web,但是排除了tomcat,编译会不会报错,首先我们引入的jar包,引入的是字节码,所以编译肯定不会报错,毕竟别人已经编译好了的,怎么可能会报错呢?
2、启动会不会报错
我们项目启动,肯定回去运行下面这段逻辑的
@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {}
此时spring用的是ASM字节码操作技术,是直接解析字节码获取Servlet.class, Tomcat.class这些对应的字节码类全民,后面再用加载器进行反射加载,所以也不会报错!
牛逼!
