目录
[什么是Spring Boot自动配置](#什么是Spring Boot自动配置)
什么是Spring Boot自动配置
SpringBoot的自动配置简单来说就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们去手动装配,从而简化开发,省去了大部分繁琐的配置操作。
自动配置中需要的重要注解
我们接下来通过一步一步了解Spring Boot自动配置中的关键注解来明白SpringBoot怎么实现自动配置。
一.@Condition
@Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建Bean 操作。
增加条件判断注解的目的是为了使我们的程序动态的开启自动配置。
举例说明:
我们使用Spring Boot进行程序开发时,会需要在pom.xml配置文件中进行坐标导入,当你导入某个坐标后,你在程序中就能使用需要的类和方法。
这里以导入Redis坐标为例:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
当你导入坐标之后,你的程序就装配了该依赖库封装的两个模板类,帮助我们实现操作redis。这时你在启动类中getBean("redisTemplate"),就能获取到其中redisTemplate模板类。
java
@SpringBootApplication
public class SpringbootCondition01Application {
public static void main(String[] args) {
//启动SpringBoot的应用,返回Spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(SpringbootCondition01Application.class, args);
//获取Bean,redisTemplate
Object redisTemplate = context.getBean("redisTemplate");
System.out.println(redisTemplate);
}
}
启动后获取到
如果你没有添加starter-data-redis坐标,那么从容器中获取模板类则报空
那么在以上例子中其实就实现了通过@Conditon进行选择配置,如果存在该坐标就帮你在容器中注入模板类。
为了更好的理解,我们可以自己使用@Condition来选择注入一下。
假设我们现在需求在 Spring 的 IOC 容器中有一个 User 的 Bean,要求:导入Jedis坐标后,加载该Bean,没导入,则不加载。
1.创建一个User类
java
public class User {
}
因为我们不能直接在User类上直接写注入注解,这样就不能选择,直接注入了User类。所以创建一个间接创建User类的配置类。
2.创建UserConfig配置类
java
@Configuration //声明配置类
public class UserConfig {
@Bean //向容器中注入一个User类型的user对象
@Conditional(value= ClassCondition.class) //判断,通过ClassCondition的实现类
public User user(){
return new User();
}
}
3.创建ClassCondition实现类
必须实现Condition接口。
java
public class ClassCondition implements Condition {
/**
*
* @param context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
* @param metadata 注解元对象。 可以用于获取注解定义的属性值
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//1.需求: 导入Jedis坐标后创建Bean
//思路:判断redis.clients.jedis.Jedis.class文件是否存在
boolean flag = true;
try {
Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
} catch (ClassNotFoundException e) {
flag = false;
}
return flag;
}
}
4.测试
在启动类,从容器中获取getBean("user")。
因为我们没有导入jedis坐标,所以报错。
二.@Enable
SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。
例如@EnableAsync注解可以使Bean在spring应用中支持异步功能,@EnableWebMvc注解引入了MVC框架在Spring应用中需要用到的所有bean。
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
...
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
可以看到其底层原理是使用@Import注 解导入一些配置类,实现Bean的动态加载。
@Import注解 :@Enable底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。
@Import提供4种用法:
① 导入Bean
② 导入配置类
③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类
④ 导入 ImportBeanDefinitionRegistrar 实现类。
举例说明:
① 导入Bean
简单创建一个User类并且创建User的配置类,使用@Import将User类导入当前容器。这里还是选择在启动类中导入并且getBean()测试。
② 导入配置类
其实就是①的基础上,可以导入User的配置类。也成功getBean
③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类
创建一个实现ImportSelector接口的实现类,并且需要重写selectImports方法。
java
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//目前字符串数组的内容是写死的,未来可以设置在配置文件中动态加载
return new String[]{"com.apesource.ttt.pojo.User"};
}
}
测试成功
④ 导入 ImportBeanDefinitionRegistrar 实现类。
创建实现ImportBeanDefinitionRegistrar 的实现类
java
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//1.获取user的definition对象
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
//2.通过beanDefinition属性信息,向spring容器中注册id为user的对象
registry.registerBeanDefinition("user", beanDefinition);
}
}
测试成功
三.@EnableAutoConfiguration
我们观察启动类,可以发现启动类上都有@SpringBootApplication注解。
java
//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
//以为是启动了一个方法,没想到启动了一个服务
SpringApplication.run(SpringbootApplication.class, args);
}
}
进入 @SpringBootApplication注解内部
java
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
// ......
}
其中有
1.@ComponentScan
这个注解在Spring中很重要 ,它对应XML配置中的元素。
作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
2.@SpringBootConfiguration
作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;
java
//@SpringBootConfiguration注解内部
//这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
@Configuration
public @interface SpringBootConfiguration {}
//里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用
@Component
public @interface Configuration {}
3.@EnableAutoConfiguration开启自动配置功能
作用:@EnableAutoConfiguration 告诉SpringBoot开启自动配置功能,这样自动配置才能生效;
在@EnableAutoConfiguration注解内部又包含一些重要注解:
1.@AutoConfigurationPackage :自动配置包
java
//AutoConfigurationPackage的子注解
//Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
...
}
2.@Import({AutoConfigurationImportSelector.class})
作用:自动配置导入选择器,给容器中导入一些组件
看到这里我们就能大概知道,SpringBoot自动配置重要的一些地方
在启动类时通过@SpringBootApplication注解就开启了Spring Boot自动配置了一些库进入你的程序。而在该配置类内部,
1.@EnableAutoConfiguration注解内部 使用 @Import(AutoConfigurationImportSelector.class) 来加载配置类。
2.配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载这些
3.配置类,初始化Bean 并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean
实现一个自定义starter
本文一直以整合Redsi为例,这里也实现一个与Redis相关的自定义启动器。
需求: 自定义redis-starter,要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean。
这里参考考导入mybatis坐标时,我们导入了一个mybatis-spring-boot-starter的启动器。这样在我们的
就能找到mybatis相关的jar包,不仅仅导入了mybatis-spring-boot-starter,还有mybatis-spring-boot-autoconfigure。
实现步骤:
1.创建一个基本的SpringBoot项目,在项目springboot_starter中先完成基本配置。
当你在项目的pom.xml文件中导入坐标时,就开启了关于该坐标的相关自动配置。那么我们在实现自定义启动器时,也可以使用坐标。导入的坐标就是我们自定义的有关内容,我们观察pom.xml文件就能发现其实每一个项目都在其中有自己的坐标。那么我们就可以把自定义的redis-spring-boot-starter模块的坐标写入redis_starter中。
2.创建redis-spring-boot-starter模块,依赖redis-spring-boot-autoconfigure的模块
3.在redis-spring-boot-autoconfigure模块中初始化Jedis的Bean,并定义META-INF/spring.factories文件
在该模块中创建RedisAutoconfiguration配置类,注入jedis对象
为了动态的获取,创建一个application.yml在springboot_starter获取端口号、ip
在redis-spring-boot-autoconfigure中创建一个Redis身体
java
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private String host="localhost";
private int port=6379;
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
}
java
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoconfiguration {
//注入jedis
@Bean
public Jedis jedis(RedisProperties redisProperties){
return new Jedis(redisProperties.getHost(),redisProperties.getPort());
}
}
怎样加载到这个类呢,就需要MEAT-INF文件
4.在测试模块中引入自定义的redis-starter依赖,测试获取Jedis的Bean,操作redis。
获取到jedis对象证明成功实现了自定义启动时自动配置jedis.