Spring中@Import的各种用法以及ImportAware接口

@Import
注解提供了和XML<import/>
元素等价的功能,实现导入的一个或多个配置类。 @Import
即可以在类上使用,也可以作为元注解使用。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/**
	 * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
	 * or regular component classes to import.
	 */
	Class<?>[] value();

}
复制代码

注解中只有一个 value();
。支持导入 @Configuration
标注的配置类,实现 ImportSelector
接口的类、实现 ImportBeanDefinitionRegistrar
接口的类和普通的 @component
类。

作为元注解使用

@Import
可以作为元注解使用,可以在 @Import
的继承上封装一层。我的理解是,这样做不会对外(使用方)暴露我内部的具体实现细节。

举个例子:例如 @EnableAspectJAutoProxy
注解。

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
复制代码

@EnableAspectJAutoProxy
就是被 @Import
这个元注解所标志了,我们(程序员)通过使用 @EnableAspectJAutoProxy
来开启AspectJAutoProxy,而Spring底层是通过 @Import
导入相应的配置类来实现的。

导入实现ImportSelector接口的类

先来看一下 ImportSelector
接口,该接口中只有一个方法:

public interface ImportSelector {
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}
复制代码

ImportSelector,输入选择器。该接口就是用来根据给定的条件,选择导入哪些配置类。

举个例子:例如 @EnableTransactionManagement
注解。

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
复制代码

@EnableTransactionManagement
注解中使用了 @Import(TransactionManagementConfigurationSelector.class)
注解,其中 TransactionManagementConfigurationSelector
类就是实现了 ImportSelector
接口。

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {
						TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}
}
复制代码

方法的内部实现逻辑也很简单,就是根据不同的 AdviceMode
导入不同的配置类,来实现事务管理

导入实现ImportBeanDefinitionRegistrar接口的类

ImportBeanDefinitionRegistrar
接口中也只有一个方法:

public interface ImportBeanDefinitionRegistrar {
	void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
复制代码

该接口允许我们根据所给的注解元数据,按需注册额外的 BeanDefinition

举个例子:例如 @EnableAspectJAutoProxy
注解。

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
复制代码

@EnableAspectJAutoProxy
注解引入了 AspectJAutoProxyRegistrar.class
类,这个类就是实现了 ImportBeanDefinitionRegistrar
接口。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}
}
复制代码

registerBeanDefinitions
中调用了 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
方法,这个方法就是在往传入的 BeanDefinitionRegistry registry
中注册 BeanDefinition
。注册了 BeanDefinition
之后,Spring就会去实例化这个Bean,从而达到AspectJAutoProxy作用。

导入@Configuration类

这次 @Import
最常见是使用方法。我们可以拆分配置类,然后在程序中按需导入相应的配置。

举个例子:例如 @EnableRetry
注解。
使用这个注解可以开启retry功能。

@EnableAspectJAutoProxy(proxyTargetClass = false)
@Import(RetryConfiguration.class)
public @interface EnableRetry {
复制代码

其内部就是导入了 RetryConfiguration
这个配置类。

ImportAware接口

ImportAware
接口是需要和 @Import
一起使用的。在 @Import
作为元注解使用时,通过 @Import
导入的配置类如果实现了 ImportAware
接口就可以获取到导入该配置类接口的数据配置。有点绕,我们直接上代码。

举个例子: @EnableAsync
注解。

@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
复制代码
//AsyncConfigurationSelector源码
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
	@Override
	@Nullable
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {ProxyAsyncConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}
}
复制代码

默认情况下使用 AdviceMode
PROXY
,导入了 ProxyAsyncConfiguration
类。

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

	@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
		Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
		AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
		Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
		if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
			bpp.setAsyncAnnotationType(customAsyncAnnotation);
		}
		if (this.executor != null) {
			bpp.setExecutor(this.executor);
		}
		if (this.exceptionHandler != null) {
			bpp.setExceptionHandler(this.exceptionHandler);
		}
		bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
		bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
		return bpp;
	}
}
复制代码

ProxyAsyncConfiguration
asyncAdvisor
方法中需要获取到 @EnableAsync
上的一些设置值,例如: this.enableAsync.getBoolean("proxyTargetClass")
, this.enableAsync.<Integer>getNumber("order")

this.enableAsync
是其父类 AbstractAsyncConfiguration
的属性。 AbstractAsyncConfiguration
实现了 ImportAware
接口,从而就可以获取到 @EnableAsync
上的信息了。

// AbstractAsyncConfiguration#setImportMetadata 源码
public void setImportMetadata(AnnotationMetadata importMetadata) {
	this.enableAsync = AnnotationAttributes.fromMap(
			importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
	if (this.enableAsync == null) {
		throw new IllegalArgumentException(
				"@EnableAsync is not present on importing class " + importMetadata.getClassName());
	}
}
复制代码

可能这个例子有点复杂的,还有一个稍微简单一点的例子: EnableRedisHttpSession
。关于这个,读者可以自己去看一下源码debug学习一下。

原文 

https://juejin.im/post/5d6a580be51d453b5c121905

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Spring中@Import的各种用法以及ImportAware接口

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址