Spring-AOP

前言

AOP全称是Aspect-Oriented Programming,对应到编程术语中就是面向切面的编程。简单来说就是执行一段代码之前以及之后分别插入另一段代码,从而实现对这段代码全方位监视。

 

术语

Jointpoint

连接点,抽象统一了method,constructor,field。比如最常用的Method,对应到类就是`ReflectiveMethodInvocation`,可以拦截方法调用,并添加自定义的advice。这个概念是aop内部封装的过程中用到,使用者不需要直接接触。

 

Pointcut

切入点,其实就是一个过滤器,其子类衍生出静态的切入点,动态切入点,基于表达式的切入点等。本质就是通过对类,方法,输入参数的解析,判断哪些是需要进行拦截的,哪些是可以被过滤的。

 

Advice

通知,或者增强,这两种翻译都可以强行解释,是为了实现拦截Jointpoint后具体的逻辑,比如常见的MethodInterceptor,对方法进行拦截。这个接口是使用者最关心的,直接关系到业务逻辑。

 

核心模块

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
    </dependency>

  

实现思路

spring支持两种动态代理方案,1种是基于jdk的动态代理,第2种是基于cglib的动态代理。详细参考`DefaultAopProxyFactory`,spring基于3个条件来选择哪一种动态代理方案,只要符合其中一个条件spring就会采用基于cglib的方案。1.是否需要优化(基于cglib的动态代理方案执行性能比JDK的高);2.是否声明了代理对象是类(JDK不支持对类的动态代理,而cglib支持);3.没有代理接口声明;所有配置可以通过`org.springframework.aop.framework.ProxyFactory`配置。用户注册的adivce先被缓存在列表中(Interceptor是Advice接口的子接口),如果没有显示声明则会被封装成`DefaultPointcutAdvisor`,这里就涉及到Pointcut这个术语,默认的Pointcut允许拦截所有类和方法。

 

基于JDK的动态代理

核心类是`JDKDynamicAopProxy`,首先根据配置选择型的将spring自己的3个接口加入到代理接口,然后自己实现了JDK中的`InvocationHandler`接口。当JDK的动态代理回调`InvocationHandler`的时候,先判断是否是spring自己增加的几个接口,如果是则通过反射调用直接返回,如果不是则通过执行`JoinPoint`的子类`ReflectiveMethodInvocation`,回调第一个advice,之后的advice通过使用者自己触发调用,这样形成了一个调用链。需要注意的是,spring为了提高效率,额外拦截了hashcode和equals方法直接处理,也不会进入advice的拦截处理流程。假如我们有两个advice,那么实际的执行顺序为

advice1.before -> advice2.before -> poxy.process-> advice2.after -> advice1.after

详细demo参考

public class AopTest {

    public static void main(String[] args){
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        test();
    }

    public static void test(){
        // 基于对类的代理
        ProxyFactory proxyFactory = new ProxyFactory();
        // target 表示被真正执行代理的实例
        proxyFactory.setTarget(new AopTester());
        proxyFactory.setInterfaces(AopInterface.class);
        // advice 可以对调用进行拦截从而实现aop
        proxyFactory.addAdvice(new MyInterceptor1());
        proxyFactory.addAdvice(new MyInterceptor1());
        AopInterface aopTester = (AopInterface) proxyFactory.getProxy();
        System.out.println(aopTester.run("test"));
    }

    /**
     * 被代理的接口
     */
    public static interface AopInterface{
        public String run(String s);
    }

    /**
     * 被代理的类
     */
    public static class AopTester implements AopInterface{
        @Override
        public String run(String s){
            return s+" ing ...";
        }
    }

    /**
     * 拦截器1,也是advice,提供通知
     */
    public static class MyInterceptor1 implements MethodInterceptor{
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("1 before");
            Object obj = invocation.proceed();
            System.out.println(obj);
            System.out.println("1 after");
            return "1 "+obj;
        }
    }

    /**
     * 拦截器2,也是advice,提供通知
     */
    public static class MyInterceptor2 implements MethodInterceptor{
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("2 before");
            Object obj = invocation.proceed();
            System.out.println(obj);
            System.out.println("2 after");
            return "2 "+obj;
        }
    }
}

 

总结

spring的aop以动态代理为技术基础,结合JDK和cglib的proxy方案,抽象出Pointcut和Advice两个核心接口,让使用者非常方便的控制需要拦截的内容以及拦截后的逻辑。

 

参考

版本 4.3.10.RELEASE

 

posted @ 2018-10-29 20:38  ulysses_you  阅读(...)  评论(... 编辑 收藏