Spring AOP源码解析-创建代理对象

一.简介

在上一篇文章中,分析了Spring是如何为目标bean筛选合适的通知器。现在通知器选好了,接下来就是通过代理的方式将通知器(Advisor)所持有的所有通知(Advice)织入到bean的某些方法前后,这篇文章就是分析创建代理的过程。

二.背景知识

2.1 proxy-target-class

Spring AOP部分使用JDK动态代理或者CGLIB来为目标对象创建代理(建议尽量使用JDK动态代理),如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则会创建一个CGLIB代理。如果你希望强制使用CGLIB代理(如果希望代理目标对象的所有方法,而不只是实现自接口的方法)那也可以。但是需要考虑以下两个问题:

  • 无法通知(advice)Final方法,因为它们不能被覆写。
  • 你需要将CGLIB二进制发行包放在classpath下。

JDK本身就提供了动态代理,强制使用CGLIB代理需要将<aop:config>的proxy-target-class属性设为true:

<aop:config proxy-target-class="true">
    <aop:aspect id="xxx" ref="xxxx">
        <!-- 省略 -->
    </aop:aspect>
</aop:config>

当使用CGLIB代理和@Aspect自动代理支持,可以按照以下方式设置:

<aop:aspectj-autoproxy proxy-target-class="true"/>

如上,默认情况下 proxy-target-class 属性为 false。当目标 bean 实现了接口时,Spring 会基于 JDK 动态代理为目标 bean 创建代理对象。若未实现任何接口,Spring 则会通过 CGLIB 创建代理。而当 proxy-target-class 属性设为 true 时,则会强制 Spring 通过 CGLIB 的方式创建代理对象,即使目标 bean 实现了接口。

JDK动态代理和CGLIB代理的区别:

  • JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
  • CGLIB代理:其实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的Java字节码编辑类库)操作字节码实现的,性能比JDK强。

2.2 AopProxy接口

为目标 bean 创建代理对象前,需要先创建 AopProxy 对象,然后再调用该对象的 getProxy 方法创建实际的代理类。我们先来看看 AopProxy 这个接口的定义,如下:

public interface AopProxy {

    /** 创建代理对象 */
    Object getProxy();
    
    Object getProxy(ClassLoader classLoader);
}

在 Spring 中,有两个类实现了 AopProxy,如下:

2.3 创建代理的入口处


public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    
    @Override
    /** bean 初始化后置处理方法 */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                // 如果需要,为 bean 生成代理对象
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
 
        /*
         * 如果是基础设施类(Pointcut、Advice、Advisor 等接口的实现类),或是应该跳过的类,
         * 则不应该生成代理,此时直接返回 bean
         */ 
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            // 将 <cacheKey, FALSE> 键值对放入缓存中,供上面的 if 分支使用
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
 
        // 为目标 bean 查找合适的通知器
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        /*
         * 若 specificInterceptors != null,即 specificInterceptors != DO_NOT_PROXY,
         * 则为 bean 生成代理对象,否则直接返回 bean
         */ 
        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());
            /*
             * 返回代理对象,此时 IOC 容器输入 bean,得到 proxy。此时,
             * beanName 对应的 bean 是代理对象,而非原始的 bean
             */ 
            return proxy;
        }
 
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        // specificInterceptors = null,直接返回 bean
        return bean;
    }
}

三.动态代理

3.1 基于JDK的动态代理

基于 JDK 的动态代理主要是通过 JDK 提供的代理创建类 Proxy 为目标对象创建代理,下面我们来看一下 Proxy 中创建代理的方法声明。如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

参数列表解释如下:

  • loader:类加载器
  • interfaces:目标类所实现的接口列表
  • h:用于封装代理逻辑

JDK动态代理对目标类是有一定的要求的,即要求目标类必须实现了接口,JDK动态代理只能为实现了接口的目标类生成代理对象。至于 InvocationHandler,是一个接口类型,在自定义的InvocationHandler中需要重写三个函数(这三个函数后面会解析到用途):

  • 构造函数,将代理的对象传入。
  • invoke方法,此方法中实现了AOP增强的所有逻辑
  • getProxy方法,获得代理对象

