springAOP

99封情书 提交于 2020-01-26 11:26:50

纯程序的springAOP

现在,我们都是用AspectJ来实现面向AOP,因为它的语法很简单。而且,spring非常支持AspectJ。可是我们,还是要从程序化的AOP开始,因为AOP是基于代理的,纯程序能够让我们理清其中的代理模式。

前置通知的应用

AOP能够对方法进行增强,当然,还可以保证方法执行的安全性。我们通过一个类似于Spring Security一样的功能,来了解前置通知的纯程序化的代码。

比如我们有一个bean,我们要对其进行保护:

package com.ocean.testaop.aopfinal;

public class SecureBean {

    public void writeMessage(){
        System.out.println("今天下午3点三号门见");
    }
}

这是机密消息,必须要通过身份验证才能够看到。

我们用一个类来存储用户信息:

package com.ocean.testaop.aopfinal;

public class UserInfo {
    private String userName;
    private String password;

    public UserInfo(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    public String getUserName() {
        return userName;
    }

    public String getPassword() {
        return password;
    }
}

然后我们需要一个验证用户登录的类:

package com.ocean.testaop.aopfinal;

public class SecurityManager {
    private static ThreadLocal<UserInfo> threadLocal
             = new ThreadLocal<>();
    
    //用户登录
    public void login(String userName, String password){
        threadLocal.set(new UserInfo(userName,password));
    }
    
    //用户不登录
    public void logout(){
        threadLocal.set(null);
    }
    
    //拿到登录的用户
    public UserInfo getLoggedOnUser(){
       return threadLocal.get();
    }
}

现在,我们要来创建一个advice,用来对SecureBean进行保护,一个前置通知就要实现MethodBeforeAdvice接口,注意

MethodBeforeAdvice---->BeforeAdvice------>Advice,

Advice是通知的顶级接口。

package com.ocean.testaop.aopfinal;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class SecurityAdvice implements MethodBeforeAdvice {
    private SecurityManager securityManager;

    public SecurityAdvice(SecurityManager securityManager) {
        this.securityManager = securityManager;
    }

    //如果用户是Jack,那就算通过验证;密码不管了
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        UserInfo user = securityManager.getLoggedOnUser();

        if(user == null){
            System.out.println("没有用户被授权");
            throw new SecurityException("你要先登录才能调用方法:"+method.getName());
        }else if("Jack".equals(user.getUserName())){
            System.out.println("Jack,验证通过!");
        }else{
            System.out.println("登录用户是:"+user.getUserName()+" ,不允许调用方法: " + method.getName());
            throw new SecurityException("错误的登录用户!");
        }


    }
}

我们测试一下:

package com.ocean.testaop.aopfinal;

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ProxyFactoryBean;

public class SecurityDemo {


    public static void main(String[] args) {
        SecurityManager securityManager = new SecurityManager();

        SecureBean proxy = getSecureBean();

        securityManager.login("Jack","123456");
        proxy.writeMessage();
        securityManager.logout();

        System.out.println("---------------------------------------------------------------------");

        try{
            securityManager.login("Ocean","123456");
            proxy.writeMessage();
        }catch (SecurityException e){
            System.out.println("捕获到的异常信息: " + e.getMessage());
        }finally {
            securityManager.logout();
        }

        System.out.println("---------------------------------------------------------------------");

        try{
            proxy.writeMessage();
        }catch (SecurityException e){
            System.out.println("捕获到的异常: " + e.getMessage());
        }

    }


    //产生代理
    private static SecureBean getSecureBean(){
        //new出目标对象
        SecureBean target = new SecureBean();

        //ProxyFactory控制aop的织入与代理的创建
        ProxyFactory proxyFactory = new ProxyFactory();
        //指定目标对象
        proxyFactory.setTarget(target);
        //传入通知
        proxyFactory.addAdvice(new SecurityAdvice(new SecurityManager()));

        //拿到代理
        SecureBean proxy = (SecureBean) proxyFactory.getProxy();

        return proxy;
    }
}

结果:

首先我们关注代理的产生。

