实战Spring高级装配中条件化的bean

条件化的 bean,当某个条件触发时创建的 bean。

目录

 

什么是条件化的 bean

条件化地配置bean

ConditionContext 接口

AnnotatedTypeMetadata 接口

@Profile注解

总结


什么是条件化的 bean

比如:

  1. 当应用中包含了特定的库,才创建。
  2. 当某个 非当前 bean 声明后,才创建。
  3. 当某个 特定的环境变量设置后,才创建。

Spring 4 之前很难实现,Spring 4引入了一个新的@Conditional注解。

条件化地配置bean

  @Bean
  @Conditional(MagicExistsCondition.class)
  public MagicBean magicBean() {
    return new MagicBean();
  }

@Conditional 注解就是指定了条件类,就是 MagicExistsCondition。@Conditional将会 通过Condition接口进行条件对比:

Condition 接口是 spring4 源码中的类:

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);

}

可以看出来,这个接口实现起来很简单直接,只需提 供matches()方法的实现即可。如果matches()方法返回true,那么就会创建带有@Conditional注解的bean。如果matches()方法返 回false,将不会创建这些bean。

这里有两个参数,一个是环境条件,一个是注解类型。

在Condition中检查是否存在magic属性

我们需要检查 magic 属性是否存在,就要实现刚才那个接口,Condition,调用 matches 方法来检查:

public class MagicExistsCondition implements Condition {

  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    Environment env = context.getEnvironment();
    return env.containsProperty("magic");
  }
  
}

ConditionContext 可以获取当前的环境,调用 getEnvironment 方法,返回当前应用程序正在运行的{@link环境},如果没有可用的环境,则返回{@code null}。如果有就会返回一个类 Enviroment,其中这个类 containsProperty 可以用来比较 需要的 当前 环境中是否存在 magic 这个环境。

如果条件满足,也就意味着环境符合,所有@Conditional注解上引用MagicExistsCondition的 bean都会被创建。

ConditionContext 接口


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
 * @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.
	 * @return the registry or {@code null}
	 */
	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.
	 * @return the bean factory or {@code null}
	 */
	ConfigurableListableBeanFactory getBeanFactory();

	/**
	 * Return the {@link Environment} for which the current application is running
	 * or {@code null} if no environment is available.
	 * @return the environment or {@code null}
	 */
	Environment getEnvironment();

	/**
	 * Return the {@link ResourceLoader} currently being used or {@code null}
	 * if the resource loader cannot be obtained.
	 * @return a resource loader or {@code null}
	 */
	ResourceLoader getResourceLoader();

	/**
	 * Return the {@link ClassLoader} that should be used to load additional
	 * classes or {@code null} if the default classloader should be used.
	 * @return the class loader or {@code null}
	 */
	ClassLoader getClassLoader();

}

以上接口根据顺序分别含义为:

  1. 借助getRegistry()返回的BeanDefinitionRegistry检查bean定义;
  2. 借助getBeanFactory()返回的ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性;
  3. 借助getEnvironment()返回的Environment检查环境变量是否存在以及它的值是什么;
  4. 读取并探查getResourceLoader()返回的ResourceLoader所加载的资源;
  5. 借助getClassLoader()返回的ClassLoader加载并检查类是否存在。

都不存在的话,返回的是 null。

AnnotatedTypeMetadata 接口

能让我们检查带有@Bean 接口注解的方法上,还有什么其他的注解。

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 type has an annotation or
	 * meta-annotation of the given type defined.
	 * 

If this method returns {@code true}, then * {@link #getAnnotationAttributes} will return a non-null Map. * @param annotationType the annotation type to look for * @return whether a matching annotation is defined */ boolean isAnnotated(String annotationType); /** * Retrieve the attributes of the annotation of the given type, * if any (i.e. if defined on the underlying class, as direct * annotation or as meta-annotation). * @param annotationType 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. */ Map getAnnotationAttributes(String annotationType); /** * Retrieve the attributes of the annotation of the given type, * if any (i.e. if defined on the underlying class, as direct * annotation or as meta-annotation). * @param annotationType 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. */ Map getAnnotationAttributes(String annotationType, boolean classValuesAsString); /** * Retrieve all attributes of all annotations of the given type, if any (i.e. if * defined on the underlying type ({@link AnnotationMetadata class} or * {@link MethodMetadata method}), as direct annotation or as meta-annotation). * @param annotationType 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 getAllAnnotationAttributes(String annotationType); /** * Retrieve all attributes of all annotations of the given type, if any (i.e. if * defined on the underlying type ({@link AnnotationMetadata class} or * {@link MethodMetadata method}), as direct annotation or as meta-annotation). * @param annotationType 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 getAllAnnotationAttributes(String annotationType, boolean classValuesAsString); }

isAnnotated()方法,能够判断带有@Bean注解的方法是不是还有其他特定的注解。

其他方法则是 根据不同参数 返回 不同数据接口的值。

@Profile注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
	String[] value();
}

从 Spring 4 开始,Profile 注解 进行了重构,使其基于@Conditional和Condition实现。

当前接口,也用了 @Conditional 注解,然后他的条件 类为 ProfileCondition,再看一下这个类,同样是继承了 Condition接口。

ProfileCondition检查某个bean profile是否可用

当前条件,基于 Profile 这个类 就行匹配,看下源码:

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 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;
	}

}

我们可以看到,ProfileCondition通过AnnotatedTypeMetadata得到了用于@Profile注解的所有属性。借助该信息,它会明确地检查value属性,该属性包含了bean的profile名称。然后,它根据通过ConditionContext得到的Environment来检查[借助acceptsProfiles()方法]该profile是否处于激活状态。

总结

本篇文章其是就是 解释了 上一篇:https://blog.csdn.net/Soinice/article/details/115556949?spm=1001.2014.3001.5501 @Profile注解的作用。

 

你可能感兴趣的:(spring,java)