Spring之AOP的实现

目录

1.前言

2.代理的实现

2.1 静态代理

2.2 动态代理

2.2.1 JDK实现动态代理

2.2.2 CGLIB实现动态代理

3. Spring中AOP的实现原理

3.1 CGLib动态代理的实现

3.2 JDK动态代理的实现

4. JDK动态代理 VS CGLIB动态代理

4.1 性能对比

4.1.1 创建性能对比

4.1.2 调用性能对比

4.2 如何设置代理方式


1.前言

在上一遍文章Spring之面向切面编程(AOP)中主要是了解了AOP相关的知识,这篇文章中主要讲解AOP的实现原理。

2.代理的实现

首先,代理可分为静态代理和动态代理,而动态代理又有jdk动态代理和cglib动态代理两种实现,下面来讲解具体的实现。

2.1 静态代理

静态代理比较好理解,就是我们自己创建一个代理对象,去代替被代理的对象做事情,进而对被代理的对象进行增强。

 我们先创建一个动物的接口,并定义了一个方法call

/**
 * 定义一个动物的借口
 */
public interface Animal {
    /**
     * 定义一个抽象的方法
     */
    void call();
}

然后创建一个动物的实现类Dog,并实现了call方法,如下:

/**
 * 定义个类Dog,实现了Animal接口
 */
public class Dog implements Animal {

    @Override
    public void call() {
        System.out.println("Dog call");
    }
}

这个Dog就是被代理的对象,我们现在创建一个代理类AnimalProxy,它实现了Animal,如下:

/**
 * 动物的代理类
 */
public class AnimalProxy implements Animal{
    private Animal animal;

    //传入被代理的对象
    public AnimalProxy(Animal animal) {
        this.animal = animal;
    }

    @Override
    public void call() {
        System.out.println("调用业务方法之前执行");
        //调用被代理对象的业务方法
        animal.call();
        System.out.println("调用业务方法之后执行");
    }
}

然后我们看方法的调用

    public static void main(String[] args) {
        //1.静态代理
        //创建被代理的对象
        Dog dog = new Dog();
        //创建代理对象,并把被代理的对象通过构造方法传入
        Animal animal = new AnimalProxy(dog);
        //调用代理类的方法
        animal.call();
    }

可以看到,我们先创建被代理的对象dog,然后把它传入到代理类AnimalProxy的构造方法去创建代理对象,最后调用代理类的call方法,进而调用了dog的call方法,当然在调用dog的call方法之前和之后,我们可以自定义的业务逻辑,比如做日志拦截、权限控制等。

但是静态代理存在一个问题,就是针对每一个接口,都需要手动去写一个代理类,有没有什么好的方式呢?答案是肯定有的,这就要说到动态代理了。

2.2 动态代理

比较Spring AOP和AspectJ_JKX_geek的博客-程序员资料- 程序员资料

2.2.1 JDK实现动态代理

JDK动态代理主要有用到两个类,InvacationHandler和Proxy

我们先看InvacationHandler,它是一个接口,其中只有一个invoke方法

public interface InvocationHandler {

    /**
     * @param proxy 代理对象
     * @param method  被代理目标实例的某个方法
     * @param args 被代理实例的某个方法的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

我们可以通过实现该接口,并重写invoke方法,来把横切逻辑和业务逻辑编织在一起

而Proxy类是用于创建代理对象的,我们可以调用其newProxyInstance方法来创建代理对象

public class Proxy implements java.io.Serializable {

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
     //省略代码
   }
}

其中:

第一个参数为类加载器

第二个参数是要实现的接口,根据jdk动态代理的实现,这里应该是我们需要代理类的父接口,我们拿2.1中为例,则这里为Animal

第三个参数是一个InvacationHandler实例,其实就是我们自定义的InvacationHandler实现类,用于在业务方法调用之前进行拦截

接下来,我们首先自定义类JDKProxyHandler,并实现InvacationHandler

/**
 * JDK动态代理
 */
//实现InvocationHandler接口
public class JDKProxyHandler implements InvocationHandler {

