• Spring AOP底层实现原理


    Spring AOP底层实现原理,使用jdk动态代理以及cglib动态代理

    Aop底层使用动态代理实现:有两种情况动态代理

    第一种:有接口情况,使用jdk动态代理

    第二种:没有接口情况,使用cglib动态代理

    1、使用jdk动态代理

    创建接口实现类的代理对象,增强类的方法

    (1)声明接口:

    public interface TargetInteface {
        void method1();
        void method2();
        int method3(Integer i);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2)创建接口的实现类:

    public class Target implements TargetInteface {
        @Override
        public void method1() {
            System.out.println("method1 running ...");
        }
    
        @Override
        public void method2() {
            System.out.println("method2 running ...");
        }
    
        @Override
        public int method3(Integer i) {
            System.out.println("method3 running ...");
            return i;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里,我们需要创建代理,从这三个方法切入,执行操作

    (4)创建代理对象(核心类)

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class TargetProxy {
        public static  <T> Object getTarget(T t) {
            return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            // proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
                            //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
                            System.out.println("执行方法前...");
                            Object invoke = method.invoke(t, args);
                            System.out.println("执行方法后...");
                            return invoke;
                }
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Proxy.newProxyInstance()三个参数:

    ClassLoader loader用来指明生成代理对象使用的类加载器;

    Class[ ] interfaces 用来指明目标类实现的所有接口;

    InvocationHandler h 用来指明产生的这个代理对象要做的事情。

    实现InvocationHandler接口后需要重写invoke方法,代理类调用的所有方法实际上都是通过invoke方法调用的目标方法,invoke方法内部实现了两个逻辑:一个是增强逻辑,一个是执行目标方法。通过method.getName()获取当前调用的是代理对象的哪个方法,如果该方法是要被增强的方法,则进行增强,否则直接执行。

    此时我们就已经创建好了代理对象:(同时呢我们也可以自定义一个类,写上我们想要切面的方法,可以自定义在执行方法前和执行方法后去调用我们想要测试的方法)

    (4)创建测试类

    public class TargetUser {
    
        public static void main(String[] args) {
            TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
            target.method1();
            System.out.println("-----------------------------");
            target.method2();
            System.out.println("-----------------------------");
            System.out.println(target.method3(3));
            System.out.println("----------------");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    (5)测试结果

    在这里插入图片描述

    此时:我们用jdk创建动态代理就ok了,主要还是利用Proxy.newProxyInstance()对象,此时需要重写Object invoke方法

    2、使用cglib动态代理

    创建子类的代理对象,增强类中的方法。

    CGLib代理不要求目标类一定要实现了接口,它采用非常底层的字节码技术,可以为一个类创建子类,从而解决无接口代理问题。

    (1)创建目标类,要增强的类

    public class Target {
        public void method1() {
            System.out.println("method1 running ...");
    }
    
        public void method2() {
            System.out.println("method2 running ...");
        }
    
        public int method3(Integer i) {
            System.out.println("method3 running ...");
            return i;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    (2)创建代理对象 (核心类)

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    
    public class TargetProxy {
    
        public static <T> Object getProxy(T t) {
            Enhancer en = new Enhancer(); //帮我们生成代理对象
            en.setSuperclass(t.getClass());//设置要代理的目标类
            en.setCallback(new MethodInterceptor() {//代理要做什么
                @Override
                public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    System.out.println("执行方法前。。。");
                    //调用原有方法  
                    Object invoke = methodProxy.invokeSuper(object, args);
                    // Object invoke = method.invoke(t, args);// 作用等同与上面。
                    System.out.println("执行方法后。。。");
                    return invoke;
                }
            });
            return en.create();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    重写:Object intercept()方法

    Enhancer en = new Enhancer(); //帮我们生成代理对象
    en.setSuperclass(t.getClass());//设置要代理的目标类
    en.setCallback(new MethodInterceptor() {//代理要做什么

    return en.create(); 返回代理对象供调用

    (3)创建测试类

    public class TargetUser {
    
        public static void main(String[] args) {
            Target target = (Target) TargetProxy.getProxy(new Target());
            System.out.println(target.getClass().getName());
            target.method1();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (4)执行结果

    在这里插入图片描述

    3、总结

    • Spring在运行期,生成动态代理对象,不需要特殊的编译器
    • Spring AOP的底层,是通过JDK动态代理 和 CGLib动态代理技术,为目标对象执行横向织入
    • Spring AOP非常智能,它可以根据目标对象是否实现了接口,自动选择使用哪种代理方式(默认使用JDK动态代理)
    • 如果目标对象实现了接口,Spring使用JDK的java.lang.reflect.Proxy类代理
    • 如果目标对象没有实现接口,Spring使用CGLib库生成目标对象的子类
    • 程序中应优先对接口创建代理,便于程序的解耦 和 维护
    • 标记为final的方法,不能被代理(因为方法被final修饰后,不能被重写)
    • JDK动态代理,是针对接口生成子类,接口中的方法不能被final修饰
    • CGLib代理,是针对目标类产生子类,因此类和方法不能使用final修饰
    • 如果目标对象没有实现接口,Spring使用CGLib库生成目标对象的子类
    • 程序中应优先对接口创建代理,便于程序的解耦 和 维护
    • 标记为final的方法,不能被代理(因为方法被final修饰后,不能被重写)
    • JDK动态代理,是针对接口生成子类,接口中的方法不能被final修饰
    • CGLib代理,是针对目标类产生子类,因此类和方法不能使用final修饰
    • Spring只支持方法的连接点,不提供属性的连接点,即Spring AOP仅对方法层面进行增强
  • 相关阅读:
    c# 容器变换
    javascript数据类型
    无人机航迹规划:五种最新智能优化算法(KOA、COA、LSO、GRO、LO)求解无人机路径规划MATLAB
    Mybatis-Plus复杂语句多级嵌套分组带分页查询
    C/C++语言100题练习计划 97——素数对
    【云原生】zookeeper + kafka on k8s 环境部署
    车险计算器微信小程序源码 带流量主功能
    HackTheBox You know 0xDiablos pwn题目
    Java进阶(一) Java高效读取大文件,占内存少
    只需三步,完美卸载Docker
  • 原文地址:https://blog.csdn.net/qq_45830276/article/details/126697005