目录
1.前言
2.代理的实现
2.1 静态代理
2.2 动态代理
2.2.1 JDK实现动态代理
2.2.2 CGLIB实现动态代理
3. Spring中AOP的实现
1.前言
在上一遍文章Spring之面向切面编程(AOP)中主要是了解了AOP相关的知识,这篇文章中主要讲解AOP的实现原理。
2.代理的实现上一篇文章中我们有讲到,代理可分为静态代理和动态代理,而动态代理又有jdk动态代理和cglib动态代理两种不同的实现,下面来讲解具体的实现。
2.1 静态代理静态代理比较好理解,就是我们自己创建一个代理对象,去代替被代理的对象做事情,进而对被代理的对象进行增强。
我们先创建一个动物的接口,并定义了一个方法call
public interface Animal { void call(); }
然后创建一个动物的实现类Dog,并实现了call方法,如下:
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 { 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
//实现InvocationHandler接口 public class JDKProxyHandler implements InvocationHandler { //目标业务类 private Object target; //传入目标业务类 public JDKProxyHandler(Object target) { this.target = target; } @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的实现类:
public class CglibInterceptor implements MethodInterceptor { 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的实现我们以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方法
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
我们启动项目,通过http://localhost:8080/my/test访问后,就会进入intercept方法
可以看到代理对象proxy是MyController的一个使用CGLIB增强的子类,调用的方法为test
我们继续看DynamicAdvisedInterceptor中的intercept方法,首先要看
List
该方法获得了方法的所有增强,
然后通过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; } }
其实也是回到了CglibMethodInvacation对象,所以又回到了它的proceed方法
直到AspectJAroundAdvice,它的invoke方法比较特殊
@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); }
而在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); // TODO AopUtils.invokeJoinpointUsingReflection 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的around方法,其实就是我们的环绕增强的方法,它会利用发射直接调用around方法,我们再来看看我们自定义的环绕增强
//环绕通知 @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; }
需要关注的是joinPoint.proceed方法,这里调用的是MethodInvocationProceedingJoinPoint的proceed方法
@Override public Object proceed() throws Throwable { return this.methodInvocation.invocableClone().proceed(); }
这里调用了MethodInvocation的proceed方法,而procced方法是CglibMethodInvocation来实现的,所以又回到了CglibMethodInvocation的procced方法,然后继续走ReflectiveMethodInvocation的proceed方法,从chain中取值,当获取到的值为MethodBeforeAdviceInterceptor时,它的invoke方法也值得看一看:
@Override public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); return mi.proceed(); }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)