我们new出了一个代理工厂,并且把目标对象和通知都传给了工厂,由此产生出一个代理。

addAdvice()将通知封装到DefaultPointcutAdvisor中进行处理。
DefaultPointcutAdvisor----->PointcutAdvisor------>Advisor

SpringAOP里面,切面需要由实现了advisor接口的类表示。

我们接着观察拿到代理的过程:

 //拿到代理
 SecureBean proxy = (SecureBean) proxyFactory.getProxy();


最终还是会委托给cglib或者是jdk动态代理。

关于这两个代理,后面叙述。

代理产生后,我们调用代理的方法时:

proxy.writeMessage();

都会经过SecurityAdvice的检查。

注意main方法中的new SecurityManager()private static SecureBean getSecureBean()方法中的new SecurityManager()并非同一个对象,但因为我们用ThreadLocal把数据都存在了当前线程中,所以这并没有关系。

通知的类型与引入通知

Spring有6种通知:

  • 前置通知
  • 后置通知
  • 后置通知返回
  • 环绕通知
  • 异常通知
  • 引入通知

环绕通知类似于前置通知和后置通知的组合,但是它有返回值。

我们稍稍看看引入通知。

为什么要使用引用?因为我们可以动态地为一个类添加功能。对,是类,而不是方法,不是方法的前后置增强,所以,引用是作用于类的。

比如现在我有一个联系人的类:

package com.ocean.testaop.aopfinal;

public class Contact {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

我想要知道,该类的实例是否有修改,即,是否调用了setName(String name)方法。

为了实现这个额外功能,我们定义一个接口:

package com.ocean.testaop.aopfinal;

public interface IsModified {
    boolean isModified();
}

下面,我们就要把这个接口引入,这样的类被称为mixin(将advice与扩展接口混合):

package com.ocean.testaop.aopfinal;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
//创建mixin
public class IsModifiedMixin extends DelegatingIntroductionInterceptor implements IsModified {

    private boolean isModified = false;

    private Map<Method,Method> methodCache = new HashMap<>();


    @Override
    public boolean isModified() {
        return isModified;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable{
       if(!isModified){
           //是否是setter方法
           if(invocation.getMethod().getName().startsWith("set") && invocation.getArguments().length == 1){
               //取出对应的getter方法
               Method getter = getGetter(invocation.getMethod());

               if(getter != null){
                   //传递给setter方法的参数
                   Object newValue = invocation.getArguments()[0];
                   //getter方法的返回值
                   Object oldValue = getter.invoke(invocation.getThis(),null);

                   //将两者进行比较以确定是否调用了setter方法,以此监测对象是否被改变
                   if(newValue == null && oldValue == null){
                       isModified = false;
                   }else if(newValue == null && oldValue != null){
                       isModified = true;
                   }else if(newValue != null && oldValue == null){
                       isModified = true;
                   }else {
                       //都不是null的话,就比较值
                       isModified = !newValue.equals(oldValue);
                   }

               }
           }
       }
       //调用父类的invoke方法,将调用分派到被通知对象
       return super.invoke(invocation);
    }


    private Method getGetter(Method setter){
        //从缓存当中根据setter去拿getter方法
        Method getter = methodCache.get(setter);

        //拿到就返回
        if(getter != null){
            return getter;
        }

        //拿不到就找到getter方法并put进map中
        String getterName = setter.getName().replaceFirst("set","get");
        try{
            getter = setter.getDeclaringClass().getMethod(getterName,null);
            synchronized (methodCache){
                methodCache.put(setter,getter);
            }
            return getter;
        }catch (NoSuchMethodException e){
            return null;
        }

    }

}

DelegatingIntroductionInterceptor 是个什么类呢?

它最终还是个advice。

advice需要由advisor来封装,我们再建一个advisor:

package com.ocean.testaop.aopfinal;

import org.springframework.aop.support.DefaultIntroductionAdvisor;
//创建advisor
public class IsModifiedAdvisor extends DefaultIntroductionAdvisor {