下面来演示下JDK动态代理的使用方式,这个场景就是在之前写过的计算器方法上面加日志:

(1)创建接口


 
public interface Calculator {
	
	public int add(int i,int j);
	public int sub(int i,int j);
	public int mul(int i,int j);
	public int div(int i,int j);
 

}

(2)创建接口实现类


import com.test.inter.Calculator;
 
public class MyMathCalculator implements Calculator{
 
	@Override
	public int add(int i, int j) {
		System.out.println("【add】方法开始了");
		int result=i+j;
		return result;
	}
 
	@Override
	public int sub(int i, int j) {
		System.out.println("【sub】方法开始了");
		int result=i-j;
		return result;
	}
 
	@Override
	public int mul(int i, int j) {
		System.out.println("【mul】方法开始了");
		int result=i*j;
		return result;
	}
 
	@Override
	public int div(int i, int j) {
		System.out.println("【div】方法开始了");
		int result=i/j;
		return result;
	}
	
	
 
}

(3)创建一个类来实现目标方法

import java.lang.reflect.Method;
import java.util.Arrays;
 
public class LogUtils {
	//方法开始前
	public static void logStart(Method method,Object...arg2){
		System.out.println("【"+method.getName()+"】方法开始执行,用的参数列表【"+Arrays.asList(arg2)+"】");
	}
	
    //方法正常返回
	public static void logReturn(Method method,Object result){
		System.out.println("【"+method.getName()+"】方法开始执行,计算结果是"+result);
	}
	
     //方法抛出异常
	public static void logException(Method method,Exception e){
		System.out.println("【"+method.getName()+"】方法执行出现异常了,异常信息是"+e.getCause()+"这个异常已经通知了测试小组");
	}
	
    //方法结束
	public static void logEnd(Method method){
		System.out.println("【"+method.getName()+"】方法最终结束了");
 
	}
	
}

(4)编写一个类,为传入的参数对象创建一个动态代理对象(这一步也是写成创建自定义InvocationHandler,用于对接口提供的方法进行增强。然后在测试的时候先实例化目标对象和InvocationHandler,根据目标对象生成代理对象,最后调用代理对象的方法。):

package com.test.proxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
 
import com.test.inter.Calculator;
import com.test.utils.LogUtils;
 
/*
 * 生成代理的类
 * JDK 默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的。
 */
public class caculatorProxy {
	
	/*
	 * 为传入的参数对象创建一个动态代理对象
	 * Calculator calculator:被代理的对象
	 */
	public static Calculator getProxy(final Calculator calculator)
	{
		//方法执行器,帮目标对象执行目标方法
		InvocationHandler h=new InvocationHandler() {
			
			/*
			 * (non-Javadoc)
			 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
			 * Object proxy:代理对象,给JDK使用,任何时候都不要动这个对象
			 * Method method:当前将要执行的目标对象的方法
			 * Object[] arg2:这个方法调用时外界传入的参数值
			 */
			@Override
			public Object invoke(Object proxy, Method method, Object[] arg2)
					throws Throwable {
				//利用反射执行目标方法
				//目标方法执行后的返回值
				System.out.println("这是动态代理将要帮你执行方法...");
				
				Object result=null;
				try {
					LogUtils.logStart(method, arg2);
					result = method.invoke(calculator, arg2);
				    LogUtils.logReturn(method, result);
				} catch (Exception e) {
					LogUtils.logException(method, e);
				}
				finally{
					LogUtils.logEnd(method);
				}
				//返回值必须返回出去外界才能拿到真正执行后的返回值
				return result;
			}
		};
		Class<?>[] interfaces=calculator.getClass().getInterfaces();
		ClassLoader loader=calculator.getClass().getClassLoader();
		//Proxy为目标对象创建代理对象
		Object proxy=Proxy.newProxyInstance(loader, interfaces, h);
		return (Calculator) proxy; 
		
	}
 
}