    //目标业务类
    private Object target;

    //传入目标业务类
    public JDKProxyHandler(Object target) {
        this.target = target;
    }

    /**
     * @param proxy 代理对象
     * @param method  被代理目标实例的某个方法
     * @param args 被代理实例的某个方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("jdk动态代理 调用业务方法之前");
        //利用反射调用业务类的目标方法
        Object reVal = method.invoke(target, args);
        System.out.println("jdk动态代理 调用业务方法之后");
        return reVal;
    }
}

在我们自定义的JDKProxyHandler中,我们提供通过构造方法把目标业务类的对象传递进来,然后在invoke方法中,可以通过method.invoke(target, args)来调用业务类的目标方法,并且在这之前或者之后可以做增强逻辑。

我们再来看看调用端的使用

    public static void main(String[] args) {

        //2.jdk动态代理
        //被代理的目标业务类
        Animal jdkTarget = new Dog();
        //将目标业务类和横切代码编织到一起
        JDKProxyHandler jdkProxyHandler = new JDKProxyHandler(jdkTarget);
        //创建代理对象
        Animal jdkAnimal = (Animal) Proxy.newProxyInstance(jdkTarget.getClass().getClassLoader(), jdkTarget.getClass().getInterfaces(), jdkProxyHandler);
        //调用代理类的方法
        jdkAnimal.call();

    }

我们运行起来可以发现,jdkTarget.getClass().getInterfaces()其实就是Animal,并且最后通过Proxy.newProxyInstance方法得到的其实是一个代理类对象

当我们调用了代理对象的call方法,则会进入到我们重写的invoke方法中,如下:

可以看到,proxy就是我们新创建出来的代理对象,method就是我们调用的call方法,target则是被代理的Dog对象,最后我们通过method.invoke(target,args)反射来调用业务类的方法call

现在这个图,是不是更好理解了?

 

2.2.2 CGLIB实现动态代理

如果我们被代理的类没有实现接口,比如下面的猫类Cat没有实现Animal,则不能使用jdk动态代理,而需要使用cglib动态代理的方式来实现

/**
 * 猫的实现类,不实现接口
 */
public class Cat {
    public void call() {
        System.out.println("Cat call");
    }
}

cglib是采用底层的字节码技术,可以为我们被代理的类去创建一个子类,然后在子类中采用方法拦截的技术拦截所有父类方法的调用,并在其中去织入我们的横切逻辑。

主要是使用了MethodInterceptor接口,它实现了Callback接口,其中的intercept方法就为拦截方法

public interface MethodInterceptor extends Callback {
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}

其中:

第一个参数Object var1是代理对象

第二个参数Method var2是被调用的方法

第三个参数Object[] var3是方法的参数

第四个参数MethodProxy var4是方法的代理,可通过该对象的invokeSuper方法调用其父类的方法

是不是跟InvocationHandler的invoke方法很像?

另外,springframework提供了一个Enhancer类用于去创建代理对象

下面我们来创建一个MethodInterceptor的实现类:

/**
 * cglib方式的拦截类
 */
public class CglibInterceptor implements MethodInterceptor {

    /**
     * 获取代理对象
     * @param targetClass
     * @return
     */
    public Object getProxy(Class targetClass) {
        Enhancer enhancer = new Enhancer();
        //设置回调对象
        enhancer.setCallback(this);
        //设置父类为被代理的对象
        enhancer.setSuperclass(targetClass);
        //创建子类实例(代理对象)
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib动态代理 调用业务方法之前");
        //通过代理类调用父类中的方法
        Object reVal = methodProxy.invokeSuper(obj, args);
        System.out.println("cglib动态代理 调用业务方法之后");
        return reVal;
    }
}

首先,我们提供了一个getProxy方法,用于获取代理对象,参数为需要代理的类的class对象,这里为Cat的class对象

通过Enhancer的setCallback方法去设置回调的独享,这里就是本身,所以传的是this

通过Enhancer的setSuperclass方法去设置父类,这里就是传入的参数

最后调用Enhancer的create去创建一个代理对象

然后我们重写了intercept方法,通过methodProxy.invokeSuper(obj,args)调用代理对象父类的业务方法(Cat中的call方法),当然在这之前和之后可以实现横切逻辑。

接下来,我们看看调用端:

