zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    牛儿
    Tarjan算法
    There&nbsp;is&nbsp;no&nbsp;resul…
    Struts2+JQuery+Json登陆实例
    struts2+jquery+easyui+datagrid+j…
    Spring:JdbcTemplate使用指南
    使用Spring的jdbcTemplate进一步简…
    JDBC连接MySQL数据库及示例
    PLSQL导入/导出数据方法
    PLSQ创建用户L
  • 原文地址:https://www.cnblogs.com/ulysses-you/p/9873105.html
Copyright © 2011-2022 走看看