情景1 引入第三方starter
背景
为什么会有上面这个疑问呢?其实还是自己几年前看springboot的源码,那时候只是单纯的跟踪代码去看,完全没有太过去了解本质导致的。比如看到自动配置发现mybatis的自动配置类
public class MybatisAutoConfiguration implements InitializingBean {
...
@Bean
@ConditionalOnMissingBean
public 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这些对应的字节码类全民,后面再用加载器进行反射加载,所以也不会报错!
牛逼!