  public static void main(String[] args) {
        //3.cglib动态代理
        //方法的拦截对象
        CglibInterceptor cglibInterceptor = new CglibInterceptor();
        Cat cat = (Cat) cglibInterceptor.getProxy(Cat.class);
        //调用代理类的方法
        cat.call();
    }

我们运行起来,可以看到创建出来的代理对象其实是Cat的子类

我们再来回头看看CGLIB动态代理的实现,是不是很一目了然了呢?

3. Spring中AOP的实现原理

3.1 CGLib动态代理的实现

我们以MyController的test方法作为连接点看看Spring中AOP的实现逻辑

@RestController
@RequestMapping("/my")
public class MyController {

    @GetMapping("/test")
    public void test() {
        System.out.println("test 业务方法");
    }
}

然后我们定义一个切面MyAspect


@Aspect //告诉Spring 这是一个切面
@Component  //告诉Spring容器需要管理该对象
public class MyAspect {

    //通过规则确定哪些方法是需要增强的
    @Pointcut("execution (public * com.yc.springboot.controller.MyController.*())")
    public void controller() {
    }

    //前置通知
    @Before("controller()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before advice");
    }
    //返回通知
    @AfterReturning(
            pointcut = "controller()",
            returning = "retVal"
    )
    public void afterReturning(JoinPoint joinPoint, Object retVal) {
        System.out.println("after returning advice, 返回结果 retVal:" + retVal);
    }

    //异常通知
    @AfterThrowing(
            pointcut = "controller()",
            throwing = "ex"
    )
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        System.out.println("after throwing advice, 异常 ex:" + ex.getMessage());
    }

    //后置通知
    @After("controller()")
    public void after(JoinPoint joinPoint) {
        System.out.println("after advice");
    }

    //环绕通知
    @Around("controller()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around before advice");
        //相当于是before advice
        Object reVal = null;
        try {
            reVal = joinPoint.proceed();
        } catch (Exception e) {
            //相当于afterthrowing advice
            System.out.println("around afterthrowing advice");
        }
        //相当于是after advice
        System.out.println("around after advice");
        return reVal;
    }
}

因为MyController没有实现任何接口,所以Spring是使用CGLIB动态代理来实现的,如果Spring提供了一个跟我们之前自定义的CglibInterceptor类似的类,是不是就可以实现了呢?

这就是DynamicAdvisedInterceptor,它实现了MethodInterceptor接口,并重写了intercept方法


	/**
	 * General purpose AOP callback. Used when the target is dynamic or when the
	 * proxy is not frozen.
	 */
	private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

		private final AdvisedSupport advised;

		public DynamicAdvisedInterceptor(AdvisedSupport advised) {
			this.advised = advised;
		}

		@Override
		@Nullable
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			boolean setProxyContext = false;
			Object target = null;
			TargetSource targetSource = this.advised.getTargetSource();
			try {
				if (this.advised.exposeProxy) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
				// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
				target = targetSource.getTarget();
				Class<?> targetClass = (target != null ? target.getClass() : null);
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				// Check whether we only have one InvokerInterceptor: that is,
				// no real advice, but just reflective invocation of the target.
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
					// 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 = methodProxy.invoke(target, argsToUse);
				}
				else {
					// We need to create a method invocation...
					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null && !targetSource.isStatic()) {
					targetSource.releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

	}