    public IsModifiedAdvisor(){
        super(new IsModifiedMixin());
    }
}

每次创建一个advisor实例,就会有一个mixin实例。

现在我们测试:

package com.ocean.testaop.aopfinal;

import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.framework.ProxyFactory;

public class IntroductionDemo {
    public static void main(String[] args) {
        Contact target = new Contact();
        target.setName("Ocean");

        IntroductionAdvisor advisor = new IsModifiedAdvisor();

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(advisor);
        proxyFactory.setOptimize(true);

        Contact proxy = (Contact) proxyFactory.getProxy();
        IsModified proxyInterface = (IsModified) proxy;

        System.out.println("--------------------check the original information---------------------------------------");
        System.out.println("Is this name an instance of Contact? ---->" + (proxy instanceof Contact));
        System.out.println("Is this name an instance of IsModified?---->" + (proxy instanceof IsModified));
        System.out.println("Has the name 'Ocean' been modified yet?---->" + proxyInterface.isModified());

        System.out.println("--------------------set the same name----------------------------------------");
        proxy.setName("Ocean");
        System.out.println("Has the name 'Ocean' been modified yet?---->" + proxyInterface.isModified());

        System.out.println("--------------------modify the name----------------------------------------");
        proxy.setName("Caliente");
        System.out.println("Has the name 'Ocean' been modified yet?---->" + proxyInterface.isModified());




    }
}

代码proxyFactory.setOptimize(true);要求强制使用cglib代理。

使用AspectJ

所以,Spring纯程序的AOP还是挺麻烦的。因为AspectJ语法的简介,所以spring在官方文档中也有详细的解说。

我们用注解的方式来了解AspectJ。

我们有一个目标类:

package com.ocean.testaop.aopfinal;

import org.springframework.stereotype.Component;

@Component
public class Math {
    public int divide(int i, int j){
        System.out.println("Math's divide running");
        System.out.println("----------------------------------------");
        return i/j;
    }
}

里面做一个除法。

然后我们有一个切面(aspect):

package com.ocean.testaop.aopfinal;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
public class MathAspect {

    @Pointcut("execution(* com.ocean.testaop.aopfinal..*.divide*(..))")
    public void myPointcut(){}

    @Before("myPointcut()")
    public void beforeMethod(JoinPoint joinPoint){
        System.out.println("before the target method---->" + joinPoint.getSignature().getName());
        System.out.println("---------------------------------------");
    }

    @After("myPointcut()")
    public void afterMethod(JoinPoint joinPoint){
        System.out.println("after the target method------>" + joinPoint.getSignature().getName() + " ,and the arguments are"
        + Arrays.asList(joinPoint.getArgs()));
        System.out.println("-----------------------------------------");
    }

    @AfterReturning(value = "myPointcut()",returning = "value")
    public void afterReturningMethod(JoinPoint joinPoint,int value){
        System.out.println("method------->" + joinPoint.getSignature().getName() + " completes, and the return value is "
        + value);
        System.out.println("-------------------------------------------");
    }

    @AfterThrowing(value = "myPointcut()",throwing = "ex")
    public void afterThrowingMethod(Exception ex){
        System.out.println("Exception caught: " + ex.getMessage());
        System.out.println("-------------------------------------------");
    }
}

前面说过,spring有6种通知,以上切面展现了4种。

注意这里有个切点(pointcut)的概念,切点就是目标方法和通知的连接点。

"execution(* com.ocean.testaop.aopfinal..*.divide*(..))"

就是aspectJ的表达式。第一个*表示任意返回值,那是什么方法的任意返回值呢?是com.ocean.testaop.aopfinal包下面的任意类的以divide开头的方法,方法的参数同样是任意个。

然后写一个配置类来扫描包:

package com.ocean.testaop.aopfinal;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = "com.ocean.testaop.aopfinal")
@EnableAspectJAutoProxy
public class AOPConfig {
}

对于@EnableAspectJAutoProxy我们等下会详细说。

最后进行测试:

package com.ocean.testaop.aopfinal;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AnnotationAspectJTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AOPConfig.class);

        Math math = applicationContext.getBean(Math.class);

        math.divide(1,1);


    }
}

