Spring-AOP

前言

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

 

术语

Jointpoint

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

 

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容许拦截全部类和方法。ide

 

基于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两个核心接口,让使用者很是方便的控制须要拦截的内容以及拦截后的逻辑。spa

 

参考

版本 4.3.10.RELEASE翻译

相关文章
相关标签/搜索