首页 > 其他分享 >Spring03_代理模式

Spring03_代理模式

时间:2023-04-15 15:36:21浏览次数:44  
标签:对象 Spring03 代理 接口 System 模式 println public

一、静态代理

(一)代理模式概述

​ 在不改变原始类(或叫被代理类)的情况下,通过引入代理类来给原始类附加功能。一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法来实现代理模式。

​ 我直接就是用老师举的例子

image-20230413145530118

​ 上图的三个角色:

1、真实对象(能够完成业务的):局长;

2、代理对象角色:秘书;

3、抽象角色(抽象类或者接口,真实对象和代理对象角色都继承/实现抽象角色):上图没有

public interface AbstractBean {
    /**
     * dosomething
     */
    void doSomething();
}

import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class JuzhangBean implements AbstractBean{

    private String name;

    @Override
    public void doSomething() {
        System.out.println("局长dosomething");
    }
}


public class MishuBeanTest {

    @Test
    public void testStaticProxy() {
        //真实对象
        JuzhangBean juzhangBean = new JuzhangBean().setName("王英英");
        //代理对象角色
        MishuBean mishuBean = new MishuBean(juzhangBean).setName("鼠鼠");
        //不认识但是认识秘书,所以找秘书办事,但是最后依然是局长拍板
        mishuBean.doSomething();
    }
}

image-20230413151125025

​ 下面演示一下附加功能,比如说在办事情之前代理对象(秘书)要约你一起大保健,那自然是得咱们请客,结果他找完局长之后完了之后还要去洗个脚,我自然就是不能让他白忙活。

//这里是秘书的doSomething方法
@Override
public void doSomething() {
    System.out.println("Before:保个健先");
    System.out.println("_____________");
    System.out.println("秘书dosomething");
    juzhangBean.doSomething();
    System.out.println("_____________");
    System.out.println("After:洗个脚先");
}

​ 可见代理对象可以在不修改原有类的基础上给业务额外添加功能:

image-20230413151836542

二、动态代理

(一)动态代理概述

​ 静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式 的“重复”代码,增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。我们不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。

(二)JDK 动态代理

​ 所谓jdk动态代理,是使用java反射包中的类和接口实现动态代理的功能。反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy

​ 使用 JDK 动态代理首先要创建一个 ProxyHandler 类,让他来实现 InvocationHandler 接口,实现之后会提示你重写里面的 invoke() 方法,这个 invoke 方法里面就是被代理对象需要增强的方法了。

 invoke(proxy:就是代理对象、newProxyInstance方法的返回对象, method调用的方法,args方法中的参数)

​ 定义完成之后使用 Proxy.newProxyInstance,也就是在运行期间生成代理类,其返回值是需要由被代理类所实现的接口来接收,接收到的就是产生的代理对象。

Proxy.newProxyInstance(类加载器, 目标类实现的接口, InvocationHandler对象)

​ 当代理对象被调用,会调用 InvocationHandler 实现类中的 invoke 方法来达到增强的目的。

​ 需要注意,JDK 代理只能针对接口做代理

public interface AbstractBean {
    /**
     * dosomething
     */
    void doSomething();
}


@Data
@Accessors(chain = true)
public class JuzhangBean implements AbstractBean{

    private String name;

    @Override
    public void doSomething() {
        System.out.println("局长dosomething");
    }
}

public class JdkProxy implements InvocationHandler {
    //传入真实对象
    private AbstractBean abstractBean;

    public JdkProxy(AbstractBean abstractBean) {
        this.abstractBean = abstractBean;
    }