当然,这属于正常返回的情况:

如果我们把除数换成0,就会回调afterThrowingMethod方法:

package com.ocean.testaop.aopfinal;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AnnotationAspectJTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AOPConfig.class);

        Math math = applicationContext.getBean(Math.class);

        math.divide(1,0);


    }
}


这样很简单吧。

我们将这个代码逻辑还原成spring程序代码看看:

那就写一个环绕通知,这样就可以看到通知的全貌了。

我们创建一个拦截器:

package com.ocean.testaop.aopfinal;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class DivisionInterceptor implements MethodInterceptor {


    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("look at the problem");

        Object retVal = methodInvocation.proceed();

        System.out.println("problem is solved");

        return retVal;
    }
}

这里其实就完成了前后置增强。

然后就是使用ApsectJ切点:

package com.ocean.testaop.aopfinal;

import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class AspectJPointcut {
    public static void main(String[] args) {
        Math target = new Math();

        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* com.ocean.testaop.aopfinal..*.divide*(..)))");
        Advisor advisor = new DefaultPointcutAdvisor(pointcut,new DivisionInterceptor());

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(advisor);

        Math proxy = (Math) proxyFactory.getProxy();

        proxy.divide(1,1);
    }
}


我们也完成了around advice的功能,不过代码量还是很大。

springAOP原理

原对象何时变为代理对象?

我们都知道,aop使用了代理模式,那么,目标对象究竟是怎样被代理的呢?

还是使用Math类的例子。

我们打个断点:

getBean之后,拿到的Math是这样的:

这已经不是原生对象了,它在容器中就是一个代理对象。

如果我们不在切面类的上面加上注解@Aspect呢?



最后拿到的Math还是原来的对象。

好,现在我们要走一边bean的生命周期,以期知道原生对象是在哪里被代理的,以及关键的组件是什么?


此时,原生对象还未产生:

当我们跑完final Object bean = instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null;

Math对象就从instanceWrapper中拿出来了。


好,那么它是在哪个地方被代理的呢?


我们发现执行beanInstance = this.doCreateBean(beanName, mbdToUse, args);时,原对象就变成代理对象了,所以代理的过程还是发生在doCreateBean这个方法中。

在原生对象产生后,原对象中途给了exposedObject:Object exposedObject = bean;

所以我们需要关注exposedObject什么时候改变。

它是在初始化bean方法调用的时候改变的:exposedObject = this.initializeBean(beanName, exposedObject, mbd);

因此我们走进初始化代码。

初始化代码里也是把bean给了另一个变量保存:Object wrappedBean = bean;

这时我们就要关注wrappedBean。

当调用后置处理器后:wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

原生对象就变成代理对象了。

所以,是后置处理器的问题。

我们知道,在bean被放入单例池之前,后置处理器可以对bean进行修改。

谁主导了代理的工作?

现在我们暂停一下,来关注@EnableAspectJAutoProxy这个注解。因为加了它之后,AspectJ才起作用。


这个注解上方有Import注解,它的作用是给容器中自定义注入组件。

举个例子,比如我现在随便写一个类:

package com.ocean.testaop.aopfinal;

import org.springframework.beans.factory.annotation.Value;



public class NobodyClass {

    @Value(value = "Ocean")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

它上面我没有加@Component注解,我们要手动给它加到容器:

package com.ocean.testaop.aopfinal;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;

@Component
public class SelfDefiniedImportClass implements ImportBeanDefinitionRegistrar{
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(NobodyClass.class);
        registry.registerBeanDefinition("nobody",beanDefinition);

    }
}

现在我们使用@Import注解:

package com.ocean.testaop.aopfinal;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;

@Configuration
@ComponentScan(basePackages = "com.ocean.testaop.aopfinal")
@EnableAspectJAutoProxy
@Import(SelfDefiniedImportClass.class)
public class AOPConfig {
}

测试:

package com.ocean.testaop.aopfinal;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AnnotationAspectJTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AOPConfig.class);

