SpringBoot的自动配置就是当Spring容器启动后,一些自动配置类(只是自动配置类,并不是当前的组件配置到IOC容器中,自动配置类通过@Conditional注解来按需配置)就自动装配的IOC容器中,不需要我们手动注入,从而简化了开发,省去繁琐的配置。
一、Condition
Condition是在Spring4.0增加的条件判断功能,通过这个功能可以实现选择性的创建Bean操作。
具体实例:
在Spring的IOC容器中有一个User的Bean
步骤:
1.导入Jedis坐标后,加载该Bean,没有导入,则不加载
自定义类ClassCondition实现Condition接口:
package com.ly.springboot_conditional.condition;
import com.ly.springboot_conditional.domain.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;
/**
*
* @param //context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
* @param //metadata 注解元对象。 可以用于获取注解定义的属性值
* @return
*/
public class ClassCondation implements Condition {
@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;*/
//需求:导入通过注解属性value指定坐标后创建Bean
//获取注解属性值 value
Map<String,Object> map=metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
System.out.println(map);
String[] value=(String[]) map.get("value");
boolean flag=true;
try {
for (String className : value) {
Class<?> cls=Class.forName(className);
}
} catch (ClassNotFoundException e) {
flag=false;
}
return flag;
}
}
注入User类型的bean:
package com.ly.springboot_conditional.config;
import com.ly.springboot_conditional.condition.ClassCondation;
import com.ly.springboot_conditional.condition.ConditionOnClass;
import com.ly.springboot_conditional.domain.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfig {
//@Conditional中的ClassCondition.class的matches方法,返回true执行以下代码,否则反之
@Bean
// @Conditional(ClassCondition.class)
// @ConditionOnClass(value="redis.clients.jedis.Jedis")
@ConditionOnClass(value={"com.alibaba.fastjson.JSON","redis.clients.jedis.Jedis"})
public User user(){
return new User();
}
//情况2
@Bean
//当容器中有一个key=k1且value=v1的时候user2才会注入
//在application。properties文件中添加k1=v1
@ConditionalOnProperty(name = "k1",havingValue = "v1")
public User user2(){
return new User();
}
}
pom文件中引入Jedis坐标测试类
package com.ly.springboot_conditional;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class SpringbootConditionalApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context=SpringApplication.run(SpringbootConditionalApplication.class, args);
/******获取容器中User******/
// Object user1 = context.getBean("user");
// System.out.println(user1);
Object user2 = context.getBean("user2");
System.out.println(user2);
}
}
如果没引入Jedis成功会报异常;
导入Jedies坐标之后
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
实例2:
在Spring的IOC容器中有一个User的Bean,要求:
1.将实现类的判断定义为动态的。判断哪个字节码文件存在可以动态指定
步骤:
- 不使用@Conditional(ClassCondition.class)注解
- 自定义注解@ConditionOnClass,因为它和@Conditional注解功能一致,
- 编写ClassCondition中的matches方法
自定义注解ConditionOnClass:
package com.ly.springboot_conditional.condition;
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})//可修饰在类与方法上
@Retention(RetentionPolicy.RUNTIME)//注解生效节点runtime
@Documented//生成文档
@Conditional(value = ClassCondation.class)
public @interface ConditionOnClass {
String[] value();//设置此注解的属性redis.clients.jedis.Jedis
}
自定义类ClassCondition的matches方法判断逻辑,根据自定义注解指定的value值来动态判断是否可以加载某个bean:
package com.ly.springboot_conditional.condition;
import com.ly.springboot_conditional.domain.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;
/**
*
* @param //context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
* @param //metadata 注解元对象。 可以用于获取注解定义的属性值
* @return
*/
public class ClassCondation implements Condition {
@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;*/
//需求:导入通过注解属性value指定坐标后创建Bean
//获取注解属性值 value
Map<String,Object> map=metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
System.out.println(map);
String[] value=(String[]) map.get("value");
boolean flag=true;
try {
for (String className : value) {
Class<?> cls=Class.forName(className);
}
} catch (ClassNotFoundException e) {
flag=false;
}
return flag;
}
}
测试是否引入fast.json的坐标来加载user的bean:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fast.json</artifactId> <version>1.2.62</version> </dependency>
总结:
自定义条件:
- 定义条件类:自定义类实现Condition接口,重写matches方法中进行逻辑及判断,返回boolean值。 matches的两个参数: ·context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。 ·metadata:元数据对象,用于获取注解属性。
- 判断条件:在初始化Bean时,实验@Conditional(条件类.class)注解 SpringBoot提供的常用注解: 一下注解在springboot-autoconfiguration的condition包下 · ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean · ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean · ConditionOnMissingBean:判断环境中有没有对应的bean才初始化Bean · ConditionOnBean:判断环境中有对应Bean才初始化Bean
二、@Enable注解
SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理 是使用@Import注 解导入一些配置类,实现Bean的动态加载
@Import 注解
@Enable底层依赖于 @Import 注解导入一些类,使用 @Import 导入的类会被 Spring 加载到 IOC 容器中。
而 @Import 提供 4 中用法:
导入Bean
导入配置类
导入 ImportSelector 实现类。一般用于加载配置文件中的类
导入 ImportBeanDefinitionRegistrar 实现类。
三、.@EnableAutoConfiguration 注解
1、当SpringBoot应用启动的时候,就从主方法里面进行启动的它主要加载了@SpringBootApplication注解主配置类。
@SpringBootApplication
public class SpringbootCondition01Application {
public static void main(String[] args) {
SpringApplication.run(SpringbootCondition01Application.class, args);
}
2.@SpringBootApplication注解的内部当中,可以发现它是一个复合注解,它由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个注解组成
(1)@ComponentScan
这个注解在Spring中很重要 ,它对应XML配置中的元素。
作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
(2)@SpringBootConfiguration
作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;
(3)@EnableAutoConfiguration
它主要利用了一个AutoConfigurationImportSelector选择器给Spring容器中来导入一些组件。以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration 告诉SpringBoot开启自动配置功能,这样自动配置才能生效;
@AutoConfigurationPackage :自动配置包
作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;
AutoConfigurationImportSelector :自动配置导入选择器,给容器中导入一些组件
进入 AutoConfigurationImportSelector类的内部。扫描所有jar包类路径下META-INF/spring.factories,把扫描到的这些文件的内容包装成properties对象,从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后将他们添加到容器中。从META-INF/spring.factories 位置来加载一个文件默认扫描我们当前系统里面所有 META-INF/spring.factories位置的文件
总结原理:
- @EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class) 来加载配置类。
- 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot应用启动时,会自动加载这些配置类,初始化Bean并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean
四.自定义启动器
自定义redis-starter ,要求当导入 redis 坐标时, SpringBoot 自动创建 Jedis 的 Bean
实现步骤:
1、创建redis_spring_boot_autoconfigure模块
2、创建自动加载和属性配置类
package com.ly;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoconfiguration {
//注入jedis
@Bean
@ConditionalOnMissingBean(Jedis.class)
public Jedis jedis(RedisProperties redisProperties){
return new Jedis(redisProperties.getHost(),redisProperties.getPort());
}
}
package com.ly;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private String host="localhost";
private int port=6379;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
3、定义META INF/spring.factories文件,引入jedis坐标
4、创建springboot_starter模块,依赖springboot_redis_autoconfigure的模块,在pom.xml中引入坐标
5、在springboot_starter启动类测试