admin管理员组

文章数量:1599541

Springboot Condition 详解

  • Condition 是spring boot 4.0增加的功能,它可以选择性的创建bean

  • @Conditional 源码查看

  //
  // Source code recreated from a .class file by IntelliJ IDEA
  // (powered by FernFlower decompiler)
  //
  
  package org.springframework.context.annotation;
  
  import java.lang.annotation.Documented;
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;
  
  @Target({ElementType.TYPE, ElementType.METHOD})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  public @interface Conditional {
      Class<? extends Condition>[] value();
  }

可见@Conditional里需要传入一个Condition的子类

  • 查看Condition源码
  //
  // Source code recreated from a .class file by IntelliJ IDEA
  // (powered by FernFlower decompiler)
  //
  
  package org.springframework.context.annotation;
  
  import org.springframework.core.type.AnnotatedTypeMetadata;
  
  @FunctionalInterface
  public interface Condition {
    /**
    * 方法参数说明:
    * ConditionContext: 上下文对象:可以用户获取classloader,ioc容器,环境
    * AnnotatedTypeMetadata: 注解元对象:可以获取注解定义的属性值
    **/
      boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
  }

Condition接口提供了一个方法matches,当返回true时条件成立,创建对应的类。返回false反之

使用时,用户实现Condition接口,加入用户自己的判断逻辑,选择性的创建对应的been。

示例

  • 创建一个spring boot 工程

    Application类

package com.lab;

import com.lab.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * springboot启动类
 *
 * @author 杨秋颐 wyuyangqy@163
 * @since 2021-12-14 14:49
 */
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        User user = (User)context.getBean("user");
        System.out.println("user = " + user);
    }
}
  • 用户实体类 略

  • 用户配置类

  package com.lab.config;
  
  import com.lab.pojo.User;
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Configuration;
  
  /**
   * 用户配置类
   *
   * @author 杨秋颐 wyuyangqy@163
   * @since 2021-12-14 14:55
   */
  @Configuration
  public class UserConfig {
  
      @Bean
      public User user(){
          return new User();
      }
  }
  • 启动springboot 可见控制台是可以输出用户,也就是用户类spring已经给我们创建了

添加Condition

  • 修改 用户配置类
  package com.lab.config;
  
  import com.lab.pojo.User;
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Conditional;
  import org.springframework.context.annotation.Configuration;
  
  /**
   * 用户配置类
   *
   * @author 杨秋颐 wyuyangqy@163
   * @since 2021-12-14 14:55
   */
  @Configuration
  public class UserConfig {
  
      @Bean
      @Conditional(UserCondition.class) //添加Conditional注解 UserCondition见下面代码
      public User user(){
          return new User();
      }
  }
  • UserCondition代码,由刚才的源码查看可知道,我们需要去实现Condition接口,并重写matches方法
  package com.lab.config;
  
  import org.springframework.context.annotation.Condition;
  import org.springframework.context.annotation.ConditionContext;
  import org.springframework.core.type.AnnotatedTypeMetadata;
  
  /**
   * 创建用户的条件
   *
   * @author 杨秋颐 wyuyangqy@163
   * @since 2021-12-14 15:06
   */
  public class UserCondition implements Condition {
      @Override
      public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //todo 用户自己的判断逻辑
        //默认返回 false,则不创建user
          return false;
      }
  }
  • 启动springboot 可见控制台报错,在ioc容器中没找到相应名为"user"的bean

需求当引入了fastjson时,创建user

  • 修改UserCondition类
  package com.lab.config;
  
  import org.springframework.context.annotation.Condition;
  import org.springframework.context.annotation.ConditionContext;
  import org.springframework.core.type.AnnotatedTypeMetadata;
  
  /**
   * 创建用户的条件
   *
   * @author 杨秋颐 wyuyangqy@163
   * @since 2021-12-14 15:06
   */
  public class UserCondition implements Condition {
      @Override
      public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
          // 当引入了fastjson 刚创建user
          try {
              Class<?> aClass = Class.forName("com.alibaba.fastjson.JSON");
              return true;
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
              return false;
          }
      }
  }
  • 启动springboot 可见控制台报错,在ioc容器中没找到相应名为"user"的bean

  • 在maven中引入 fastjson

    <!-- https://mvnrepository/artifact/com.alibaba/fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.78</version>
    </dependency>
    
  • 启动springboot 可见控制台是可以输出用户,也就是用户类spring已经给我们创建了