//        Math math = applicationContext.getBean(Math.class);
//
//        math.divide(1,1);

        NobodyClass nobody = (NobodyClass) applicationContext.getBean("nobody");
        System.out.println(nobody.getName());

    }
}

最后得到:

所以,想容器中注册组件是成功的。

现在的问题是,为什么要向容器中注册AspectJAutoProxyRegistrar呢?

我们在这个类里面的一个方法中打个断点:


首先他问有没有这样一个名字的组件:

org.springframework.aop.config.internalAutoProxyCreator

(我用的是spring4.3.18版本,你用其他版本和我看到的源码可能不太一样,你可能看到一个常量,不过意思是一样的。)

当然是没有的:

接下类他就把rootBeanDefinition给new出来:

参数cls是传进来的:

它的类型是AnnotationAwareAspectJCreator。

所以,最后注册的组件就是AnnotationAwareAspectJCreator,名称是internalAutoProxyCreator。

接下来的问题是:AnnotationAwareAspectJCreator是谁?

实线表示extends,虚线表示implements。

最后,我们知道,AnnotationAwareAspectJCreator既是一个PostProcessor,又是一个Aware。


我们给继承树上所有与PostProcessor和Aware有关的逻辑打上断点,然后了解AnnotationAwareAspectJCreator是如何创建的:


refresh的时候,停在了这里,注释说:注册拦截bean创建的后置处理器。


调用后置处理器注册代理的registerBeanPostProcessors方法。


他说要注册实现了Ordered接口的后置处理器。

当然,我们的AnnotationAwareAspectJCreator是实现Ordered接口的,所以进来了。

现在要去getBean。

走下去。


调doGetBean。

getSingleton方法:public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)


在getSingleton方法中,调用singletonFactory的getObject方法。


此时传进来的的singletonFactory是一个匿名内部类。

AbstractBeanFactory是一个抽象类,实现了ObjectFactory接口。


createBean。


doCreateBean。


创建bean。


初始化bean。


初始化逻辑里面,有一个invokeAwareMethods(beanName, bean)

我们的AnnotationAwareAspectJCreator既是一个BeanClassLoaderAware,又是一个BeanFactoryAware。

到了AbstractAdvisorAutoProxyCreator类的setBeanFactory方法了。


先调用父类AbstractAutoProxyCreator的setBeanFactory方法。


最后来到了AnnotationAwareAspectJCreator的initBeanFactory方法。


如此一来,invokeAwareMethods(beanName, bean)的逻辑就走完了。


接下来就给bean包装一下,调用初始化方法前的后置处理器,调用初始化方法,然后调用初始化方法后的后置处理器。

最后返回出去。


把BeanPostProcessor排个序注册好:



加进beanFactory中。


至此,后置处理器注册完了。

那么他是怎么工作的呢?


我们debug接着跑。

判断“math”是否在advsedBeans中。并不在,advisedBeans是个空集合。


判断它是不是基础类型?即是不是Advice或Pointcut或Advisor或AopInfrastructureBean类型的。当然不是。

走进这个方法:

它又会问不是不个Aspect:

即有没有Aspect注解。

然后判断是否要跳过:


它列出了我们写的所有通知。

最终调用父类的shouldSkip方法返回false。


好,postProcessBeforeInstantiation结束了,这是Math实例创建之前做的工作。

下面是重要的 postProcessAfterInitialization。这是初始化后的工作。


我们进入wrapIfNecessary(bean, beanName, cacheKey);方法。


里面有一个拿到所有通知和顾问的方法,进去:


他要拿到所有可用的advisor。然后给advisor排个序。



因为specificInterceptors != DO_NOT_PROXY,所以进入if,把当前bean即“math”保存到advisedBean中。


重点来了,他这里就要创建代理了:



最后的重任就落在了我们的ObjenesisCglibAopProxy或者是JdkDynamicAopProxy身上了。


这里的Math对象就被改变了。这样我们和之前的逻辑就接上了。

