1. 程式人生 > >Spring 之 Enable* 註解的工作原理

Spring 之 Enable* 註解的工作原理

    通過簡單的@Enable* 來開啟一項功能的支援,從而避免自己配置大量的程式碼降低使用的難度. 通過觀察@Enable*註解的原始碼,發現所有的註解都有一個@Import註解,它是用來匯入配置類的,這也就意味著這些自動開啟的實現其實是匯入了一些自動配置的 bean, 這些匯入的配置方式主要分為以下三種類型.

第一類:直接匯入配置類

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

}

直接匯入 SchedulingConfiguration,這個類註解了 @Configuration,且註冊了一個 scheduledAnnotationProcessor 的 bean, 原始碼如下:

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {

	@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
		return new ScheduledAnnotationBeanPostProcessor();
	}

}

第二類:依據條件選擇配置類

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

	Class<? extends Annotation> annotation() default Annotation.class;

	boolean proxyTargetClass() default false;

	AdviceMode mode() default AdviceMode.PROXY;

	int order() default Ordered.LOWEST_PRECEDENCE;

}

AsyncConfigurationSelector 通過條件來選擇需要匯入的配置類, AsyncConfigurationSelector的根介面為 ImportSelector,這個介面需要重寫 selectImports 方法,在此方法內進行條件判斷.此例中,若 adviceMode 為 PROXY, 則返回 ProxyAsyncConfiguration,若 adviceMode 為ASPECTJ , 則返回 AspectJAsyncConfiguration.原始碼如下:

package org.springframework.scheduling.annotation;

import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.lang.Nullable;

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

}

第三類:動態註冊bean

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

	boolean proxyTargetClass() default false;

	boolean exposeProxy() default false;

}

AspectJAutoProxyRegistrar 實現了 ImportBeanDefinitionRegistrar 介面,ImportBeanDefinitionRegistrar 的作用是在執行時自動新增 bean 到已有的配置類,通過重寫方法:

public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

其中, AnnotationMetadata 引數用來獲得當前配置類上的註解;BeanDefinitionRegistry 引數用來註冊 bean; 原始碼如下:

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

}