目录
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 动态代理
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个:
- ExposeInvocationInterceptor
- AspectJAfterThrowingAdvice
- AfterReturningAdviceInterceptor
- AspectJAfterAdvice
- AspectJAroundAdvice
- 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方法,我们用一个时序图来展示方法的调用流程
可以看出:
- 当我们访问MyController的test方法时,首先被调用的是DynamicAdvisedInterceptor的intercept方法
- 在ReflectiveMethodInvocation中的proceed方法中,不断地从chain中遍历拦截器,然后调用其invoke方法,形成一条责任链,完成拦截后的处理;如果遍历完拦截器后,调用invokeJoinpoint方法,触发真正的业务方法。
- 如果我们有around增强,需要手动调用proceed方法,是上述流程图的18,否则后续业务方法也得不到执行
- 可以看到增强方法和业务方法的回调步骤为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方法。
下面是流程图:
可以看到不同点在于:
- 这里第一步回调的编程了JdkDynamicAopProxy的inovke方法
- 没有了CglibMethodInvacation,而是直接调用了ReflectiveMethodInvocation中的proceed方法,包括后续拦截器中再次调用proceed方法时,也是ReflectiveMethodInvocation中的proceed方法
- 在第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失效的问题。