专栏首页冰枫一、Spring AOP源码解析
原创

一、Spring AOP源码解析

在Spring中可以通过xml方式,或者注解方式来实现Aop,如果通过注解方式则需要在xml中配置

<aop:aspectj-autoproxy/>

这篇文章详细讲解一下注解方式是如何来实现Aop的。

一、注册beanDefinitionParser

AopNamespaceHandler是aop命名空间的处理类

它注册了解析config、aspectj-autoproxy、scoped-proxy标签的BeanDefinitionParser,spring-configured标签从2.1开始就被移动了context命名空间中。

	@Override
	public void init() {
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}

我们直接来看AspectJAutoProxyBeanDefinitionParser的代码:

二、aspectj-autoproxy解析入口

解析分成两项工作

1.注册AspectJAnnotationAutoProxyCreator

2.获取子标签,注册到beanDefinition

@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		extendBeanDefinition(element, parserContext);
		return null;
	}

三、注册AspectJAnnotationAutoProxyCreator

1.如果要代理的类实现了接口,则默认使用jdk动态代理否则将使用cglib进行代理

2.如果配置了proxy-target-class为true,将强制使用cglib进行代理

	public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {
	  // 注册registerAspectJAnnotationAutoProxyCreator
		BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
				// 处理xml中配置的aspectj-autoproxy中的proxy-target-class 和 expose-proxy属性
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		// 注册组件
		registerComponentIfNecessary(beanDefinition, parserContext);
	}

顾名思义注册或者升级AspectJAnnotationAutoProxyCreator,如果有优先级更高的AspectJAnnotationAutoProxyCreator,那么升级为优先级更高的AspectJAnnotationAutoProxyCreator,这里不进行详细解释

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}

完成了上述工作,AnnotationAwareAspectJAutoProxyCreator就已经在IOC容器中注册完毕了。

四、AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator它层级实现了InstantiationAwareBeanPostProcessor

,学习过Spring IOC容器源码的童鞋应该知道它的postProcessor方法将会在IOC容器中bean的实例化,初始化等等时候被调用。aop的核心也就是在此。

在AbstractAutoProxyCreator类中对这些方法进行了重写:

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		// 在className和beanName中用"_"连接 例:cn.blingfeng.Test_test
		Object cacheKey = getCacheKey(beanClass, beanName);
		// 如果beanName为空或者bean还未被处理过
		if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
			// 如果不需要代理
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			// 如果为Advisor Advice AopInfrastructureBean 类的实现类 或者 为pointCut则不需要处理
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}
		
		if (beanName != null) {
			// 获取定制的targetSource,可以通过子类重写此方法,来实现不同的机制
			TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
			if (targetSource != null) {
				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 boolean postProcessAfterInstantiation(Object bean, String beanName) {
		return true;
	}

	@Override
	public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {

		return pvs;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (!this.earlyProxyReferences.contains(cacheKey)) {
			 // bena进行包装
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 如果bean还未被处理
		if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		// bean不需要增强
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		// 如果为Advisor Advice AopInfrastructureBean 类的实现类 或者 为pointCut则不需要处理
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// 获取增强
		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;
	}

isInfrastructureClass()方法具体实现,如果为Advice或Advisor或AopInfrastrutureBean的实现则返回true

	protected boolean isInfrastructureClass(Class<?> beanClass) {
		boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
				Advisor.class.isAssignableFrom(beanClass) ||
				AopInfrastructureBean.class.isAssignableFrom(beanClass);
		if (retVal && logger.isTraceEnabled()) {
			logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
		}
		return retVal;
	}

shouldSkip()方法具体实现,

首先获取所有的增强,可以看到上面有一个todo注释:考虑缓存aspect names,因为findCandidateAdvisors()这个方法,我们在后面的获取增强方法,进行代理,还要用到,所以弄一个Set集合进行缓存可以提升性能。

然后遍历增强,如果为AspectJPointcutAdvisor派生类则返回true

@Override
	protected boolean shouldSkip(Class<?> beanClass, String beanName) {
		// TODO: Consider optimization by caching the list of the aspect names
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		for (Advisor advisor : candidateAdvisors) {
			if (advisor instanceof AspectJPointcutAdvisor) {
				if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
					return true;
				}
			}
		}
		return super.shouldSkip(beanClass, beanName);
	}

获取增强

@Override
	protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

1.首先获取所有的增强

2.寻找适用于该bean的增强

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		// 1.
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		// 2.
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

我们虽使用了注解来使用aop,但是xml仍可以进行配置,因此需要获取xml和注解配置的增强

@Override
	protected List<Advisor> findCandidateAdvisors() {
		// 获取xml中配置的增强
		List<Advisor> advisors = super.findCandidateAdvisors();
		// 获取注解配置的增强
		advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		return advisors;
	}

xml和注解获取增强不详细描述,xml无非就是进行解析获取。注解的话就是获取所有的beanName,然后扫描这些bean的Aspect注解,然后获取切点,增强等等。

然后根据xml或者注解中配置的表达式获取适用于此bean的增强。下篇进行代理创建的分析

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring容器初始化完成的回调方法

    我们可能经常会碰到一些奇奇怪怪的需求,比如在IOC容器初始化完成前实例化一些bean,bean的初始化回调等等等。今天来讲一下如何实现Spring IOC容器如...

    冰枫
  • dubbo源码——服务提供者的服务暴露过程(一)

    <dubbo:xxx/>标签是Spring的自定义标签,可以查看dubbo.jar包下的META-INF->spring.handlers

    冰枫
  • CountDownLatch和CyclicBarrier解决运动员赛跑多线程问题

    最近有道多线程的面试题: 五个运动员(相当于五个线程),一个裁判(Main线程),满足以下三个条件,如何实现: 1.同时起跑 2.要所有运动员都到达终点以...

    冰枫
  • 07-如何为Hue集成AD认证

    Fayson在前面的文章《01-如何在Window Server 2012 R2搭建Acitve Directory域服务》、《02-Active Direct...

    Fayson
  • ReentrantLock是如何基于AQS实现的

    ReentrantLock是一个可重入的互斥锁,基于AQS实现,它具有与使用 synchronized 方法和语句相同的一些基本行为和语义,但功能更强大。

    本人秃顶程序员
  • 2020-09-13:判断一个正整数是a的b次方,a和b是整数,并且大于等于2,如何求解?

    首先确定b的范围,b的范围一定在[2,logN]里。然后遍历b,求a的范围,如果范围长度等于0,说明这个正整数是a的b次方。

    福大大架构师每日一题
  • Web安全实战

    前言 本章将主要介绍使用Node.js开发web应用可能面临的安全问题,读者通过阅读本章可以了解web安全的基本概念,并且通过各种防御措施抵御一些常规的恶意攻击...

    wangxl
  • 生气!面试官你过来,我给你手写一个Spring Aop实现!

    又是一周过去了,不知上周发的关于Spring循环依赖使用的三级缓存你们掌握到什么样子了呢?这周又是一篇深度好文(自夸一下),作者每天下班肝了好几天赶出来的文章,...

    止术
  • 可用性测试的实施详解(纯干货!)

    顾翔老师开发的bugreport2script开源了,希望大家多提建议。文件在https://github.com/xianggu625/bug2testscr...

    小老鼠
  • 斐波那契数列

    用户3003813

扫码关注云+社区

领取腾讯云代金券