我们启动项目,通过http://localhost:8080/my/test访问后,就会进入intercept方法

 可以看到代理对象proxy是MyController的一个使用CGLIB增强的子类,调用的方法为test

我们继续看DynamicAdvisedInterceptor中的intercept方法,首先要看

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

该方法获得了方法的所有增强或者增强的拦截,包括以下6个:

  1.  ExposeInvocationInterceptor
  2.  AspectJAfterThrowingAdvice
  3.  AfterReturningAdviceInterceptor
  4.  AspectJAfterAdvice
  5.  AspectJAroundAdvice
  6.  MethodBeforeAdviceInterceptor

然后通过chain去创建一个CglibMethodInvacation对象

new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)

它继承了ReflectiveMethodInvocation类

	private static class CglibMethodInvocation extends ReflectiveMethodInvocation 

调用了其proceed方法,我们看看代码:

		@Override
		@Nullable
		public Object proceed() throws Throwable {
			try {
				return super.proceed();
			}
			catch (RuntimeException ex) {
				throw ex;
			}
			catch (Exception ex) {
				if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) {
					throw ex;
				}
				else {
					throw new UndeclaredThrowableException(ex);
				}
			}
		}

只是调用了父类的proceed方法,其实就是ReflectiveMethodInvocation的proceed方法:

	@Override
	@Nullable
	public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

currentInterceptorIndex是表示chain中当前拦截的序号,开始为-1,这里把currentInterceptorIndex自增1,然后作为下标从chain中获取值,得到的则是ExposeInvocationInterceptor,然后调用它的invoke方法,我们来看看它的invoke方法

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		MethodInvocation oldInvocation = invocation.get();
		invocation.set(mi);
		try {
			return mi.proceed();
		}
		finally {
			invocation.set(oldInvocation);
		}
	}

直接调用了mi的proceed方法,而mi就是传入进来的参数,是CglibMethodInvacation对象,所以又回到了它的proceed方法,它会继续调用父类的proceed方法。

这个时候currentInterceptorIndex的值为0,自增后获取到的增强为AspectJAfterThrowingAdvice,看下它的invoke方法,如下:

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
			if (shouldInvokeOnThrowing(ex)) {
				invokeAdviceMethod(getJoinPointMatch(), null, ex);
			}
			throw ex;
		}
	}

这里跟ExposeInvocationInterceptor的invoke方法很像,都是调用了CglibMethodInvacation的proceed方法,不过不同的是,这里增加在catch里,调用了invokeAdviceMethod方法,从方法名我们也能看出,是调用增强的方法了,我们来看看这个方法:

	protected Object invokeAdviceMethod(
			@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
			throws Throwable {

		return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
	}

	protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
		Object[] actualArgs = args;
		if (this.aspectJAdviceMethod.getParameterCount() == 0) {
			actualArgs = null;
		}
		try {
			ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
			//利用反射调用增强方法
			return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
		}
		catch (IllegalArgumentException ex) {
			throw new AopInvocationException("Mismatch on arguments to advice method [" +
					this.aspectJAdviceMethod + "]; pointcut expression [" +
					this.pointcut.getPointcutExpression() + "]", ex);
		}
		catch (InvocationTargetException ex) {
			throw ex.getTargetException();
		}
	}

关键的是this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);这里的aspectJAdviceMethod其实就是MyAspect的afterThrowing方法,这也就是为什么我们程序抛出异常后,可以回调异常增强方法的原因。

假设我们还没发生异常,我们继续看AspectJAfterThrowingAdvice的invoke方法中调用mi.proceed()的流程,这里又是调用了CglibMethodInvacation的proceed方法,然后调用其父类ReflectiveMethodInvocation的proceed方法,这个时候从chain中取出的是AfterReturningAdviceInterceptor,我们来看它的invoke方法:

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
	
    private final AfterReturningAdvice advice;

    @Override
	public Object invoke(MethodInvocation mi) throws Throwable {
        //继续流程
		Object retVal = mi.proceed();
        //继续流程后,调用增强方法
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}
}

