文章目录
什么是 SpringBoot 自动配置
SpringBoot
的自动配置就是当 Spring
容器启动之后,一些配置类,bean
对象就自动存入到了 IoC
容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作
SpringBoot
自动配置,就是指 SpringBoot
是如何将依赖 jar
包中的配置以及 Bean
加载到 Spring IoC
容器中的
我们学习主要分以下两个方面:
Spring
是如何把对象加载到SpringIoC
容器中的SpringBoot
是如何实现的
Spring 加载 Bean
问题描述
需求:使用 Spring
管理第三方的 jar
包的配置
- 引入第三方的包,其实就是在该项目下,引入第三方的代码,我们采用在该项目下创建不同的目录来模拟第三方的代码引入
数据准备:
- 创建项目
spring-autoconfig
,当前项目目录为:com.example.demo
- 模拟第三方代码文件在
com.bite.autoconfig
目录下
第三方文件代码:
public class BiteConfig {
public void study() {
System.out.println("start study...");
}
}
- 获取
BiteConfig
这个Bean
写测试代码
@SpringBootTest
class ApplicationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
void contextLoads() {
BiteConfig biteConfig = applicationContext.getBean(BiteConfig.class, "biteConfig");
System.out.println(biteConfig);
}
}
- 运行程序
- 观察日志:
No qualifying bean of type 'com.bite.autoconfig.BiteConfig' available
- 没有
com.bite.autoconfig.BiteConfig
这个类型的Bean
原因分析
Spring
通过五大注解和 @Bean
注解可以帮助我们把 Bean
加载到 SpringIoC
容器中,以上有个前提就是这些注解类需要和 SpringBoot
启动类在同一个目录下(@SpringBootApplication
标注的类就是 SpringBoot
的启动类)
- 启动类所在目录为:
com.example.deml
,而BiteConfig
这个类在com.bite.autoconfig
下,所以SpringBoot
没有扫描到
解决方案
我们需要指定路径或者引入的文件,告诉 spring
,让 Spring
进行扫描到
常见的解决方案有两种:
@ComponentScan
组件扫描Import
导入(使用@Import
导入的类会被Spring
加载到IoC
容器中)
我们通过代码来看如何解决
@ComponentScan
通过 @ComponentScan
注解,指定 Spring
扫描路径
@ComponentScan("com.bite.autoconfig")
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 可以指定扫描多个包
@ComponentScan({"com.bite.autoconfig","com.example.demo"})
运行程序:
可以看到,这次 biteConfig
的 Bean
获取到了
Spring
是否使用了这种方式呢?- 非常明显,没有(因为我们引入第三方框架时,没有加扫描路径,如
mybatis
) - 如果
SpringBoot
采用这种方式,当我们引入大量的第三方依赖,比如MyBatis
,jackson
等时,就需要在启动类上配置不同依赖需要扫描的包 - 这种方式会非常繁琐
@Import
@Import
导入主要有以下几种形式
- 导入类
- 导入
ImportSelector
接口实现类
1 . 导入类
@Import(BiteConfig.class)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 运行程序
- 可以看到,这种方式也可以告诉
Spring
加载biteConfig
问题:如果又多了一些配置项呢?
@Component
public class BiteConfig2 {
public void study2() {
System.out.println("start study2...");
}
}
我们可以采用导入多个类
@Import({BiteConfig.class, BiteConfig2.class})
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
很明显,这种方式也很繁琐,所以,SpringBoot
依然没有采用
2 . 导入 ImportSelector 接口实现类
ImportSelector
接口实现类
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 需要导入的全限定类名
return new String[]{"com.bite.autoconfig.BiteConfig", "com.bite.autoconfig.BiteConfig2"};
}
}
启动类:
@Import(MyImportSelector.class)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
运行程序:
- 可以看到,我们采用这种方式也可以导入第三方依赖提供的
Bean
问题
问题:
但是他们都有一个明显的问题,就是使用者需要知道第三方依赖中有哪些 Bean
对象或配置类。如果漏掉了其中一些 Bean
,就可能导致我们的项目出现大事故
依赖中有哪些 Bean
,使用时需要配置哪些 Bean
,第三方依赖最清楚,那能否由典发那个依赖来做这件事呢?
- 比较常见的方案就是第三方依赖给我们提供一个注解,这个注解一般都以
@EnableXxxx
开头,注解中封装的就是@Import
注解
- 第三方依赖提供注解
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)
public @interface EnableBiteConfig {
}
注解中封装 @Import
注解,导入 MyImportSelector.class
- 在启动类上使用第三方提供的注解
@EnableBiteConfig
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
运行程序:
- 可以看到,这种方式也可以导入第三方依赖提供的
Bean
- 并且这种方式更优雅一点,
SpringBoot
采用的也是这种方式