    /**
     * 代理对象调用真实对象的时候执行的方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before真实对象执行之前");
        //真实对象的方法
        method.invoke(abstractBean,args);
        System.out.println("After真实对象执行之后");
        return null;
    }
}

​ 我尼玛这玩意是真难看懂啊

@Test
public void testJdk() {
    //真实对象
    JuzhangBean juzhangBean = new JuzhangBean().setName("局长");


    JdkProxy jdkProxy = new JdkProxy(juzhangBean);

    //创建代理类、
	//参数:ClassLoader 当前类加载器,interface代理类要实现的接口 Invocationhandler接口,不过需要被实现写增强方法
    AbstractBean proxyAbstractBean = (AbstractBean)Proxy.newProxyInstance(juzhangBean.getClass().getClassLoader(), new Class[]{AbstractBean.class}, jdkProxy);
    //调用代理对象
    proxyAbstractBean.doSomething();
}

image-20230414145615316

​ 关于为什么 JDK 代理只能代理有接口的类:JDK 动态代理会在程序运行期间生成一个叫 $Proxy0 的代理类,这个类继承自 java.lang.reflect.Proxy 并且会实现被代理类的接口,每个动态代理类都继承了一个 Proxy ,在 Java 里面又是不支持多继承的,在上面这个例子中产生的代理类大概是这样的。这玩意本来就继承自 extends 类了,就不能再继承别的类,只能去实现接口了。

public final class $Proxy0 extends Proxy implements AbstractBean{}

​ 动态代理本身就是为了对原始的实现进行拦截并做功能和拓展,在实际开发中大多是面向接口的,所以这样合情合理。

(三)cglib 动态代理

​ 上面我们分析过 JDK 动态代理只能代理有接口的类,但是有的类是没有接口的,这时候我们可以使用 cglib 动态代理。Spring 框架本身就依赖 cglib 的 jar 包。

​ 同样需要定义一个代理类 CglibProxy 实现 MethodInterceptor 接口image-20230414173415000

@Data
public class Juzhang {
    private String name;

    public void doSomething() {
        System.out.println("dosomething");
    }

    public int do1 () {
        System.out.println("do1");
        return 1000;
    }

}

public class CglibProxy implements MethodInterceptor {

    //传入真实对象
    private Juzhang juzhang;

    public CglibProxy(Juzhang juzhang) {
        this.juzhang = juzhang;
    }


    Enhancer enhancer = new Enhancer();

    /**
     * 获得代理类对象得方法
     * @return
     */
    public Juzhang getProxy() {
        enhancer.setSuperclass(juzhang.getClass());
        enhancer.setCallback(this);
        return (Juzhang)enhancer.create();
    }