可以看到,这里是首先调用mi.proceed方法,让流程继续并得到返回值,然后再把返回值作为参数调用this.advice.afterReturning方法,advice是一个AfterReturningAdvice 对象,而AfterReturningAdvice是一个接口,afterReturning是它的子类AspectJAfterReturningAdvice来实现的,我们看看这个方法:

public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice
		implements AfterReturningAdvice, AfterAdvice, Serializable {


	@Override
	public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
		if (shouldInvokeOnReturnValueOf(method, returnValue)) {
			invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
		}
	}
}

可以看到,这里又是调用了invokeAdviceMethod方法,上面已经介绍过这个方法就是利用反射调用我们的增强方法,只不过这里的增强方法是MyAspect的返回增强afterReturning,这样业务方法执行完后就能把返回值传递到我们的afterReturning方法中。

不过this.advice.afterReturning方法也是在Object retVal = mi.proceed()方法执行后才调用,所以还是要继续看ReflectiveMethodInvocation的invoke方法,这个时候获得的是AspectJAfterAdvice看看它的invoke方法:

public class AspectJAfterAdvice extends AbstractAspectJAdvice
		implements MethodInterceptor, AfterAdvice, Serializable {


	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
            //继续流程
			return mi.proceed();
		}
		finally {
            //调用增强方法
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}
}

可以看到,也是先调用mi.proceed方法,然后再在finally中调用了后置增强方法after,这也是为什么我们业务方法中就算抛出了一场,我们的后置增强方法也能得到执行。

我们继续ReflectiveMethodInvocation的invoke方法,这个时候得到的是AspectJAroundAdvice对象,它的invoke方法如下:

public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {


	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		if (!(mi instanceof ProxyMethodInvocation)) {
			throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
		}
		ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
		ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
		JoinPointMatch jpm = getJoinPointMatch(pmi);
        //调用增强方法
		return invokeAdviceMethod(pjp, jpm, null, null);
	}
}

可以看到,这里并没有调用mi.proceed方法,而是直接调用了invokeAdviceMethod方法,也就是调用了我们自己切面里的MyAspect的around方法,可是我们发现前置增强方法和业务方法还没调用,为什么就不继续调用mi.proceed方法了呢?我们来看看我们的around方法就知道了

    //环绕增强
    @Around("controller()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around before advice");
        //相当于是before advice
        Object reVal = null;
        try {
            //调用切点的proceed方法
            reVal = joinPoint.proceed();
        } catch (Exception e) {
            //相当于afterthrowing advice
            System.out.println("around afterthrowing advice");
        }
        //相当于是after advice
        System.out.println("around after advice");
        return reVal;
    }

关键代码是joinPoint方法,它是接口ProceedingJoinPoint中的一个方法

public interface ProceedingJoinPoint extends JoinPoint {

    Object proceed() throws Throwable;
}

它的具体实现在MethodInvocationProceedingJoinPoint中

public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {

	@Override
	public Object proceed() throws Throwable {
		return this.methodInvocation.invocableClone().proceed();
	}
}

this.methodInvocation.invocableClone()就是把this.methodInvocation对象clone 了一份,而methodInvocation是一个ProxyMethodInvocation对象,它的proceed在它的子类ReflectiveMethodInvocation和ReflectiveMethodInvocation的子类CglibMethodInvocation中都有实现。

惊喜不?这不又回到了ReflectiveMethodInvocation的proceed方法,这也是为什么如果我们对方法做了环绕的增强,而不去手动调用joinPoint.proceed();方法时,业务方法也得不到执行的原因。

