Spring03_代理模式

一、静态代理

(一)代理模式概述

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

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

image-20230413145530118

​ 上图的三个角色:

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

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

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

copy
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

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

copy
//这里是秘书的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 方法里面就是被代理对象需要增强的方法了。

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

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

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

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

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

copy
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; } }

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

copy
@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 类了,就不能再继承别的类,只能去实现接口了。

copy
public final class $Proxy0 extends Proxy implements AbstractBean{}

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

(三)cglib 动态代理

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

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

copy
@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 代理。

posted @   Purearc  阅读(52)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
🚀