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

相关推荐
小突突突2 小时前
模拟实现Java中的计时器
java·开发语言·后端·java-ee
web137656076432 小时前
Scala的宝藏库:探索常用的第三方库及其应用
开发语言·后端·scala
闲猫2 小时前
go 反射 interface{} 判断类型 获取值 设置值 指针才可以设置值
开发语言·后端·golang·反射
LUCIAZZZ3 小时前
EasyExcel快速入门
java·数据库·后端·mysql·spring·spring cloud·easyexcel
Asthenia04123 小时前
依托IOC容器提供的Bean生命周期,我们能在Bean中做些什么?又能测些什么?
后端
Ase5gqe3 小时前
Spring中的IOC详解
java·后端·spring
小万编程3 小时前
基于SpringBoot+Vue奖学金评比系统(高质量源码,可定制,提供文档,免费部署到本地)
java·spring boot·后端·毕业设计·计算机毕业设计·项目源码
南雨北斗4 小时前
ThinkPHP6控制器方法返回的 Content-Type类型
后端
CryptoRzz4 小时前
springboot接入方式对接股票数据源API接口
后端
Json_4 小时前
4. 使用springboot做一个音乐播放器软件项目【数据库表的创建】
java·后端·产品