• 【探索Spring底层】10.Spring选择代理


    【探索Spring底层】Spring选择代理

    1. 前言回顾

    在前面的【探索Spring底层】7.AOP增强有提到AOP的proxy增强有两种,分别是

    • jdk
    • cglib

    简单阐述一下两种的特点

    • jdk:要求目标必须实现接口,并且jdk代理和目标是平级关系
    • cglib:不要求目标实现接口,cglib代理和目标是父子关系

    具体底层原理请看【探索Spring底层】8.手撕jdk代理原理与cglib代理原理

    既然这里有两种方式,Spring是如何选择的呢?

    下面一一道来


    2. 切面的概念

    切面分为两种:

    • aspect = 通知1(advice) + 切点1(pointcut)

    • advisor = 更细粒度的切面,包含一个通知和切点

    顾问(Advisor)是 Spring 提供的另一种切面。其可以完成更为复杂的切面织入功能

    这里会就Advisor介绍Spring的选择代理

    而且aspect在生效之前会被拆解成多个advisor


    3. Spring选择代理

    这里创建一个切点,切点的表达式为execution(* foo())

    然后新建一个通知,使用invocation的proceed调用目标原方法

    新建一个DefaultPointcutAdvisor默认切面对象,构建参数为切点和切面

    一切准备好之后,就可以进行创建代理了

    需要新建一个目标对象

    然后借助ProxyFactory生成代理,ProxyFactory就会选择使用jdk还是cglib生成代理

    需要给ProxyFactory设置一个目标对象,和切面(包含了切点和通知)

    利用ProxyFactory的getProxy就可以生成一个代理对象了

    代理对象中的方法就是增强后的方法

    public class A15 {
        public static void main(String[] args) {
    
    
            // 1. 备好切点
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            // 2. 备好通知
            MethodInterceptor advice = invocation -> {
                System.out.println("before...");
                Object result = invocation.proceed(); // 调用目标
                System.out.println("after...");
                return result;
            };
            // 3. 备好切面
            DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
    
    		//4.创建代理
            Target1 target = new Target1();
            ProxyFactory factory = new ProxyFactory();
            factory.setTarget(target);
            factory.addAdvisor(advisor);
            I1 proxy = (I1) factory.getProxy();
            System.out.println(proxy.getClass());
            proxy.foo();
            proxy.bar();
        }
    
        interface I1 {
            void foo();
    
            void bar();
        }
    
        static class Target1 implements I1 {
            public void foo() {
                System.out.println("target1 foo");
            }
    
            public void bar() {
                System.out.println("target1 bar");
            }
        }
    
        static class Target2 {
            public void foo() {
                System.out.println("target2 foo");
            }
    
            public void bar() {
                System.out.println("target2 bar");
            }
        }
    }
    

    这里就是advisor的执行流程

    重点还是没有提到,到底怎么选择哪种代理规则的呢?

    这里看一下ProxyConfig的源码

    public class ProxyConfig implements Serializable {
        private static final long serialVersionUID = -8409359707199703185L;
        private boolean proxyTargetClass = false;
        private boolean optimize = false;
        boolean opaque = false;
        boolean exposeProxy = false;
        private boolean frozen = false;
    	.....
    }
    

    其他乱七八糟的就不看了,主要看看成员变量的proxyTargetClass,这个十分重要

    这个成员变量会影响到Spring选择的代理规则

    • proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
    • proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
    • proxyTargetClass = true, 总是使用 cglib 实现

    上面的程序默认使用的是cglib

    在这里插入图片描述

    只要给ProxyFactory设置一个接口的话,就会自动选择成jdk

    同理如果没有设置接口的话,使用的就是cglib

    factory.setInterfaces(target.getClass().getInterfaces());
    

    在这里插入图片描述

    ​ 如果proxyTargetClass设置为true,则总是使用cglib

    factory.setProxyTargetClass(true);
    

    在这里插入图片描述


  • 相关阅读:
    OpenGL光照之基础光照
    2022年电工杯数学建模B题5G网络环境下应急物资配送问题求解全过程论文及程序
    IDEA 快捷键记录【个人向】
    图解Kafka架构学习笔记(一)
    线程安全(上)
    SPA项目开发之CRUD+表单验证
    九州未来入选“AIGC算力产业全景图”
    C语言基础篇 —— 4.1 管理内存:栈(stack)、堆(heap)、数据区(.data)
    QImage
    【unity】ComputeShader的学习使用
  • 原文地址:https://blog.csdn.net/weixin_51146329/article/details/127102077