    /**
     * 和 invoke 方法的作用一样
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Before");
        Object result = method.invoke(juzhang,objects);
        System.out.println("After");
        return result;
    }
}

@Test
    public void testJdk() {
        //真实对象
        JuzhangBean juzhangBean = new JuzhangBean().setName("局长");


        JdkProxy jdkProxy = new JdkProxy(juzhangBean);

        //创建代理类
        AbstractBean proxyAbstractBean = (AbstractBean) Proxy.newProxyInstance(juzhangBean.getClass().getClassLoader(), new Class[]{AbstractBean.class}, jdkProxy);
        // 调用代理对象
        proxyAbstractBean.doSomething();
    }

image-20230414173416970

(四)总结

​ JDK 动态代理实现接口,CGLIB 动态继承思想。

​ JDK 动态代理只想效率高于 cglib。

​ 如果对象有接口实现,选择 JDK 代理,没有实现接口选择 CGLIB 代理。

标签:对象,Spring03,代理,接口,System,模式,println,public
From: https://www.cnblogs.com/purearc/p/17321207.html

相关文章

  • ASEMI代理ADA4940-1ACPZ-R7原装ADI车规级ADA4940-1ACPZ-R7
    编辑:llASEMI代理ADA4940-1ACPZ-R7原装ADI车规级ADA4940-1ACPZ-R7型号:ADA4940-1ACPZ-R7品牌:ADI/亚德诺封装:LFCSP-16批号:2023+引脚数量:16安装类型:表面贴装型ADA4940-1ACPZ-R7汽车芯片ADA4940-1ACPZ-R7特征小信号带宽:260MHz超低功率1.25mA极低谐波失真−122dBTHD,50kHz时1MHz时的......
  • 03装饰者模式
    例子星巴兹是以扩张速度最快而闻名的咖啡连锁店。因为扩张速度实在太快,他们着急更新订单系统,来匹配他们的饮料供应要求。实现1---继承购买咖啡时,也可以要求其中加入各种调料,例如:蒸奶,豆浆很明显,星巴兹为自己制造了一个维护噩梦,如果牛奶的价钱上扬,怎么办?新增一种焦糖调料风味......
  • ASEMI代理ADA4940-1ACPZ-R7原装ADI车规级ADA4940-1ACPZ-R7
    编辑:llASEMI代理ADA4940-1ACPZ-R7原装ADI车规级ADA4940-1ACPZ-R7型号:ADA4940-1ACPZ-R7品牌:ADI/亚德诺封装:LFCSP-16批号:2023+引脚数量:16安装类型:表面贴装型ADA4940-1ACPZ-R7汽车芯片ADA4940-1ACPZ-R7特征小信号带宽:260MHz超低功率1.25mA极低谐波失真−122dBTHD,50k......
  • ASEMI代理AD9959BCPZ原装ADI车规级AD9959BCPZ
    编辑:llASEMI代理AD9959BCPZ原装ADI车规级AD9959BCPZ型号:AD9959BCPZ品牌:ADI/亚德诺封装:LFCSP-56批号:2023+安装类型:表面贴装型引脚数量:56类型:车规级芯片工作温度:−40°C~125°CAD9959BCPZ特征4个同步DDS通道@500MSPS之间的独立频率/相位/振幅控制通道频率/相位/振幅变化的匹配延迟......
  • ASEMI代理AD9959BCPZ原装ADI车规级AD9959BCPZ
    编辑:llASEMI代理AD9959BCPZ原装ADI车规级AD9959BCPZ型号:AD9959BCPZ品牌:ADI/亚德诺封装:LFCSP-56批号:2023+安装类型:表面贴装型引脚数量:56类型:车规级芯片工作温度:−40°C~125°CAD9959BCPZ特征4个同步DDS通道@500MSPS之间的独立频率/相位/振幅控制通道频率/相位/振幅......
  • PyQt5 软件在 macOS HiDPI 模式下出现字体模糊的问题
    ​ Retina屏幕是苹果公司在2010年在 WWDC上发布的一种高密度像素的屏幕。HiDPI是一种渲染技术,它可以让Retina屏幕上的图像更加清晰。HiDPI技术会将图像渲染成两倍于原始分辨率的大小,然后再将其缩小到原始分辨率的大小,这样就可以让图像更加清晰。PyQt5编写的软件在Wi......
  • proxyman代理使用
    下载链接https://proxyman.io/抓包过滤 篡改请求或者响应修改响应 修改请求1.选择断点再改模版 ......
  • 什么是 三维渲染内核 的 流渲染模式?有那些功能和优势?
    什么是三维渲染内核的流渲染模式?Streaming流渲染模式是将三维渲染任务放在云服务器上进行,然后将渲染结果以视频流的形式传输到客户端。在这种模式下,客户端负责接收和显示视频流,三维场景的渲染和处理任务不再依赖于客户端设备,而是由云端服务器承担。客户端设备只需要接收云端服务......
  • rust模式匹配(可驳模式匹配与不可驳模式匹配)
    rust的一个特性就是模式匹配(OOP编程语言好像都有?),模式匹配常常和变量绑定一起考虑常见的模式匹配有:match、iflet、whilelet、let、for、函数参数等等(for和let我个人认为说是模式匹配,其实有点牵强了,除非这样let(x,y)=(1,2)可驳与不可驳可驳模式匹配要求一定要穷尽所有......
  • ASEMI代理ADI亚德诺AD8210YRZ-REEL7车规级芯片
    编辑-ZAD8210YRZ-REEL7芯片参数:型号:AD8210YRZ-REEL7偏移电压(RTI):±1.0mV超温(RTI):±1.8mV差分输入阻抗:2kΩ共模输入阻抗:5MΩ共模输入电压范围:−2~+65V差分输入电压范围:250mV共模抑制:120dB输出电压范围:0.05~4.9V输出阻抗:2Ω小信号−3dB带宽:450kHz斜率:3V/µs......