您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關SpringBoot中怎么自動裝配Condition的實現方式,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
@Conditional注解在Spring4.0中引入,其主要作用就是判斷條件是否滿足,從而決定是否初始化并向容器注冊Bean。
@Conditional
注解定義如下:其內部只有一個參數為Class對象數組,且必須繼承自Condition
接口,通過重寫Condition
接口的matches
方法來判斷是否需要加載Bean
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { Class<? extends Condition>[] value(); }
Condition
接口定義如下:該接口為一個函數式接口,只有一個matches
接口,形參為ConditionContext context, AnnotatedTypeMetadata metadata
。ConditionContext
定義如2.2.1
,AnnotatedTypeMetadata
見名知意,就是用來獲取注解的元信息的
@FunctionalInterface public interface Condition { boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
2.2.1 ConditionContext
ConditionContext
接口定義如下:通過查看源碼可以知道,從這個類中可以獲取很多有用的信息
public interface ConditionContext { /** * 返回Bean定義信息 * Return the {@link BeanDefinitionRegistry} that will hold the bean definition * should the condition match. * @throws IllegalStateException if no registry is available (which is unusual: * only the case with a plain {@link ClassPathScanningCandidateComponentProvider}) */ BeanDefinitionRegistry getRegistry(); /** * 返回Bean工廠 * Return the {@link ConfigurableListableBeanFactory} that will hold the bean * definition should the condition match, or {@code null} if the bean factory is * not available (or not downcastable to {@code ConfigurableListableBeanFactory}). */ @Nullable ConfigurableListableBeanFactory getBeanFactory(); /** * 返回環境變量 比如在application.yaml中定義的信息 * Return the {@link Environment} for which the current application is running. */ Environment getEnvironment(); /** * 返回資源加載器 * Return the {@link ResourceLoader} currently being used. */ ResourceLoader getResourceLoader(); /** * 返回類加載器 * Return the {@link ClassLoader} that should be used to load additional classes * (only {@code null} if even the system ClassLoader isn't accessible). * @see org.springframework.util.ClassUtils#forName(String, ClassLoader) */ @Nullable ClassLoader getClassLoader(); }
通過一個簡單的小例子測試一下@Conditional
是不是真的能實現Bean的條件化注入。
首先我們創建一個SpringBoot項目
3.1.1 導入依賴
這里我們除了springboot依賴,再添加個lombok依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.ldx</groupId> <artifactId>condition</artifactId> <version>0.0.1-SNAPSHOT</version> <name>condition</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3.1.2 添加配置信息
在application.yaml 中加入配置信息
user: enable: false
3.1.3 創建User類
package com.ldx.condition; import lombok.AllArgsConstructor; import lombok.Data; /** * 用戶信息 * @author ludangxin * @date 2021/8/1 */ @Data @AllArgsConstructor public class User { private String name; private Integer age; }
3.1.4 創建條件實現類
package com.ldx.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; /** * 用戶bean條件判斷 * @author ludangxin * @date 2021/8/1 */ public class UserCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { Environment environment = conditionContext.getEnvironment(); // 獲取property user.enable String property = environment.getProperty("user.enable"); // 如果user.enable的值等于true 那么返回值為true,反之為false return "true".equals(property); } }
3.1.5 修改啟動類
package com.ldx.condition; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @Slf4j @SpringBootApplication public class ConditionApplication { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(ConditionApplication.class, args); // 獲取類型為User類的Bean User user = applicationContext.getBean(User.class); log.info("user bean === {}", user); } /** * 注入User類型的Bean */ @Bean @Conditional(UserCondition.class) public User getUser(){ return new User("張三",18); } }
3.2.1 當user.enable=false
報錯找不到可用的User類型的Bean
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.5.3) 2021-08-01 17:07:51.994 INFO 47036 --- [ main] com.ldx.condition.ConditionApplication : Starting ConditionApplication using Java 1.8.0_261 on ludangxindeMacBook-Pro.local with PID 47036 (/Users/ludangxin/workspace/idea/condition/target/classes started by ludangxin in /Users/ludangxin/workspace/idea/condition) 2021-08-01 17:07:51.997 INFO 47036 --- [ main] com.ldx.condition.ConditionApplication : No active profile set, falling back to default profiles: default 2021-08-01 17:07:52.461 INFO 47036 --- [ main] com.ldx.condition.ConditionApplication : Started ConditionApplication in 0.791 seconds (JVM running for 1.371) Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.ldx.condition.User' available at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172) at com.ldx.condition.ConditionApplication.main(ConditionApplication.java:16) Process finished with exit code 1
3.2.2 當user.enable=true
正常輸出UserBean實例信息
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.5.3) 2021-08-01 17:13:38.022 INFO 47129 --- [ main] com.ldx.condition.ConditionApplication : Starting ConditionApplication using Java 1.8.0_261 on ludangxindeMacBook-Pro.local with PID 47129 (/Users/ludangxin/workspace/idea/condition/target/classes started by ludangxin in /Users/ludangxin/workspace/idea/condition) 2021-08-01 17:13:38.024 INFO 47129 --- [ main] com.ldx.condition.ConditionApplication : No active profile set, falling back to default profiles: default 2021-08-01 17:13:38.434 INFO 47129 --- [ main] com.ldx.condition.ConditionApplication : Started ConditionApplication in 0.711 seconds (JVM running for 1.166) 2021-08-01 17:13:38.438 INFO 47129 --- [ main] com.ldx.condition.ConditionApplication : user bean === User(name=張三, age=18)
上面的例子通過使用@Conditional
和Condition
接口,實現了spring bean的條件化注入。
好處:
可以實現某些配置的開關功能,如上面的例子,我們可以將UserBean換成開啟緩存的配置,當property的值為true時,我們才開啟緩存的配置。
當有多個同名的bean時,如何抉擇的問題。
實現自動化的裝載。如判斷當前classpath中有mysql的驅動類時(說明我們當前的系統需要使用mysql),我們就自動的讀取application.yaml中的mysql配置,實現自動裝載;當沒有驅動時,就不加載。
從上面的使用說明中我們了解到了條件注解的大概使用方法,但是代碼中還是有很多硬編碼的問題。比如:UserCondition
中的property的key包括value都是硬編碼,其實我們可以通過再擴展一個注解來實現動態的判斷和綁定。
import org.springframework.context.annotation.Conditional; import java.lang.annotation.*; /** * 自定義條件屬性注解 * <p> * 當配置的property name對應的值 與設置的 value值相等時,則注入bean * @author ludangxin * @date 2021/8/1 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented // 指定condition的實現類 @Conditional({UserCondition.class}) public @interface MyConditionOnProperty { // 配置信息的key String name(); // 配置信息key對應的值 String value(); }
package com.ldx.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Map; /** * 用戶bean條件判斷 * @author ludangxin * @date 2021/8/1 */ public class UserCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { Environment environment = conditionContext.getEnvironment(); // 獲取自定義的注解 Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes("com.ldx.condition.MyConditionOnProperty"); // 獲取在注解中指定的name的property的值 如:user.enable的值 String property = environment.getProperty(annotationAttributes.get("name").toString()); // 獲取預期的值 String value = annotationAttributes.get("value").toString(); return value.equals(property); } }
測試后,結果符合預期。
其實在spring中已經內置了許多常用的條件注解,其中我們剛實現的就在內置的注解中已經實現了,如下。
注解 | 說明 |
---|---|
@ConditionalOnSingleCandidate | 當給定類型的bean存在并且指定為Primary的給定類型存在時,返回true |
@ConditionalOnMissingBean | 當給定的類型、類名、注解、昵稱在beanFactory中不存在時返回true.各類型間是or的關系 |
@ConditionalOnBean | 與上面相反,要求bean存在 |
@ConditionalOnMissingClass | 當給定的類名在類路徑上不存在時返回true,各類型間是and的關系 |
@ConditionalOnClass | 與上面相反,要求類存在 |
@ConditionalOnCloudPlatform | 當所配置的CloudPlatform為激活時返回true |
@ConditionalOnExpression | spel表達式執行為true |
@ConditionalOnJava | 運行時的java版本號是否包含給定的版本號.如果包含,返回匹配,否則,返回不匹配 |
@ConditionalOnProperty | 要求配置屬性匹配條件 |
@ConditionalOnJndi | 給定的jndi的Location 必須存在一個.否則,返回不匹配 |
@ConditionalOnNotWebApplication | web環境不存在時 |
@ConditionalOnWebApplication | web環境存在時 |
@ConditionalOnResource | 要求制定的資源存在 |
關于SpringBoot中怎么自動裝配Condition的實現方式就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。