SpringBoot自动装配-@Conditional条件装配和自定义Starter
一. @Conditional
@Conditional主要用来提供自动装配的条件约束,一般和@Configuration和@Bean配合使用。
1.1 @Conditional
首先先来看一下它的结构:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
可以看出来,该注解可以接收一个Condition类型的数组。而Condition是一个函数式接口,主要用来提供matches方法,提供一个条件匹配规则,返回true表示可以注入Bean,反之则不注入。
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
案例1:@Conditional实现条件装配
自定义Condition:
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String os = conditionContext.getEnvironment().getProperty("os.name");
// 如果电脑运行环境是Mac,那么返回true
// 如果你的电脑是window,这里输入Windows
if (os.contains("Mac OS X")) {
return true;
}
return false;
}
}
自定义配置类:
@Configuration
public class ConditionConfig {
@Conditional(MyCondition.class)
@Bean
// 只有MyCondition返回true时,这里才会加载这个Bean
public FirstBean getBean(){
return new FirstBean();
}
}
启动类:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionConfig.class);
// FirstBean是我自定义的Bean
FirstBean bean = context.getBean(FirstBean.class);
System.out.println(bean.toString());
}
}
结果:
如果这时候改一下逻辑,比如当环境为Window时候才返回true,那么结果会报错,报错如下:
1.2 SpringBoot中的@Conditional
SpringBoot中,对@Conditional做了一些拓展,扩展的注解如下:
- ConditionalOnBean/ConditionalMissingBean:容器中存在某个类或者不存在某个Bean时进行Bean装载。
- ConditionalOnClass/ConditionalMissingClass:classpath下存在指定类或者不存在指定类时进行Bean装载。
- ConditionalOnCloudPlatForm:只有运行在指定的云平台上才加载指定的Bean。
- ConditionalOnExpression:基于spEL表达式的条件判断。
- ConditionalOnJava:只有运行指定版本的Java才会装载Bean。
- ConditionalOnJndi:只有指定的资源通过JNDI加载后才装载Bean。
- ConditionalOnWebApplication/ConditionalOnNotWebApplication:如果是Web应用或者不是,才装载Bean。
- ConditionalOnProperty:系统中指定的对应的属性是否有对应的值,有才会装载Bean。
- ConditionalOnResource:要加载的Bean依赖指定资源是否存在于classpath中。
- ConditionalOnSingleCandidate:只有在确定了给定Bean类的单个候选项时才会加载Bean。
这些注解只需要添加到@Configuration配置类的类级别或者方法级别时,根据每个注解的作用来传参即可。
案例2:@Conditional扩展
ConditionalOnProperty:
- 只有在application.properties或者application.yml文件当中test.bean.enble=true时才会加载这个类
- 如果没有匹配上也会加载,因为matchIfMissing=true(默认是false)
@Configuration
@ConditionalOnProperty(value = "test.bean.enble", havingValue = "true", matchIfMissing = true)
public class ConditionConfig {
}
ConditionalOnBean:
- 只有容器中存在FirstBean这个类,ConditionConfig才会装配
@Configuration
@ConditionalOnBean(FirstBean.class)
public class ConditionConfig {
}
ConditionalOnResource:
- 在classpath中如果存在test.properties文件,那么会自动装载ConditionConfig。
@Configuration
@ConditionalOnResource(resources = "/test.properties")
public class ConditionConfig {
}
1.3 spring-autoconfigure-metadata
除了@Conditional注解类,在SpringBoot中还提供了spring-autoconfigure-metadata.properties文件来实现批量自动装配的条件配置。
起作用和@Conditional一样,只是将这些条件配置放在了配置文件中。同时,通过这种配置化的方式来实现条件过滤必须要遵循两个条件:
- 配置文件的路径和名称必须是/META-INF/spring-autoconfigure-metadata.properties
- 配置文件中key的格式:自动配置类的全路径名.条件=值
优势:
- 可以有效地降低SpringBoot的启动时间,通过这种过滤方式可以减少配置类的加载数量,因为这个过滤发生在配置类的装载之前,所以他可以降低SpringBoot启动时装载Bean的耗时。
二. Starter
Starter组件主要有三个功能:
- 涉及相关组件的Jar包依赖。
- 自动实现Bean的装配。
- 自动声明并且加载application.properties文件中的属性配置。
Starter的命名规范:
- 官方命名的格式:spring-boot-starter-模块名称
- 自定义命名格式:模块名称-spring-boot-starter
案例3:自定义实现一个基于Redis的Starter
项目结构:
自定义配置类RedisAutoConfiguration:
- 定义需要自动装配的配置类,主要是把RedissonClient装配到IOC容器中。
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
@Configuration
@ConditionalOnClass(Redisson.class)
@EnableConfigurationProperties(RedissonProperties.class)
public class RedisAutoConfiguration {
RedissonClient redissonClient(RedissonProperties redissonProperties) {
Config config = new Config();
String prefix = "redis://";
if (redissonProperties.isSsl()) {
prefix = "rediss://";
}
SingleServerConfig singleServerConfig = config.useSingleServer().setAddress(prefix + redissonProperties.getHost() + ":" + redissonProperties.getPort())
.setConnectTimeout(redissonProperties.getTimeout());
if (!StringUtils.isEmpty(singleServerConfig)) {
singleServerConfig.setPassword(redissonProperties.getPassword());
}
return Redisson.create(config);
}
}
自定义配置类RedissonProperties:
- 定义属性类,实现在application.properties中配置Redis的连接参数。
- @ConfigurationPropertie的作用是把当前类的属性和配置文件(properties/yml)中的配置进行绑定,并且前缀是my.redisson
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "my.redisson")
public class RedissonProperties {
private String host="localhost";
private String password;
private int port=6379;
private int timeout;
private boolean ssl;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public boolean isSsl() {
return ssl;
}
public void setSsl(boolean ssl) {
this.ssl = ssl;
}
}
spring.factories:
- 目的是让SpringBoot程序可以扫描到该文件完成自动装配,key和value对应如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.config.RedisAutoConfiguration
application.properties:
- 设置属性配置。
- 这个属性会自动绑定到RedissonProperties中定义的对应属性上。
my.redission=192.168.237.130
my.redission.port=6379
pom:
- 添加starter依赖(redis-spring-boot-starter)
<parent>
<groupId>org.springframework.boot</groupId>
<version>2.3.5.RELEASE</version>
<artifactId>spring-boot-starter-parent</artifactId>
</parent>
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.11.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>redis-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
完成以上步骤后,一个简单的手写Starter就完成了。