javascript
Spring Boot AutoConfiguration注解@ConditionalXXXX之前生今世
1.注解@Conditional的定義
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Conditional {/*** All {@link Condition}s that must {@linkplain Condition#matches match}* in order for the component to be registered.*/Class<? extends Condition>[] value();}注解@Conditional標識一個組件時,該組件只有全面滿足value()指定的所有條件時才可以注冊到容器中。
注解@Conditional的使用場景如下:
. 作為類型級別的注解,作用在一個直接或者間接@Component注解(包括@Configuration作為元注解的類)的類上,目標是組成自定義的steretype注解。
. 作為方法級別的注解,作用在任意的@Bean 方法上
如果一個標注了@Configuration的類,也標注了@Conditional,所有的@Bean方法,@Import和@ComponentScan注解關聯的類將也滿足這些Conditions。
注意,@Conditional注解不能繼承,從父類或者重寫方法的condition是不起作用的。
其中,一個Condition是要注冊的Bean定義之前可以編程決定的狀態。詳細信息如下:
2 前生 Condition定義
public interface Condition {/*** Determine if the condition matches.* @param context the condition context* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}* or {@link org.springframework.core.type.MethodMetadata method} being checked.* @return {@code true} if the condition matches and the component can be registered* or {@code false} to veto registration.*/boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);}一個單獨的condition是一個組件為注冊為bean時必須滿足matches()方法。
Conditions在要注冊的組件變成bean definition之前必須檢查立即檢查所有的matches方法。
Condition也必須和BeanFactoryPostProcessor一樣滿足同樣的限制條件。更細粒度的控制可以考慮使用ConfigurationCondition。
3.后世
spring-boot-autoconfigure condition相關的類如下:
3.1?ConditionalOnBean定義
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnBeanCondition.class) public @interface ConditionalOnBean {/*** The class type of bean that should be checked. The condition matches when all of* the classes specified are contained in the {@link ApplicationContext}.* @return the class types of beans to check*/Class<?>[] value() default {};/*** The class type names of bean that should be checked. The condition matches when all* of the classes specified are contained in the {@link ApplicationContext}.* @return the class type names of beans to check*/String[] type() default {};/*** The annotation type decorating a bean that should be checked. The condition matches* when all of the annotations specified are defined on beans in the* {@link ApplicationContext}.* @return the class-level annotation types to check*/Class<? extends Annotation>[] annotation() default {};/*** The names of beans to check. The condition matches when all of the bean names* specified are contained in the {@link ApplicationContext}.* @return the name of beans to check*/String[] name() default {};/*** Strategy to decide if the application context hierarchy (parent contexts) should be* considered.* @return the search strategy*/SearchStrategy search() default SearchStrategy.ALL;}ConditionalOnBean作用:當指定bean的類名或者名稱已經在BeanFactory中存在時才算滿足條件。
其實現類為OnBeanCondition,檢查指定的bean是存在還是不存在。
@Order(Ordered.LOWEST_PRECEDENCE) class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition {/*** Bean definition attribute name for factory beans to signal their product type (if* known and it can't be deduced from the factory bean class).*/public static final String FACTORY_BEAN_OBJECT_TYPE = BeanTypeRegistry.FACTORY_BEAN_OBJECT_TYPE;@Overridepublic ConfigurationPhase getConfigurationPhase() {return ConfigurationPhase.REGISTER_BEAN;}@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context,AnnotatedTypeMetadata metadata) {ConditionMessage matchMessage = ConditionMessage.empty();if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {BeanSearchSpec spec = new BeanSearchSpec(context, metadata,ConditionalOnBean.class);MatchResult matchResult = getMatchingBeans(context, spec);if (!matchResult.isAllMatched()) {String reason = createOnBeanNoMatchReason(matchResult);return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnBean.class, spec).because(reason));}matchMessage = matchMessage.andCondition(ConditionalOnBean.class, spec).found("bean", "beans").items(Style.QUOTE, matchResult.getNamesOfAllMatches());}if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {BeanSearchSpec spec = new SingleCandidateBeanSearchSpec(context, metadata,ConditionalOnSingleCandidate.class);MatchResult matchResult = getMatchingBeans(context, spec);if (!matchResult.isAllMatched()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, spec).didNotFind("any beans").atAll());}else if (!hasSingleAutowireCandidate(context.getBeanFactory(),matchResult.getNamesOfAllMatches(),spec.getStrategy() == SearchStrategy.ALL)) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, spec).didNotFind("a primary bean from beans").items(Style.QUOTE, matchResult.getNamesOfAllMatches()));}matchMessage = matchMessage.andCondition(ConditionalOnSingleCandidate.class, spec).found("a primary bean from beans").items(Style.QUOTE, matchResult.namesOfAllMatches);}if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {BeanSearchSpec spec = new BeanSearchSpec(context, metadata,ConditionalOnMissingBean.class);MatchResult matchResult = getMatchingBeans(context, spec);if (matchResult.isAnyMatched()) {String reason = createOnMissingBeanNoMatchReason(matchResult);return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingBean.class, spec).because(reason));}matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, spec).didNotFind("any beans").atAll();}return ConditionOutcome.match(matchMessage);} }3.2?ConditionalOnClass
當指定的類在classpath下認定滿足條件,實現類為:OnClassCondition。
定義如下:
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnClassCondition.class) public @interface ConditionalOnClass {/*** The classes that must be present. Since this annotation parsed by loading class* bytecode it is safe to specify classes here that may ultimately not be on the* classpath.* @return the classes that must be present*/Class<?>[] value() default {};/*** The classes names that must be present.* @return the class names that must be present.*/String[] name() default {};}3.3?ConditionalOnCloudPlatform
指定的云平臺激活時滿足條件。實現類為:OnCloudPlatformCondition
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnCloudPlatformCondition.class) public @interface ConditionalOnCloudPlatform {/*** The {@link CloudPlatform cloud platform} that must be active.* @return the expected cloud platform*/CloudPlatform value();}3.4?ConditionalOnExpression
/*** Configuration annotation for a conditional element that depends on the value of a SpEL* expression.** @author Dave Syer*/ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnExpressionCondition.class) public @interface ConditionalOnExpression {/*** The SpEL expression to evaluate. Expression should return {@code true} if the* condition passes or {@code false} if it fails.* @return the SpEL expression*/String value() default "true";}3.5?ConditionalOnJava
/*** {@link Conditional} that matches based on the JVM version the application is running* on.** @author Oliver Gierke* @author Phillip Webb* @author Andy Wilkinson* @since 1.1.0*/ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnJavaCondition.class) public @interface ConditionalOnJava {/*** Configures whether the value configured in {@link #value()} shall be considered the* upper exclusive or lower inclusive boundary. Defaults to* {@link Range#EQUAL_OR_NEWER}.* @return the range*/Range range() default Range.EQUAL_OR_NEWER;/*** The {@link JavaVersion} to check for. Use {@link #range()} to specify whether the* configured value is an upper-exclusive or lower-inclusive boundary.* @return the java version*/JavaVersion value();/*** Range options.*/enum Range {/*** Equal to, or newer than the specified {@link JavaVersion}.*/EQUAL_OR_NEWER,/*** Older than the specified {@link JavaVersion}.*/OLDER_THAN}/*** Java versions.*/enum JavaVersion {/*** Java 1.9.*/NINE(9, "1.9", "java.security.cert.URICertStoreParameters"),/*** Java 1.8.*/EIGHT(8, "1.8", "java.util.function.Function");private final int value;private final String name;private final boolean available;JavaVersion(int value, String name, String className) {this.value = value;this.name = name;this.available = ClassUtils.isPresent(className, getClass().getClassLoader());}/*** Determines if this version is within the specified range of versions.* @param range the range* @param version the bounds of the range* @return if this version is within the specified range*/public boolean isWithin(Range range, JavaVersion version) {Assert.notNull(range, "Range must not be null");Assert.notNull(version, "Version must not be null");switch (range) {case EQUAL_OR_NEWER:return this.value >= version.value;case OLDER_THAN:return this.value < version.value;}throw new IllegalStateException("Unknown range " + range);}@Overridepublic String toString() {return this.name;}/*** Returns the {@link JavaVersion} of the current runtime.* @return the {@link JavaVersion}*/public static JavaVersion getJavaVersion() {for (JavaVersion candidate : JavaVersion.values()) {if (candidate.available) {return candidate;}}return EIGHT;}}}3.6?ConditionalOnJndi
/*** {@link Conditional} that matches based on the availability of a JNDI* {@link InitialContext} and the ability to lookup specific locations.** @author Phillip Webb* @since 1.2.0*/ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnJndiCondition.class) public @interface ConditionalOnJndi {/*** JNDI Locations, one of which must exist. If no locations are specific the condition* matches solely based on the presence of an {@link InitialContext}.* @return the JNDI locations*/String[] value() default {};}3.7?ConditionalOnMissingBean
/*** {@link Conditional} that only matches when the specified bean classes and/or names are* not already contained in the {@link BeanFactory}.* <p>* The condition can only match the bean definitions that have been processed by the* application context so far and, as such, it is strongly recommended to use this* condition on auto-configuration classes only. If a candidate bean may be created by* another auto-configuration, make sure that the one using this condition runs after.** @author Phillip Webb* @author Andy Wilkinson*/ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnBeanCondition.class) public @interface ConditionalOnMissingBean {/*** The class type of bean that should be checked. The condition matches when each* class specified is missing in the {@link ApplicationContext}.* @return the class types of beans to check*/Class<?>[] value() default {};/*** The class type names of bean that should be checked. The condition matches when* each class specified is missing in the {@link ApplicationContext}.* @return the class type names of beans to check*/String[] type() default {};/*** The class type of beans that should be ignored when identifying matching beans.* @return the class types of beans to ignore* @since 1.2.5*/Class<?>[] ignored() default {};/*** The class type names of beans that should be ignored when identifying matching* beans.* @return the class type names of beans to ignore* @since 1.2.5*/String[] ignoredType() default {};/*** The annotation type decorating a bean that should be checked. The condition matches* when each annotation specified is missing from all beans in the* {@link ApplicationContext}.* @return the class-level annotation types to check*/Class<? extends Annotation>[] annotation() default {};/*** The names of beans to check. The condition matches when each bean name specified is* missing in the {@link ApplicationContext}.* @return the name of beans to check*/String[] name() default {};/*** Strategy to decide if the application context hierarchy (parent contexts) should be* considered.* @return the search strategy*/SearchStrategy search() default SearchStrategy.ALL;}3.8?ConditionalOnMissingClass
/*** {@link Conditional} that only matches when the specified classes are not on the* classpath.** @author Dave Syer*/ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnClassCondition.class) public @interface ConditionalOnMissingClass {/*** The names of the classes that must not be present.* @return the names of the classes that must not be present*/String[] value() default {};}3.9?ConditionalOnNotWebApplication
/*** {@link Conditional} that only matches when the application context is a not a web* application context.** @author Dave Syer*/ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnWebApplicationCondition.class) public @interface ConditionalOnNotWebApplication {}3.10?ConditionalOnProperty
/*** {@link Conditional} that checks if the specified properties have a specific value. By* default the properties must be present in the {@link Environment} and* <strong>not</strong> equal to {@code false}. The {@link #havingValue()} and* {@link #matchIfMissing()} attributes allow further customizations.** <p>* The {@link #havingValue} attribute can be used to specify the value that the property* should have. The table below shows when a condition matches according to the property* value and the {@link #havingValue()} attribute:** <table summary="having values" border="1">* <tr>* <th>Property Value</th>* <th>{@code havingValue=""}</th>* <th>{@code havingValue="true"}</th>* <th>{@code havingValue="false"}</th>* <th>{@code havingValue="foo"}</th>* </tr>* <tr>* <td>{@code "true"}</td>* <td>yes</td>* <td>yes</td>* <td>no</td>* <td>no</td>* </tr>* <tr>* <td>{@code "false"}</td>* <td>no</td>* <td>no</td>* <td>yes</td>* <td>no</td>* </tr>* <tr>* <td>{@code "foo"}</td>* <td>yes</td>* <td>no</td>* <td>no</td>* <td>yes</td>* </tr>* </table>** <p>* If the property is not contained in the {@link Environment} at all, the* {@link #matchIfMissing()} attribute is consulted. By default missing attributes do not* match.** @author Maciej Walkowiak* @author Stephane Nicoll* @author Phillip Webb* @since 1.1.0*/ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty {/*** Alias for {@link #name()}.* @return the names*/String[] value() default {};/*** A prefix that should be applied to each property. The prefix automatically ends* with a dot if not specified.* @return the prefix*/String prefix() default "";/*** The name of the properties to test. If a prefix has been defined, it is applied to* compute the full key of each property. For instance if the prefix is* {@code app.config} and one value is {@code my-value}, the fully key would be* {@code app.config.my-value}* <p>* Use the dashed notation to specify each property, that is all lower case with a "-"* to separate words (e.g. {@code my-long-property}).* @return the names*/String[] name() default {};/*** The string representation of the expected value for the properties. If not* specified, the property must <strong>not</strong> be equals to {@code false}.* @return the expected value*/String havingValue() default "";/*** Specify if the condition should match if the property is not set. Defaults to* {@code false}.* @return if should match if the property is missing*/boolean matchIfMissing() default false;/*** If relaxed names should be checked. Defaults to {@code true}.* @return if relaxed names are used*/boolean relaxedNames() default true;}3.11?ConditionalOnResource
/*** {@link Conditional} that only matches when the specified resources are on the* classpath.** @author Dave Syer*/ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnResourceCondition.class) public @interface ConditionalOnResource {/*** The resources that must be present.* @return the resource paths that must be present.*/String[] resources() default {};}3.12?ConditionalOnSingleCandidate
/*** {@link Conditional} that only matches when the specified bean class is already* contained in the {@link BeanFactory} and a single candidate can be determined.* <p>* The condition will also match if multiple matching bean instances are already contained* in the {@link BeanFactory} but a primary candidate has been defined; essentially, the* condition match if auto-wiring a bean with the defined type will succeed.* <p>* The condition can only match the bean definitions that have been processed by the* application context so far and, as such, it is strongly recommended to use this* condition on auto-configuration classes only. If a candidate bean may be created by* another auto-configuration, make sure that the one using this condition runs after.** @author Stephane Nicoll* @since 1.3.0*/ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnBeanCondition.class) public @interface ConditionalOnSingleCandidate {/*** The class type of bean that should be checked. The condition match if the class* specified is contained in the {@link ApplicationContext} and a primary candidate* exists in case of multiple instances.* <p>* This attribute may <strong>not</strong> be used in conjunction with {@link #type()}* , but it may be used instead of {@link #type()}.* @return the class type of the bean to check*/Class<?> value() default Object.class;/*** The class type name of bean that should be checked. The condition matches if the* class specified is contained in the {@link ApplicationContext} and a primary* candidate exists in case of multiple instances.* <p>* This attribute may <strong>not</strong> be used in conjunction with* {@link #value()}, but it may be used instead of {@link #value()}.* @return the class type name of the bean to check*/String type() default "";/*** Strategy to decide if the application context hierarchy (parent contexts) should be* considered.* @return the search strategy*/SearchStrategy search() default SearchStrategy.ALL;}3.13?ConditionalOnWebApplication
/*** {@link Conditional} that matches when the application is a web application. By default,* any web application will match but it can be narrowed using the {@link #type()}* attribute.** @author Dave Syer* @author Stephane Nicoll*/ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnWebApplicationCondition.class) public @interface ConditionalOnWebApplication {/*** The required type of the web application.* @return the required web application type*/Type type() default Type.ANY;/*** Available application types.*/enum Type {/*** Any web application will match.*/ANY,/*** Only servlet-based web application will match.*/SERVLET,/*** Only reactive-based web application will match.*/REACTIVE}}?
轉載于:https://www.cnblogs.com/davidwang456/p/6604112.html
總結
以上是生活随笔為你收集整理的Spring Boot AutoConfiguration注解@ConditionalXXXX之前生今世的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring boot自动配置之jdbc
- 下一篇: MyBatis的架构设计以及实例分析--