🌐 Spring中的<静态代理>、<动态代理>

1. 静态代理

  1. 名词术语

目标对象(目标类)-Target、

代理对象(代理类)-Proxy

目标方法(本质工作)

额外操作(增强方法)

  1. 静态代理的缺点

额外操作的调用和目标方法的调用混合在一起 如果业务发生变化,一定需要去对方法细节做调整

实现: 在原来的卖烧饼的方法增强一个送大麦茶的方法

1.创建ISale接口

java 复制代码
public interface ISale {
    void saleShaoBing();
    void saleJianBing();
    void saleYueBing();
    void saleManTou();
}

2.创建WuDaLang实现类:目标类

java 复制代码
public class WuDaLang implements ISale{
    @Override
    public void saleShaoBing() {
        System.out.println("卖烧饼...");
    }

    @Override
    public void saleJianBing() {
        System.out.println("卖煎饼...");
    }

    @Override
    public void saleYueBing() {
        System.out.println("卖月饼...");
    }

    @Override
    public void saleManTou() {
        System.out.println("卖馒头...");
    }
}

3.创建WuDaLangProxy实现类:代理类

java 复制代码
public class WuDaLangProxy implements ISale{

    private WuDaLang target ;

    public WuDaLangProxy(WuDaLang target){
        this.target = target;
    }

    @Override
    public void saleShaoBing() {
        target.saleShaoBing();
        song();
    }

    @Override
    public void saleJianBing() {
        target.saleJianBing();
        song();
    }

    @Override
    public void saleYueBing() {
        target.saleYueBing();
        song();
    }

    @Override
    public void saleManTou() {
        target.saleManTou();
    }

    private void song(){
        System.out.println("送大麦茶...");
    }
}

4.建立测试类测试

java 复制代码
public class ProxyTest {
    @Test
    public void test01(){
        WuDaLang target = new WuDaLang();
        ISale wuDaLang = new WuDaLangProxy(target); //new WuDaLang();
        wuDaLang.saleShaoBing();
    }
}

目标类即:武大郎类具有的方法,卖烧饼

代理类即:武大郎代理类具有的方法,买烧饼+送大麦茶

2. 动态代理(JDK)

  1. 我们发现一个规律:如果你的方法是以"Bing"结尾,我就增强(送大麦茶)

  2. JDK动态代理的特点:产生的代理对象和目标对象是兄弟关系

  3. JDK动态代理的缺点:要求目标对象必须有父接口

1.创建 ISale 接口

java 复制代码
public interface ISale {
    void saleShaoBing();
    void saleJianBing();
    void saleYueBing();
    void saleManTou();
}

2.创建WuDaLang类:目标类

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;


public class WuDaLang {//implements ISale{
    public void saleShaoBing() {
        System.out.println("卖烧饼...");
    }

    public void saleJianBing() {
        System.out.println("卖煎饼...");
    }

    public void saleYueBing() {
        System.out.println("卖月饼...");
    }

    public void saleManTou() {
        System.out.println("卖馒头...");
    }
}

3.创建WuDaLangInvocationHandler类:代理类 实现以bing结束的方法大麦茶就送

java 复制代码
public class WuDaLangInvocationHandler implements InvocationHandler {

    private Object target ;

    public WuDaLangInvocationHandler(Object target){
        this.target = target ;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //proxy:代理对象    method:目标方法    args:目标方法的实参
        //获取目标方法名
        String methodName = method.getName() ;
        if(methodName.endsWith("Bing")){
            song();
        }
        return method.invoke(target,args);
    }

    private void song(){
        System.out.println("送大麦茶...");
    }
}

4.建立测试类

java 复制代码
@Test
public void test01()throws Exception{
    WuDaLang target = new WuDaLang();
    WuDaLangInvocationHandler wuDaLangInvocationHandler = new WuDaLangInvocationHandler(target);

    //通过Proxy的静态方法newProxyInstance创建代理对象
    //这个方法有三个参数:① 类加载器。因为目标类和代理类使用的类加载器是同一级别,都是应用程序类加载器,因此我们使用Target的类加载器
    //② 目标类的父接口数组。 因为我们在使用JDK动态代理产生代理对象时,需要使用目标类的父接口作为原料,因此需要传入接口数组
    //③ 产生的代理对象的逻辑是什么,我们需要在handler中进行说明,因此需要传入InvocationHandler实例
    Object proxyObj = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),wuDaLangInvocationHandler);
    //产生的代理对象是接口的实现类,因此proxyObj可以强转为ISale类型
    //WuDaLang iSale = (WuDaLang)proxyObj;
    //iSale.saleManTou();//saleShaoBing();
    Method method = proxyObj.getClass().getDeclaredMethod("saleShaoBing");
    method.invoke(proxyObj);
}

动态代理与静态代理的区别

  • 静态代理增强的方法耦合在目标类中,动态代理增强的方法则另外处理

  • 静态代理适合态代理适合目标对象固定且数量较少的情况,动态代理则更适合目标对象数量多或者目标对象在编译期不确定的情况,它能提供更加高效和灵活的代理服务

3. 动态代理(CGLIB)

  1. 目标类不需要有接口

  2. 目标类不能被final修饰

  3. 产生的代理对象是目标类的子类

  4. 早期(JDK1.4),JDK动态代理创建代理对象的性能较高,调用代理方法的性能较差;CGLIB恰好相反。 JDK8之后,JDK的动态代理性能越来越高,CGLIB的优势已经不明显。因此在2019年8月CGLIB停止更新。 Spring团队依然在默默维护CGLIB。因此在spring环境下,仍然可以使用CGLIB。

相关推荐
凡人的AI工具箱4 小时前
AI教你学Python 第11天 : 局部变量与全局变量
开发语言·人工智能·后端·python
是店小二呀4 小时前
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
开发语言·c++·后端
canonical_entropy4 小时前
金蝶云苍穹的Extension与Nop平台的Delta的区别
后端·低代码·架构
我叫啥都行5 小时前
计算机基础知识复习9.7
运维·服务器·网络·笔记·后端
无名指的等待7125 小时前
SpringBoot中使用ElasticSearch
java·spring boot·后端
.生产的驴6 小时前
SpringBoot 消息队列RabbitMQ 消费者确认机制 失败重试机制
java·spring boot·分布式·后端·rabbitmq·java-rabbitmq
AskHarries6 小时前
Spring Boot利用dag加速Spring beans初始化
java·spring boot·后端
苹果酱05677 小时前
一文读懂SpringCLoud
java·开发语言·spring boot·后端·中间件
掐指一算乀缺钱7 小时前
SpringBoot 数据库表结构文档生成
java·数据库·spring boot·后端·spring
计算机学姐9 小时前
基于python+django+vue的影视推荐系统
开发语言·vue.js·后端·python·mysql·django·intellij-idea