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

相关推荐
哎呦没几秒前
SpringBoot框架下的资产管理自动化
java·spring boot·后端
2401_857600953 分钟前
SpringBoot框架的企业资产管理自动化
spring boot·后端·自动化
NiNg_1_2344 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk6 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*6 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue6 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man6 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer087 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源