、
在applyBeanPostProcessorsAfterInitialization中,他会遍历所有的后置处理器,当用到AnnotationAwareAspectJCreator时math对象改变了:

拦截器

我们在目标方法上打一个断点并进去:



直接来到了CglibAopProxy的intercept方法。

他这里要获取所有的interceptors:


再进去:



他要遍历所有的advisor,如果是 PointcutAdvisor类型的advisor,就进if,如果是IntroductionAdvisor类型的advisor,就进else。

我们没有引入,所以就进if。

那就遍历吧,走一次:

当前的advisor:



然后给出这个advisor,我们要拿一个interceptor。

进去:


他先通过advisor拿到advice,我们的advice是AspectJAfterThrowingAdvice,它是MethodInterceptor类型的,所以就加入interceptors数组当中。

后面他又搞了三个adapter:

利用这些adpter来拿interceptor,当然,我们已经拿到了,所以就不必辛苦他了。

最后返回出去:


走第二个advisor,它是作用在afterReturningMethod上的。

但它就不是MethodInterceptor类型的了,这时候他就要借助于适配器来拿interceptor了。



拿到拦截器链之后,要new一个CglibMethodInvocation。


调用该对象的proceed方法:



currentInterceptorIndex默认为-1。

也就是说,当this.interceptorsAndDynamicMethodMatchers.size()为0的时候,会进第一个if。



这是在调用目标方法。

我们先进else:


拿到第一个interceptor:


这个是默认的interceptor,我们自己写了另外四个。


调用invoke方法。这里的this就是CiglibMethodInvocation:
在这里插入图片描述

他的invocation是个ThreadLocal:

他要把我们的MethodInvocation即CglibMethodInvocation加入当前线程共享。

然后再调用proceed方法,进去:

又来这里了(我把源码换了一下,原来的源码proceed方法进不去):


currentInterceptorIndex变成了1,于是我们拿出第二个拦截器:



又调invoke,进去:

又是proceed。


接着取拦截器。

一直调过去,最后来到了MethodBeforeAdviceInterceptor:


控制台有输出信息了:

再走proceed:


有趣的事情发生了,currentInterceptorIndex此时为4,正好是拦截器list长度5减1。

我们要进if,即调用目标方法,然后return。


一路return,直到执行后置通知。


然后是返回通知。


然后是异常通知,没异常就不catch了。

最后到了默认的ExposedInvocationInterceptor:


这就结束了。

CGLIB与JDK代理的性能

JDK只能生成接口的代理,而CGLIB代理既可以代理接口,也可以代理类。JDK生成的代理与目标对象实现了同一接口。

CGLIB会为目标类生成一个新的代理类,这个代理类继承了目标类。

CGLIB自己有固定的通知链,我们在上面已经看到了。

可以使用setInterfaces()方法强制使用JDK代理。

可以使用setOptimize(true)强制使用CGLIB代理。

我们简单测试一下CGLIB标准代理,CBLIG冻结通知链的情况下的代理,以及JDK动态代理之间的性能差异。

接口:

package com.ocean.testaop.aopfinal;

public interface SimpleBean
{
    void advised();
    void unadvised();
}

一个被通知方法,一个未被通知方法。

实现类:

package com.ocean.testaop.aopfinal;

public class DefaultSimpleBean implements SimpleBean {
   private long dummy = 0L;
    @Override
    public void advised() {
        dummy = System.currentTimeMillis();
    }

    @Override
    public void unadvised() {
        dummy = System.currentTimeMillis();
    }
}

不管是被通知方法,还是未被通知方法,都简单地算个时间戳。

beforeadvice:

package com.ocean.testaop.aopfinal;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class NoOperationBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {

    }
}

连接点:

package com.ocean.testaop.aopfinal;

import org.springframework.aop.support.StaticMethodMatcherPointcut;

import java.lang.reflect.Method;

public class TestPointCut extends StaticMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return "advised".equals(method.getName());
    }
}

拦截所有的被通知方法。

测试:

