🌐 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。

相关推荐
我命由我123452 小时前
35.Java线程池(线程池概述、线程池的架构、线程池的种类与创建、线程池的底层原理、线程池的工作流程、线程池的拒绝策略、自定义线程池)
java·服务器·开发语言·jvm·后端·架构·java-ee
whoarethenext5 小时前
qt的基本使用
开发语言·c++·后端·qt
草捏子8 小时前
主从延迟导致数据读不到?手把手教你架构级解决方案
后端
橘猫云计算机设计9 小时前
基于Python电影数据的实时分析可视化系统(源码+lw+部署文档+讲解),源码可白嫖!
数据库·后端·python·信息可视化·小程序·毕业设计
Yolo@~9 小时前
SpringBoot无法访问静态资源文件CSS、Js问题
java·spring boot·后端
大鸡腿同学10 小时前
资源背后的成事密码
后端
Asthenia041210 小时前
使用 Spring Cloud Gateway 实现四种限流方案:固定窗口、滑动窗口、令牌桶与漏桶
后端
老李不敲代码11 小时前
榕壹云门店管理系统:基于Spring Boot+Mysql+UniApp的智慧解决方案
spring boot·后端·mysql·微信小程序·小程序·uni-app·软件需求
海风极客11 小时前
Go小技巧&易错点100例(二十五)
开发语言·后端·golang
喵手11 小时前
如何使用 Spring Boot 实现分页和排序?
数据库·spring boot·后端