条件化bean
Posted 大梦几千秋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了条件化bean相关的知识,希望对你有一定的参考价值。
天行健,君子以自强不息。——《周易》
应用场景
假设你希望一个或多个bean只有在应用的类路径下包含特定的库时才创建,希望某个bean只有当某个特定的bean也声明了之后才会创建,或者只有某个特定的环境变量设置之后才会创建bean。Spring 4 引入了@Conditional注解,它可以用到带有@Bean注解的方法上,如果给定的条件满足才会创建bean,反之则会被忽略。
条件化配置bean的代码如下:
package chapter3; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class MagicConfig { @Bean @Conditional(MagicExistsCondition.class) public MagicBean magicBean() { return new MagicBean(); } }
设置给@Conditional的类可以使任意实现了Condition接口的类,如下MagicExistsCondition.java:
package chapter3; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; public class MagicExistsCondition implements Condition { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); return environment.containsProperty("magic"); } }
ConditionContext是一个接口,通过该接口我们可以做到以下几点:
* Copyright 2002-2017 the original author or authors. package org.springframework.context.annotation; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; /** * Context information for use by {@link Condition}s. * * @author Phillip Webb * @author Juergen Hoeller * @since 4.0 */ public interface ConditionContext { /** * Return the {@link BeanDefinitionRegistry} that will hold the bean definition * should the condition match, or {@code null} if the registry is not available.
* 借助返回的BeanDefinitionRegistry检查bean的定义 */ BeanDefinitionRegistry getRegistry(); /** * Return the {@link ConfigurableListableBeanFactory} that will hold the bean * definition should the condition match, or {@code null} if the bean factory * is not available.
* 检查bean是否存在,甚至探查bean的属性 */ ConfigurableListableBeanFactory getBeanFactory(); /** * Return the {@link Environment} for which the current application is running, * or {@code null} if no environment is available.
* 检查环境变量是否存在以及它的值是什么 */ Environment getEnvironment(); /** * Return the {@link ResourceLoader} currently being used, or {@code null} if * the resource loader cannot be obtained.
* 读取并探查返回的ResourceLoader所加载的资源 */ ResourceLoader getResourceLoader(); /** * Return the {@link ClassLoader} that should be used to load additional classes, * or {@code null} if the default classloader should be used.
* 借助返回的ClassLoader加载并检查类是否存在 */ ClassLoader getClassLoader(); }
AnnotatedTypeMetadata也是一个接口,通过该接口我们可以:
* Copyright 2002-2015 the original author or authors. package org.springframework.core.type; import java.util.Map; import org.springframework.util.MultiValueMap; /** * Defines access to the annotations of a specific type ({@link AnnotationMetadata class} * or {@link MethodMetadata method}), in a form that does not necessarily require the * class-loading. * * @author Juergen Hoeller * @author Mark Fisher * @author Mark Pollack * @author Chris Beams * @author Phillip Webb * @author Sam Brannen * @since 4.0 * @see AnnotationMetadata * @see MethodMetadata */ public interface AnnotatedTypeMetadata { /** * Determine whether the underlying element has an annotation or meta-annotation * of the given type defined. * <p>If this method returns {@code true}, then * {@link #getAnnotationAttributes} will return a non-null Map. * @param annotationName the fully qualified class name of the annotation * type to look for * @return whether a matching annotation is defined
* 判断带有@Bean注解的方法是否还有其他特定的注解 */ boolean isAnnotated(String annotationName); /** * Retrieve the attributes of the annotation of the given type, if any (i.e. if * defined on the underlying element, as direct annotation or meta-annotation), * also taking attribute overrides on composed annotations into account. * @param annotationName the fully qualified class name of the annotation * type to look for * @return a Map of attributes, with the attribute name as key (e.g. "value") * and the defined attribute value as Map value. This return value will be * {@code null} if no matching annotation is defined.
* 检查@Bean注解的方法上其他注解的属性 */ Map<String, Object> getAnnotationAttributes(String annotationName); /** * Retrieve the attributes of the annotation of the given type, if any (i.e. if * defined on the underlying element, as direct annotation or meta-annotation), * also taking attribute overrides on composed annotations into account. * @param annotationName the fully qualified class name of the annotation * type to look for * @param classValuesAsString whether to convert class references to String * class names for exposure as values in the returned Map, instead of Class * references which might potentially have to be loaded first * @return a Map of attributes, with the attribute name as key (e.g. "value") * and the defined attribute value as Map value. This return value will be * {@code null} if no matching annotation is defined.
* 检查@Bean注解的方法上其他注解的属性 */ Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString); /** * Retrieve all attributes of all annotations of the given type, if any (i.e. if * defined on the underlying element, as direct annotation or meta-annotation). * Note that this variant does <i>not</i> take attribute overrides into account. * @param annotationName the fully qualified class name of the annotation * type to look for * @return a MultiMap of attributes, with the attribute name as key (e.g. "value") * and a list of the defined attribute values as Map value. This return value will * be {@code null} if no matching annotation is defined. * @see #getAllAnnotationAttributes(String, boolean) */ MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName); /** * Retrieve all attributes of all annotations of the given type, if any (i.e. if * defined on the underlying element, as direct annotation or meta-annotation). * Note that this variant does <i>not</i> take attribute overrides into account. * @param annotationName the fully qualified class name of the annotation * type to look for * @param classValuesAsString whether to convert class references to String * @return a MultiMap of attributes, with the attribute name as key (e.g. "value") * and a list of the defined attribute values as Map value. This return value will * be {@code null} if no matching annotation is defined. * @see #getAllAnnotationAttributes(String) */ MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString); }
Spring4中,@Profile注解实现代码如下:
/* * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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; import org.springframework.core.env.AbstractEnvironment; import org.springframework.core.env.ConfigurableEnvironment; /** * Indicates that a component is eligible for registration when one or more * {@linkplain #value specified profiles} are active. * * <p>A <em>profile</em> is a named logical grouping that may be activated * programmatically via {@link ConfigurableEnvironment#setActiveProfiles} or declaratively * by setting the {@link AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME * spring.profiles.active} property as a JVM system property, as an * environment variable, or as a Servlet context parameter in {@code web.xml} * for web applications. Profiles may also be activated declaratively in * integration tests via the {@code @ActiveProfiles} annotation. * * <p>The {@code @Profile} annotation may be used in any of the following ways: * <ul> * <li>as a type-level annotation on any class directly or indirectly annotated with * {@code @Component}, including {@link Configuration @Configuration} classes</li> * <li>as a meta-annotation, for the purpose of composing custom stereotype annotations</li> * <li>as a method-level annotation on any {@link Bean @Bean} method</li> * </ul> * * <p>If a {@code @Configuration} class is marked with {@code @Profile}, all of the * {@code @Bean} methods and {@link Import @Import} annotations associated with that class * will be bypassed unless one or more of the specified profiles are active. This is * analogous to the behavior in Spring XML: if the {@code profile} attribute of the * {@code beans} element is supplied e.g., {@code <beans profile="p1,p2">}, the * {@code beans} element will not be parsed unless at least profile ‘p1‘ or ‘p2‘ has been * activated. Likewise, if a {@code @Component} or {@code @Configuration} class is marked * with {@code @Profile({"p1", "p2"})}, that class will not be registered or processed unless * at least profile ‘p1‘ or ‘p2‘ has been activated. * * <p>If a given profile is prefixed with the NOT operator ({@code !}), the annotated * component will be registered if the profile is <em>not</em> active — for example, * given {@code @Profile({"p1", "!p2"})}, registration will occur if profile ‘p1‘ is active * or if profile ‘p2‘ is <em>not</em> active. * * <p>If the {@code @Profile} annotation is omitted, registration will occur regardless * of which (if any) profiles are active. * * <p><b>NOTE:</b> With {@code @Profile} on {@code @Bean} methods, a special scenario may * apply: In the case of overloaded {@code @Bean} methods of the same Java method name * (analogous to constructor overloading), an {@code @Profile} condition needs to be * consistently declared on all overloaded methods. If the conditions are inconsistent, * only the condition on the first declaration among the overloaded methods will matter. * {@code @Profile} can therefore not be used to select an overloaded method with a * particular argument signature over another; resolution between all factory methods * for the same bean follows Spring‘s constructor resolution algorithm at creation time. * <b>Use distinct Java method names pointing to the same {@link Bean#name bean name} * if you‘d like to define alternative beans with different profile conditions</b>; * see {@code ProfileDatabaseConfig} in {@link Configuration @Configuration}‘s javadoc. * * <p>When defining Spring beans via XML, the {@code "profile"} attribute of the * {@code <beans>} element may be used. See the documentation in the * {@code spring-beans} XSD (version 3.1 or greater) for details. * * @author Chris Beams * @author Phillip Webb * @author Sam Brannen * @since 3.1 * @see ConfigurableEnvironment#setActiveProfiles * @see ConfigurableEnvironment#setDefaultProfiles * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME * @see Conditional * @see org.springframework.test.context.ActiveProfiles */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(ProfileCondition.class) public @interface Profile { /** * The set of profiles for which the annotated component should be registered. */ String[] value(); }
从上面@Profile注解的实现代码可发现@Profile也使用了@Conditional注解,并且引用ProfileCondition作为Condition实现,其中ProfileCondition实现如下:
/* * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.context.annotation; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.MultiValueMap; /** * {@link Condition} that matches based on the value of a {@link Profile @Profile} * annotation. * * @author Chris Beams * @author Phillip Webb * @author Juergen Hoeller * @since 4.0 */ class ProfileCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { if (context.getEnvironment() != null) { MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { for (Object value : attrs.get("value")) { if (context.getEnvironment().acceptsProfiles(((String[]) value))) { return true; } } return false; } } return true; } }
以上是关于条件化bean的主要内容,如果未能解决你的问题,请参考以下文章