好,我们继续往后面看ReflectiveMethodInvocation的proceed方法。这个时候从chain中获得的是MethodBeforeAdviceInterceptor对象,我们看它的invoke方法

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

	private final MethodBeforeAdvice advice;


	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
        //调用before增强方法
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        //继续调用proceed方法
		return mi.proceed();
	}

}

首先是调用前置增强方法before,然后再调用mi.proceed方法继续流程。这个时候其实所有的增强都遍历完了,会怎么执行呢?我们回头看看ReflectiveMethodInvocation中的proceed方法。

	public Object proceed() throws Throwable {
		// 当chain中所有的增强遍历完后,则会满足该要求
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

要注意的是开头这里,因为currentInterceptorIndex最开始是从-1开始自增的,所以这时currentInterceptorIndex=5,正好满足if要求,这时会调用invokeJoinpoint方法并得到返回值,它的子类CglibMethodInvocation有重写这个方法,看明天我们也知道,是需要调用真正的切点(被代理的方法)我们来看看该方法:

private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

		@Nullable
		private final MethodProxy methodProxy;

		public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
				Object[] arguments, @Nullable Class<?> targetClass,
				List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {

	
		@Override
		protected Object invokeJoinpoint() throws Throwable {
			if (this.methodProxy != null) {
                //通过反射调用业务方法
				return this.methodProxy.invoke(this.target, this.arguments);
			}
			else {
				return super.invokeJoinpoint();
			}
		}
	}

methodProxy其实就是被代理的方法,这里是MyController的test()方法,target是被代理的对象,这里是MyController的对象,arguments是调用时的参数。

使用this.methodProxy.invoke(this.target, this.arguments);通过反射调用真实的业务方法,并把返回值返回出去,这样,我们的业务方法就得到了真正的执行。

然后会逐步返回,调用依次调用after、afterReturning方法,我们用一个时序图来展示方法的调用流程

可以看出:

  1. 当我们访问MyController的test方法时,首先被调用的是DynamicAdvisedInterceptor的intercept方法
  2. 在ReflectiveMethodInvocation中的proceed方法中,不断地从chain中遍历拦截器,然后调用其invoke方法,形成一条责任链,完成拦截后的处理;如果遍历完拦截器后,调用invokeJoinpoint方法,触发真正的业务方法。
  3. 如果我们有around增强,需要手动调用proceed方法,是上述流程图的18,否则后续业务方法也得不到执行
  4. 可以看到增强方法和业务方法的回调步骤为around、before、 业务方法、after、afterReturning,如果有异常会回调afterThrowing

上述就是CGLIB实现动态代理方式,接下来我们再看看jdk实现动态代理的方式。

3.2 JDK动态代理的实现

像2.2.1 JDK实现动态代理中,Spring中是否有自定义的InvocationHandler的子类么?答案是肯定有的,它就是JdkDynamicAopProxy,我们来看看它的定义以及回调方法invoke方法:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

	@Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {

			Object retVal;

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

			if (chain.isEmpty()) {

				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				//创建一个方法调用
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// 调用ReflectiveMethodInvocation的proceed方法
				retVal = invocation.proceed();
			}

			// Massage return value if necessary.
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
	
				retVal = proxy;
			}
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}
}

可以看到,跟CGLIB的不同在于,CGLIB中拦截方法中创建的是CglibMethodInvocation对象,调用其proceed方法,不过最后也是调用其父类ReflectiveMethodInvocation的proceed方法。而JDK动态代理中,则是直接调用了ReflectiveMethodInvocation的proceed方法,它的方法我们之前已经介绍过了,无非就是从chain中不断取出拦截器,然后去调用相应的invoke方法。

下面是流程图:

可以看到不同点在于:

  1. 这里第一步回调的编程了JdkDynamicAopProxy的inovke方法
  2. 没有了CglibMethodInvacation,而是直接调用了ReflectiveMethodInvocation中的proceed方法,包括后续拦截器中再次调用proceed方法时,也是ReflectiveMethodInvocation中的proceed方法
  3. 在第17步调用invokeJoinpoint时,调用的是自身ReflectiveMethodInvocation的invokeJoinpoint方法,最后也同样会通过反射去调用业务方法