package com.ocean.testaop.aopfinal;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class TestProxy {
    public static void main(String[] args) {
        SimpleBean target = new DefaultSimpleBean();

        Advisor advisor = new DefaultPointcutAdvisor(new TestPointCut(),new NoOperationBeforeAdvice());

        runCGLIBTest(advisor, target);
        runCGLIBFrozenTest(advisor,target);
        runJDKTest(advisor,target);
    }

    private static void runCGLIBTest(Advisor advisor,SimpleBean target){
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setProxyTargetClass(true);
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(advisor);

        SimpleBean proxy = (SimpleBean) proxyFactory.getProxy();
        System.out.println("--------------------------RUN CGLIB TEST(STANDARD)------------------------------------------");
        test(proxy);
    }

    private static void runCGLIBFrozenTest(Advisor advisor,SimpleBean target){
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setProxyTargetClass(true);
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(advisor);
        proxyFactory.setFrozen(true);


        SimpleBean proxy = (SimpleBean) proxyFactory.getProxy();
        System.out.println("--------------------------RUN CGLIB TEST(FROZEN)------------------------------------------");
        test(proxy);

    }

    private static void runJDKTest(Advisor advisor,SimpleBean target){
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(advisor);
        proxyFactory.setInterfaces(new Class[]{SimpleBean.class});

        SimpleBean proxy = (SimpleBean) proxyFactory.getProxy();
        System.out.println("--------------------------RUN JDK TEST------------------------------------------");
        test(proxy);

    }

    private static void test(SimpleBean proxy){
        long before = 0;
        long after = 0;

        System.out.println("***********test advised method*************");
        before = System.currentTimeMillis();
        for(int i = 0; i < 500000; i++){
            proxy.advised();
        }
        after = System.currentTimeMillis();
        System.out.println("Took " + (after-before) + "ms");

        System.out.println();

        System.out.println("***********test unadvised method*************");
        before = System.currentTimeMillis();
        for(int i = 0; i < 500000; i++){
            proxy.unadvised();
        }
        after = System.currentTimeMillis();
        System.out.println("Took " + (after-before) + "ms");

        System.out.println();

        System.out.println("***********test equal() method*************");
        before = System.currentTimeMillis();
        for(int i = 0; i < 500000; i++){
            proxy.equals(proxy);
        }
        after = System.currentTimeMillis();
        System.out.println("Took " + (after-before) + "ms");

        System.out.println();

        System.out.println("***********test hashcode() method*************");
        before = System.currentTimeMillis();
        for(int i = 0; i < 500000; i++){
            proxy.hashCode();
        }
        after = System.currentTimeMillis();
        System.out.println("Took " + (after-before) + "ms");

        System.out.println();

        System.out.println("***********test Advised.getProxyTargetClass() method*************");
        Advised advised = (Advised) proxy;
        before = System.currentTimeMillis();
        for(int i = 0; i < 500000; i++){
            advised.getTargetClass();
        }
        after = System.currentTimeMillis();
        System.out.println("Took " + (after-before) + "ms");




    }
}


--------------------------RUN CGLIB TEST(STANDARD)------------------------------------------
***********test advised method*************
Took 596ms

***********test unadvised method*************
Took 246ms

***********test equal() method*************
Took 137ms

***********test hashcode() method*************
Took 56ms

***********test Advised.getProxyTargetClass() method*************
Took 18ms
--------------------------RUN CGLIB TEST(FROZEN)------------------------------------------
***********test advised method*************
Took 275ms

***********test unadvised method*************
Took 27ms

***********test equal() method*************
Took 23ms

***********test hashcode() method*************
Took 264ms

***********test Advised.getProxyTargetClass() method*************
Took 31ms
--------------------------RUN JDK TEST------------------------------------------
***********test advised method*************
Took 491ms

***********test unadvised method*************
Took 208ms

***********test equal() method*************
Took 238ms

***********test hashcode() method*************
Took 106ms

***********test Advised.getProxyTargetClass() method*************
Took 94ms

Process finished with exit code 0

CGLIB的性能要好于JDK。调用被通知方法时,冻结通知链的CBLIG代理显然更好。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!