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就完成了。

猜你喜欢

转载自blog.csdn.net/Zong_0915/article/details/112968640