4. JDK动态代理 VS CGLIB动态代理

前面讲到了,如果被代理的类有实现接口,则默认会使用JDK动态代理,而如果被代理的类没有实现接口,则使用CGLIB动态代理。那我们如何去选择呢?

下面是《精通Spring 4.x 企业应用开发实战》中的解释:

JDK动态代理所创建的代理对象,在Java1.3下,性能差强人意。虽然在高版本的JDK中动态代理对象的性能得到了很大的提高,但有研究表明,CGLib所创建的动态代理对象的性能依旧比JDK所创建的动态代理对象的性能高不少(大概10倍)。但CGLib在创建代理对象时所花费的时间却比JDK动态代理多(大概8倍)。对于singleton的代理对象或者具有实例池的代理,因为无须频繁地创建代理对象,所以比较适合采用CGLib动态代理技术;反之则适合采用JDK动态代理技术。

简单来说就是:调用方法CGLIB动态代理性能比JDK动态代理快10倍左右;创建对象JDK动态代理比CGLIB动态代理快8倍左右。所以,不需要经常创建对象的用CGLIB动态代理,如果需要经常创建对象的则用JDK动态代理。

原因也不难理解,因为JDK动态代理是基于反射机制,总体来说,反射机制在生成类的过程比较快,执行时通过反射调用委托类接口比较慢;而CGLIB动态代理是借助asm来实现的,它需要根据目标类的class文件而生成子类的class文件,所以创建的过程比较慢,但是不需要使用反射调用委托类的方法,所以执行会比较快。

我们写代码来对创建和调用两个场景来进行测试,下面的代码是基于jdk1.8

4.1 性能对比

4.1.1 创建性能对比

下面是针对不使用代理、使用静态代理、使用jdk动态代理、使用CGLIB动态代理分别创建times个对象,所需要的时间:

    /**
     * 测试创建耗时情况
     */
    public static void testCreate(int times) {
        //创建对象个数
        //1.不使用代理
        long begin1 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            Dog dog = new Dog();
        }
        long end1 = System.currentTimeMillis();
        System.out.println("不使用代理创建耗时:" + (end1 - begin1)  + "ms");

        //2.静态代理
        long begin2 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            Dog dog = new Dog();
            Animal animal = new AnimalProxy(dog);
        }
        long end2 = System.currentTimeMillis();
        System.out.println("静态代理创建耗时:" + (end2 - begin2)  + "ms");

        //3.jdk动态代理
        long begin3 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            Animal jdkTarget = new Dog();
            JDKProxyHandler jdkProxyHandler = new JDKProxyHandler(jdkTarget);
            Animal jdkAnimal = (Animal) Proxy.newProxyInstance(jdkTarget.getClass().getClassLoader(), jdkTarget.getClass().getInterfaces(), jdkProxyHandler);
        }
        long end3 = System.currentTimeMillis();
        System.out.println("jdk动态代理创建耗时:" + (end3 - begin3) + "ms");

        //4.cglib动态代理
        long begin4 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            CglibInterceptor cglibInterceptor = new CglibInterceptor();
            Cat cat = (Cat) cglibInterceptor.getProxy(Cat.class);
        }
        long end4 = System.currentTimeMillis();
        System.out.println("cglib动态代理创建耗时:" + (end4 - begin4) + "ms");
    }

我们先传入times=100,即创建100个对象或者代理对象所需时间,结果如下:

可以看到,不使用代理或者使用静态代理,创建的时间几乎为0,而使用jdk动态代理比cglib动态代理快9倍

我们再让times=10000,结果如下:

 

可以看到,jdk动态代理比cglib动态代理依然快4.2倍。

所以,jdk动态代理创建代理对象的速度的确要比cglib要快。

