Spring Aop原理

AOP,也就是 Aspect-oriented Programming,译为面向切面编程,是计算机科学中的一个设计思想,旨在通过切面技术为业务主体增加额外的通知(Advice),从而对声明为“切点”(Pointcut)的代码块进行统一管理和装饰。

通常我们使用spring-aop时都会使用注解@EnableAspectJAutoProxy开启aop支持,springboot中采用自动装配我们就不需要手动加上注解了,但原理都一样。

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

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies. The default is {@code false}.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
	 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
	 * Off by default, i.e. no guarantees that {@code AopContext} access will work.
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;

}

注解@EnableAspectJAutoProxy上面还有一个注解@Import(AspectJAutoProxyRegistrar.class),这个注解的功能就是注册beanDefinition,注册的就是AnnotationAwareAspectJAutoProxyCreator,

注册beanDefinition实际上就是注册bean,看这个类的关系图

这个类实现了BeanPostProcessor,bean的后置处理器,一眼就知道这是一个埋点会对所有的bean就行处理,根据后置处理器的生命周期,其主要是在bean的初始化前后进行对bean进行修饰。主要就是在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization这个类中实现。

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

核心实现就是org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#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.
        //1.筛选切面
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
            //2.创建代理
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

前面三个if的逻辑也很重要,主要就是判断这个bean是否需要进行aop,我们直接从注释Create Proxy if we have advice(如果有增强器就创建代理),那么这个advice是什么意思呢?

首先代码org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean

会将spring中我们需要的advisor筛选出来

org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        //查询所有的advisor
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
        //筛选出只适配该bean的advisor
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
         //扩展一个advisor
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
            //对advisor进行排序
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

查询所有advisor时主要分两类,一个时直接实现了advisor接口的类,另一种是通过注解@Aspect修饰的类,下面的@Before、@After或者@Around都是被解析成一个一个Advisor。那么我们就着重看一下Advisor的数据结构。其实我spring提供给用户使用的advisor是PointcutAdvisor,我个人理解就是基于切入点的advisor,

pointcut – the Pointcut targeting the Advice
advice – the Advice to run when Pointcut matches

public interface Advisor {

 
	Advice EMPTY_ADVICE = new Advice() {};
 
	Advice getAdvice();

 
	boolean isPerInstance();




public interface PointcutAdvisor extends Advisor {

	/**
	 * Get the Pointcut that drives this advisor.
	 */
	Pointcut getPointcut();

}


public interface Pointcut {

	 
	ClassFilter getClassFilter();

 
	MethodMatcher getMethodMatcher();

 
	Pointcut TRUE = TruePointcut.INSTANCE;

}


public interface MethodInterceptor extends Interceptor {

 
 
	Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;

}

介于上面两个接口的方法,可以看到一个advisor需要包含advice和pointcut。Pointcut又必须提供

CassFilter和MethodMatcher,又是类匹配又是方法匹配,相信大多数小伙伴都能猜测到多半就是用于筛选adviosr的,没错他确实也是这样的。那Advice是什么,spring给我们提供的的实现就是org.aopalliance.intercept.MethodInterceptor用于方法增强。基于上述描述,我们可以近似的理解

advisor就是切面,advice是增强器也就是通知,Pointcut就是切入点。一个advisor必须包含Advice和Pointcut。我们实现一个自己的advisor时,若不想基于注解@Aespect实现就可以通过这种方式实现,通过这种方式我个人觉得会更加灵活。@Transaction的实现就是自定义Advisor实现的感兴趣可以看一下BeanFactoryTransactionAttributeSourceAdvisor的实现。

塞选出切面后,第二步就是创建代理对象

	Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

这个方法中有一个非常重要的属性proxyFactory

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}
        //创建代理工程
		ProxyFactory proxyFactory = new ProxyFactory();
        //实际是将@EnableAspectJAutoProxy注解的属性拷贝过来
		proxyFactory.copyFrom(this);

		if (proxyFactory.isProxyTargetClass()) {
			// Explicit handling of JDK proxy targets (for introduction advice scenarios)
			if (Proxy.isProxyClass(beanClass)) {
				// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
				for (Class<?> ifc : beanClass.getInterfaces()) {
					proxyFactory.addInterface(ifc);
				}
			}
		}
		else {
			// No proxyTargetClass flag enforced, let's apply our default checks...
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        //切面
		proxyFactory.addAdvisors(advisors);
        //设置目标对象
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// Use original ClassLoader if bean class not locally loaded in overriding class loader
		ClassLoader classLoader = getProxyClassLoader();
		if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
			classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
		}
       //创建代理对象
		return proxyFactory.getProxy(classLoader);
	}

这个单词翻译过来就是代理工厂,那么代理对象肯定是通过这个对象生成的,我们只看jdk动态代理,那么代理对象就是通过这里产出org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)

	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
	}

扩展一个问题,如果我们在业务层想获取到原目标对象我们该怎么操作呢?

因为代理对象实现了目标对象的同时也实现了 下面这些接口

org.springframework.aop.framework.AopProxyUtils#completeProxiedInterfaces(org.springframework.aop.framework.AdvisedSupport, boolean)

	if (!advised.isInterfaceProxied(SpringProxy.class)) {
			proxiedInterfaces.add(SpringProxy.class);
		}
		if (!advised.isOpaque() && !advised.isInterfaceProxied(Advised.class)) {
			proxiedInterfaces.add(Advised.class);
		}
		if (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class)) {
			proxiedInterfaces.add(DecoratingProxy.class);
		}

所以我们只要要将代理对象强转为Advised类型,再通过getTargetSource().getTarget()就可以拿到原对象。

了解jdk动态代理的,最后一个参数为InvocationHandler,JdkDynamicAopProxy是实现了InvocationHandler的,当通过代理对象调用目标方法时,其实是会进入InvocationHandler的invoke方法,那么核心逻辑就是org.springframework.aop.framework.JdkDynamicAopProxy#invoke这个方法。接下来我们就拆成几块研究。

	if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

这行代码的exposeProxy属性就是注解@EnableAspectJAutoProxy的exposeProxy属性,当设置为true时,会将代理对象设置在线程上下文中,可以通过AopContext.currentProxy()获取当前线程上下文的代理对象。

// Get the interception chain for this method.
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

这里又会获取一变切面的通知,因为代理对象是针对类级别,但增强是针对方法级别的,所以这里会再次判断目标方法是否需要增强。

if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				retVal = invocation.proceed();
			}

这里当增强器不为空时,就会依次调用增强器进行方法增强,最终调用目标方法。这里利用了一个责任链模式,感兴趣可以细看。

这里只是理清了aop的主线而已,还有很多细节未挖掘。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值