(5)测试

package com.test.test;
 
import static org.junit.Assert.*;
 
import org.junit.Test;
 
import com.test.impl.MyMathCalculator;
import com.test.inter.Calculator;
import com.test.proxy.caculatorProxy;
 
public class AOPTest {
 
	@Test
	public void test() {
		Calculator calculatro=new MyMathCalculator();
		//代理对象和被代理对象唯一能产生的关联就是实现了同一个接口
		Calculator prox=caculatorProxy.getProxy(calculatro);
		prox.add(2, 1);
	}
 
}

(6)测试结果:

3.2 基于CGLIB的动态代理

CGLIB是一个java字节码的生成工具,它动态生成一个被代理类的子类,子类重写被代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

下来看一下CGLIB动态代理的使用方式:

(1)目标类:

public class Dog{
    
    final public void run(String name) {
        System.out.println("狗"+name+"----run");
    }
    
    public void eat() {
        System.out.println("狗----eat");
    }
}

(2)方法拦截器:

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class MyMethodInterceptor implements MethodInterceptor{

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("这里是对目标类进行增强!!!");
        //注意这里的方法调用,不是用反射哦!!!
        Object object = proxy.invokeSuper(obj, args);
        return object;
    }  
}

(3)测试类:

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

public class CgLibProxy {
    public static void main(String[] args) {
        //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\java\\java_workapace");
        
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(Dog.class);
        //设置回调函数
        enhancer.setCallback(new MyMethodInterceptor());
        
        //这里的creat方法就是正式创建代理类
        Dog proxyDog = (Dog)enhancer.create();
        //调用代理类的eat方法
        proxyDog.eat();       
    }
}

四.源码分析

4.1 createProxy方法(入口处调用的方法)

Spring在为目标方法创建代理的过程中,要会根据bean是否实现接口,以及一些其他配置来决定使用AopProxy何种实现类为目标bean创建代理对象,下来看一下代理创建的过程,如下:

	protected Object createProxy(
			Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
		//在bean对应的BeanDefinition中添加原Class对象属性(保存bean原本的Class)
		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}
 
		ProxyFactory proxyFactory = new ProxyFactory();
		//获取当前类中配置的属性
		proxyFactory.copyFrom(this);
		
		//检查proxyTargetClass属性
		if (!proxyFactory.isProxyTargetClass()) {
			//检查beanDefinitioin中是否包含preserveTargetClass属性,且属性为true
			//设置是否使用CGLib进行代理
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				//筛选代理接口并添加到proxyFactory
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
		
		//获取增强器(包括前面筛选出来的增强器,以及通过setInterceptorNames中添加的通用增强器,默认为空)
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		for (Advisor advisor : advisors) {
			//将所有增强器添加到proxyFactory
			proxyFactory.addAdvisor(advisor);
		}
		
		//设置需要代理的bean对象信息
		proxyFactory.setTargetSource(targetSource);
		//模版方法,由子类定制化代理
		customizeProxyFactory(proxyFactory);
		//用来控制代理工程被配置后,是否还允许修改代理的配置,默认为false
		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}
 
		//创建代理对象
		return proxyFactory.getProxy(getProxyClassLoader());

}

这个方法的工作流程为:

  1. 在BeanDefinition中保存bean原Class对象,因为创建代理后,bean的class会被修改(Spring4中新加入的带你,Spring3中不包含)
  2. 创建ProxyFactory
  3. 设置属性
  4. 过滤目标bean的接口,并添加到ProxyFactory
  5. 获取增强器实例,添加到ProxyFactory中
  6. 创建代理

4.1.1 过滤接口-evaluateProxyInterfaces方法

