个人随笔
目录
Springboot自动配置为啥编译和启动也不报错的基本问题解答!
2023-10-13 14:57:27

情景1 引入第三方starter

背景

为什么会有上面这个疑问呢?其实还是自己几年前看springboot的源码,那时候只是单纯的跟踪代码去看,完全没有太过去了解本质导致的。比如看到自动配置发现mybatis的自动配置类

  1. public class MybatisAutoConfiguration implements InitializingBean {
  2. ...
  3. @Bean
  4. @ConditionalOnMissingBean
  5. public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
  6. ExecutorType executorType = this.properties.getExecutorType();
  7. if (executorType != null) {
  8. return new SqlSessionTemplate(sqlSessionFactory, executorType);
  9. } else {
  10. return new SqlSessionTemplate(sqlSessionFactory);
  11. }
  12. }
  13. ...
  14. }

那时候我就在想,假如我没的项目没有引入mybatis的相关jar包,岂不是项目编译都不通过?毕竟没有毕竟这个SqlSessionTemplate是在org.mybatis.spring下面!

然后困扰了我很久,知道我再一次去了解SPI机制!

原理

原来,MybatisAutoConfiguration这个类,根本就不是在springboot

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>

而是在你要使用的mybatis功能的starter里面的。

  1. <dependency>
  2. <groupId>org.mybatis.spring.boot</groupId>
  3. <artifactId>mybatis-spring-boot-starter</artifactId>
  4. <version>2.2.2</version>
  5. </dependency>

springboot的SPI机制,会去扫描每个自动装配组件下面的spring.factories

也就是你只有引入了mybatis-spring-boot-starter这个依赖,才会有这个org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration自动配置类,才会去初始化SqlSessionTemplate这个bean。如果你不引入依赖,那么也就不会有这段代码,这就是SPI的好处。加入依赖就可以用,不加入就不能用!

情景2 springboot里面的类

背景

还有一种情况,我们看springboot代码的时候,当我们引入

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>

会看到这个web项目里面有代码

  1. package org.springframework.boot.autoconfigure.web.servlet;
  2. import org.eclipse.jetty.server.Server;
  3. import org.eclipse.jetty.util.Loader;
  4. import org.eclipse.jetty.webapp.WebAppContext;
  5. import org.apache.catalina.startup.Tomcat;
  6. ...
  7. @Configuration(proxyBeanMethods = false)
  8. class ServletWebServerFactoryConfiguration {
  9. ...
  10. @Configuration(proxyBeanMethods = false)
  11. @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
  12. @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
  13. static class EmbeddedTomcat {
  14. }
  15. ...

可以看到代码里面直接就有Tomcat.class,这些都是tomcat里面的包,但是问题来了,我们的项目中,假设用另一个容器,不用tomcat容器,那么项目中也就不可能有那不会报错么?这个可跟场景1是有区别的,场景1用的是类全名,但是这里实实在在用的是类。实时确实不会报错的,那spring怎么做到的呢?

原理

1、编译会不会报错

我们的项目引入了spring-boot-starter-web,但是排除了tomcat,编译会不会报错,首先我们引入的jar包,引入的是字节码,所以编译肯定不会报错,毕竟别人已经编译好了的,怎么可能会报错呢?

2、启动会不会报错

我们项目启动,肯定回去运行下面这段逻辑的

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
  3. @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
  4. static class EmbeddedTomcat {
  5. }

此时spring用的是ASM字节码操作技术,是直接解析字节码获取Servlet.class, Tomcat.class这些对应的字节码类全民,后面再用加载器进行反射加载,所以也不会报错!

牛逼!

 40

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2