需求二,将上面需求改为判断任意类名

  • 分析:任意类名可以用我们自定义的注解传到matches,由matches的入参:AnnotatedTypeMetadata 获取

实现

  • 定义我们自己的注解
  package com.lab.anno;
  
  import com.lab.config.UserCondition;
  import org.springframework.context.annotation.Conditional;
  
  import java.lang.annotation.Documented;
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;
  
  @Target({ElementType.TYPE, ElementType.METHOD})
  @Retention(RetentionPolicy.RUNTIME)
  @Documented
  //继承之前定义的
  @Conditional(UserCondition.class)
  public @interface UserConditionOnClasses {
      String[] value();
  }
  • 修改UserCondition 通过annotatedTypeMetadata 获取注解传进来的className
  package com.lab.config;
  
  import com.lab.anno.UserConditionOnClasses;
  import org.springframework.context.annotation.Condition;
  import org.springframework.context.annotation.ConditionContext;
  import org.springframework.core.type.AnnotatedTypeMetadata;
  
  import java.util.Map;
  
  /**
   * 创建用户的条件
   *
   * @author 杨秋颐 wyuyangqy@163
   * @since 2021-12-14 15:06
   */
  public class UserCondition implements Condition {
      @Override
      public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
          //获取传入的注解中的属性
          Map<String, Object> map = annotatedTypeMetadata.getAnnotationAttributes(UserConditionOnClasses.class.getName());
          //获取@UserConditionOnClasses 中的value属性。我们定义的是 String[] 类型
          String[] value = (String[]) map.get("value");
          // 当引入了fastjson 刚创建user
          try {
              for (String className : value) {
                  //"com.alibaba.fastjson.JSON"
                  Class<?> aClass = Class.forName(className);
              }
              return true;
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
              return false;
          }
      }
  }
  • 修改userConfig,使用我们定义的注解传入class的信息
   package com.lab.config;
   
   import com.lab.anno.UserConditionOnClasses;
   import com.lab.pojo.User;
   import org.springframework.context.annotation.Bean;
   import org.springframework.context.annotation.Conditional;
   import org.springframework.context.annotation.Configuration;
   
   /**
    * 用户配置类
    *
    * @author 杨秋颐 wyuyangqy@163
    * @since 2021-12-14 14:55
    */
   @Configuration
   public class UserConfig {
   
       @Bean
       @UserConditionOnClasses("com.alibaba.fastjson.JSON")
       public User user(){
           return new User();
       }
   }
  • 启动springboot 可见控制台是可以输出用户,也就是用户类spring已经给我们创建了

查看Springboot 给我们提供的Condition注解

  • 在spring-boot-autoconfigure下的condition包中
注解名备注
ConditionalOnBean当存在某对象时成立
ConditionalOnClass当存在某字节码时成立
ConditionalOnMissingBean当不存在某对象时成立
ConditionalOnProperty当存在某配置properties值时成立

查看Starter包的工作原理

  • 查看与condition相同目录下的data->redis

  • RedisAutoConfiguration

 //
 // Source code recreated from a .class file by IntelliJ IDEA
 // (powered by FernFlower decompiler)
 //
 
 package org.springframework.boot.autoconfigure.data.redis;
 
 import java.net.UnknownHostException;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.data.redis.core.RedisOperations;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.core.StringRedisTemplate;
 
 @Configuration
 @ConditionalOnClass({RedisOperations.class})
 @EnableConfigurationProperties({RedisProperties.class})
 @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
 public class RedisAutoConfiguration {
     public RedisAutoConfiguration() {
     }
 
     @Bean
   //当ioc容器中不存在 redisTemplate 名字的bean时,创建
     @ConditionalOnMissingBean(
         name = {"redisTemplate"}
     )
     public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
         RedisTemplate<Object, Object> template = new RedisTemplate();
         template.setConnectionFactory(redisConnectionFactory);
         return template;
     }
 
     @Bean
     @ConditionalOnMissingBean
     public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
         StringRedisTemplate template = new StringRedisTemplate();
         template.setConnectionFactory(redisConnectionFactory);
         return template;
     }
 }

本文标签: 详解SpringBootcondition