@EnableAspectJAutoProxy註解的原理(SpringAOP的原理)

spring使用aspectj提供的庫進行切入點解析和匹配,解釋與aspectj 5相同的註釋。但是,AOP運行時仍然是純SpringAOP,並且不依賴於AspectJ編譯器或weaver。

springAOP不管是用jdk動態代理還是cglib動態代理,跟aspectj的最大區別是SpringAOp用的是動態代理運行期織入,而aspectj是使用的靜態代理在編譯期織入。

先看@EnableAspectJAutoProxy註解,通過Import引入AspectJAutoProxyRegistrar,AspectJAutoProxyRegistrar實現了ImportBeanDefinitionRegistrar接口。ImportBeanDefinitionRegistrar接口的作用就是把BeanDefinitionRegistry傳遞給我們,我們可以通過這個註冊器完成beanDefinition的註冊。

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

	
	boolean proxyTargetClass() default false;

	
	boolean exposeProxy() default false;

}

接着再看AspectJAutoProxyRegistrar都做了些什麼

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

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

		//註冊AnnotationAwareAspectJAutoProxyCreator
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		//判斷配置類AppConfig是否加了@EnableAspectJAutoProxy註解
		if (enableAspectJAutoProxy != null) {
			//默認是false指用jdk動態代理,值爲true則開啓cglib代理
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

}

首先註冊了一個AnnotationAwareAspectJAutoProxyCreator,先看這個類的繼承結構圖

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
		return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
	}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}
private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}

		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}

 

 

很關鍵的一點就是,它實現了BeanPostProcessor接口,那麼就能猜測出,大致就是在bean實例化完成後,put到singletomObjects(狹義上的spring容器,一個currentHashMap,存儲beanName和bean對象)之前做一些判斷,然後將代理對象或者實際對象放入singletonObject中。

接着再看AnnotationAwareAspectJAutoProxyCreator中BeanPostProcessor接口的實現方法,發現沒有。在AbstractAutoProxyCreator中找到了

@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
		Object cacheKey = getCacheKey(beanClass, beanName);

		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}

		// Create proxy here if we have a custom TargetSource.
		// Suppresses unnecessary default instantiation of the target bean:
		// The TargetSource will handle target instances in a custom fashion.
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if (targetSource != null) {
			if (StringUtils.hasLength(beanName)) {
				this.targetSourcedBeans.add(beanName);
			}
			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		return null;
	}
@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

在fater方法中調用wrapIfNecessary,這個方法會判斷滿足切點表達式的方法上的通知是否不爲空,不爲空就返回代理對象,反之返回實際對象

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

可以測試一下,現在把通知註釋掉

@Configuration
@ComponentScan("com.spring")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {

}
@Aspect
@Component
public class TestAspectj {

	@Pointcut("execution(* com.spring.dao.*.*(..))")
	public void point(){
		//該方法就是一個標識方法,爲pointcut提供一個依附的地方
	}

//	@Before("point()")
//	public void before(){
//		System.out.println("Before");
//	}
//	@After("point()")
//	public void after(){
//		System.out.println("After");
//	}
}
public class Test {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.register(AppConfig.class);
		context.refresh();
		IndexDao indexDao = context.getBean(IndexDao.class);
		indexDao.query();
	}
}

沒有通知的時候,返回的是實際IndexDao類型

再放開通知測試看,這次不出意外的返回了cglib類型的代理對象

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章