过滤接口中主要功能是,帮助判断是否使用JDK的动态代理来创建代理。因为JDK动态代理的条件是bean实现了接口,所以Spring会将目标bean实现的接口过滤后添加到ProxyFactory中,方便判断是否使用JDK动态代理,下面是evaluateProxyInterfaces实现:
 

	protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
		//获取所有实现的接口
		Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
		boolean hasReasonableProxyInterface = false;
		for (Class<?> ifc : targetInterfaces) {
			//不是Spring内部回调用的接口(会排除掉InitializingBean、DisposableBean、Aware接口) && 不是语言内部接口 && 接口定义了一个以上的方法
			if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
					ifc.getMethods().length > 0) {
				hasReasonableProxyInterface = true;
				break;
			}
		}
		//如果满足上面三个条件,才会将接口添加到proxyFactory
		if (hasReasonableProxyInterface) {
			// Must allow for introductions; can't just set interfaces to the target's interfaces only.
			for (Class<?> ifc : targetInterfaces) {
				proxyFactory.addInterface(ifc);
			}
		}
		//条件不满足,缺少合适的接口,无法使用JDK动态代理,使用CGLib
		else {
			proxyFactory.setProxyTargetClass(true);
		}
	}

4.2 创建代理

public Object getProxy(ClassLoader classLoader) {
    // 先创建 AopProxy 实现类对象,然后再调用 getProxy 为目标 bean 创建代理对象
    return createAopProxy().getProxy(classLoader);
}

getProxy 这里有两个方法调用,一个是调用 createAopProxy 创建 AopProxy 实现类对象,然后再调用 AopProxy 实现类对象中的 getProxy 创建代理对象。这里我们先来看一下创建 AopProxy 实现类对象的过程,如下:

	protected final synchronized AopProxy createAopProxy() {
		//active会在在第一次创建代理后,设为true
		if (!this.active) {
			//设置active为true,并通知监听器(如果没有配置,为空)
			activate();
		}
		//getAopProxyFactory会返回aopProxyFactory变量,默认实现为DefaultAopProxyFactory
		return getAopProxyFactory().createAopProxy(this);
	}protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    return getAopProxyFactory().createAopProxy(this);
}

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        /*
         * 下面的三个条件简单分析一下:
         *
         *   条件1:config.isOptimize() - 是否需要优化,
         *   条件2:config.isProxyTargetClass() - 检测 proxyTargetClass 的值,
         *         前面的代码会设置这个值
         *   条件3:hasNoUserSuppliedProxyInterfaces(config) 
         *         - 目标 bean 是否实现了接口
         */
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            // 创建 CGLIB 代理,ObjenesisCglibAopProxy 继承自 CglibAopProxy
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            // 创建 JDK 动态代理
            return new JdkDynamicAopProxy(config);
        }
    }
}

Spring生成代理对象的方式有两种,JDK动态代理和CGLib,分别生成JdkDynamicAopProxy和ObjenesisCglibAopProxy,从上面代码可以看出Spring的判断条件:

  1. optimize:用来控制通过CGLib创建的代理是否使用激进的优化策略(该仅对CGLib有效)
  2. proxyTargetClass:当属性为true,使用CGLib,目标类本身被代理而不是目标类的接口,设置方式:<aop:aspectj-autoproxy proxy-target-class="true">。
  3. hasNoUserSuppliedProxyInterfaces:是否存在代理接口(也就是前面过滤接口一节中,添加进去的接口)

下面是对JDK与CGLIB方式的总结:

  • 如果是目标对象实现了接口,默认情况下会采用JDK动态代理实现AOP
  • 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
  • 如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

4.3 获取代理

如上,DefaultAopProxyFactory 根据一些条件决定生成什么类型的 AopProxy 实现类对象。生成好 AopProxy 实现类对象后,下面就要为目标 bean 创建代理对象了。

4.3.1 JdkDynamicAopProxy.getProxy

这里以 JdkDynamicAopProxy 为例,我们来看一下,该类的 getProxy 方法的逻辑是怎样的。如下:

public Object getProxy() {
    return getProxy(ClassUtils.getDefaultClassLoader());
}

public Object getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    }
    //获取代理接口
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		//检查是否在接口中定义了equals或hashCode方法
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		//创建代理对象
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

会发现 JdkDynamicAopProxy 最终调用 Proxy.newProxyInstance 方法创建代理对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值