下面我们测试下方法调用时的性能

4.1.2 调用性能对比

下面是针对不使用代理、使用静态代理、使用jdk动态代理、使用CGLIB动态代理分别调用times次call方法,所需要的时间:


    /**
     * 测试调用耗时情况
     */
    public static void testCall(int times) {
        //先进行创建
        Dog dog1 = new Dog();
        Dog dog2 = new Dog();
        Animal animal = new AnimalProxy(dog2);
        Animal jdkTarget = new Dog();
        JDKProxyHandler jdkProxyHandler = new JDKProxyHandler(jdkTarget);
        Animal jdkAnimal = (Animal) Proxy.newProxyInstance(jdkTarget.getClass().getClassLoader(), jdkTarget.getClass().getInterfaces(), jdkProxyHandler);
        CglibInterceptor cglibInterceptor = new CglibInterceptor();
        Cat cat = (Cat) cglibInterceptor.getProxy(Cat.class);
        //测试调用时间
        //1.不使用代理
        long begin1 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            dog1.call();
        }
        long end1 = System.currentTimeMillis();
        System.out.println("不使用代理调用耗时:" + (end1 - begin1)  + "ms");

        //2.静态代理
        long begin2 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            animal.call();
        }
        long end2 = System.currentTimeMillis();
        System.out.println("静态代理调用耗时:" + (end2 - begin2)  + "ms");

        //3.jdk动态代理
        long begin3 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            jdkAnimal.call();
        }
        long end3 = System.currentTimeMillis();
        System.out.println("jdk动态代理调用耗时:" + (end3 - begin3) + "ms");

        //4.cglib动态代理
        long begin4 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            cat.call();
        }
        long end4 = System.currentTimeMillis();
        System.out.println("cglib动态代理调用耗时:" + (end4 - begin4) + "ms");
    }

为了减少创建的影响,我们先一次性把所需要的对象都创建出来,再传入times=100,即调用100次call方法所需时间,结果如下:

诡异的事情出现了,发现jdk动态代理竟然是cglib动态代理的5.5倍

我们再让times=10000,结果如下:

 

可以看到,jdk动态代理依然是cglib动态代理的4倍。

所以,在jdk1.8下,jdk动态代理调用的速度同样要比CGLIB动态代理对象调用的速度要快。

这跟之前的结论是违背了么?其实这跟jdk的版本有关,从jdk1.7开始,反射的性能得到了较大的提升,所以jdk1.7之后的版本JDK动态代理比CGLIB动态代理生成的代理对象调用更快。

4.2 如何设置代理方式

我们以3.1中的为例,被代理类AOPTestController因为没有实现接口,我们在Around增强中发现的确使用了CGLIB动态代理方式

我们知道,如果没有实现接口,只能使用CGLIB动态代理,而不能使用JDK动态代理,那如果我们实现了接口,能否指定使用CGLIB动态代理呢?

我们自定义一个BaseController的接口,里面定义了testannotation方法,然后让AOPTestController实现BaseController,这个时候,结果如下:

我们发现,竟然还是使用的CGLIB代理方式, 这是为什么呢?

其实是因为这里是一个Spring Boot项目,在Spring Boot2.x的版本,默认开启了代理优化,使用了CGLIB代理的方式,可以看AopAutoConfiguration

那我们怎么才能使用JDK的代理呢?我们可以使用spring.aop.proxy-target-class = false来制定使用JDK动态代理,我们在bootstrap.yml加上如下代码:

spring:
  aop:
    proxy-target-class: false

然后再运行项目,如下:

可以看到,这样就切换到了JDK的动态代理。

但这就衍生出一个更大的问题,我们有必要去修改Spring Boot默认推荐的CGLIB动态代理方式么?或者说我们代理的类一般都会有实现接口么? 

好,点到为止。

下一篇主要介绍AOP代理对象创建的过程以及AOP失效的问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值