代理模式

  • 前言
  • 一、代理模式是什么?
  • 二、静态代理
  • 三、动态代理
    • 1.jdk动态代理(接口代理)
    • 2.cglib动态代理

前言


一、代理模式是什么?

     代理模式是常见的设计模式之一,顾名思义,代理模式就是代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理。(为真实对象提供代理,然后供其他对象通过代理访问真实对象)

二、静态代理

    以租房为例,租客找房东租房,然后中间经过房屋中介,以此为背景,它的UML图如下:

可以看出房东类(RentHouse)和代理类(IntermediaryProxy)都实现了租房接口,这就是一个静态代理的前提,那就是真实类和代理类要实现同一个接口,在代理类中实现真实类的方法同时可以进行真实类方法的增强处理,在一个代理类中就可以完成对多个真实对象的注入工作。代码如下:

public interface IRentHouse {    void rentHouse();}
public class RentHouse implements IRentHouse {    @Override    public void rentHouse() {        System.out.println("实现租房");    }}
public class IntermediaryProxy implements IRentHouse {    private IRentHouse iRent;    public IntermediaryProxy(IRentHouse iRentHouse) {        iRent=iRentHouse;    }    @Override    public void rentHouse() {        System.out.println("交中介费");        iRent.rentHouse();        System.out.println("中介负责维修管理");    }}
//client测试类public class TestStaticProxy {    public static void main(String[] args) {        //定义租房        IRentHouse iRentHouse = new RentHouse();        //定义中介        IRentHouse intermediaryProxy = new IntermediaryProxy(iRentHouse);        //中介租房        intermediaryProxy.rentHouse();    }}

三、动态代理

    从静态代理的代码中可以发现,静态代理的缺点显而易见,那就是当真实类的方法越来越多的时候,这样构建的代理类的代码量是非常大的,所以就引进动态代理.

动态代理允许使用一种方法的单个类(代理类)为具有任意数量方法的任意类(真实类)的多个方法调用提供服务,看到这句话,可以容易的联想到动态代理的实现与反射密不可分。
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。

1.jdk动态代理(接口代理)

    Jdk代理涉及到java.lang.reflect包中的InvocationHandler接口和Proxy类,核心方法是

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 

jdk动态代理过程中实际上代理的是接口,是因为在创建代理实例的时候,依赖的是java.lang.reflect包中Proxy类的newProxyInstance方法,该方法的生效就恰恰需要这个参数;

public static Object newProxyInstance(ClassLoader loader,                                          Class<" />


总结对比:
1.静态代理中,代理类和真实类实现的是同一个接口,重写同样的方法;jdk动态代理中,代理类和真实类关系不大,代理类实现无侵入式的代码扩展。
2.静态代理中当接口中方法增加的时候,在代理类代码量也会增加,显然是不妥的;jdk动态代理解决了这个问题,当业务增加,代理类的代码不会增加。
3.jdk动态代理实现的是jdk自带InvocationHandler接口,实现了这个接口的类也叫拦截器类,也叫代理类。

2.cglib动态代理

    从上面可以看出,jdk动态代理的前提条件是,要有接口存在,那还有许多场景是没有接口的,这个时候就需要cglib动态代理了,CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。
以下案例中所用到的**被代理类**和和上面jdk动态代理一样

cglib动态代理过程中生成的是实现类的子类,cglib是如何凭空创造的实现类的子类的,下面是测试代码

//所需的代理类public class CglibProxy implements MethodInterceptor {    private Enhancer enhancer=new Enhancer();    private Object bean;    public CglibProxy(Object bean) {        this.bean = bean;    }    public Object getProxy(){        //设置需要创建子类的类        enhancer.setSuperclass(bean.getClass());        enhancer.setCallback(this);        return enhancer.create();    }    @Override    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {        String methodName = method.getName();        if (methodName.equals("wakeup")){            System.out.println("早安~~~");        }else if(methodName.equals("sleep")){            System.out.println("晚安~~~");        }        return method.invoke(bean,objects);    }}
//测试类public class TestCglibProxy {    public static void main(String[] args) {        //生成虚拟代理类的代码,本来虚拟代理子类是看不见的,        //下面这句话的作用就是把执行过程中cglib增强后的class字节码文件        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\aop");        CglibProxy proxy = new CglibProxy(new Student("张三"));        Student student = (Student) proxy.getProxy();        student.wakeup();        student.sleep();        proxy = new CglibProxy(new Doctor("王教授"));        Doctor doctor = (Doctor) proxy.getProxy();        doctor.wakeup();        doctor.sleep();        proxy = new CglibProxy(new Dog("旺旺"));        Dog dog = (Dog) proxy.getProxy();        dog.wakeup();        dog.sleep();        proxy = new CglibProxy(new Cat("咪咪"));        Cat cat = (Cat) proxy.getProxy();        cat.wakeup();        cat.sleep();    }}

附上代理Cat类时候,生成的Cat的子类:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//public class Cat$$EnhancerByCGLIB$$8ca2de8b extends Cat implements Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$sleep$0$Method; private static final MethodProxy CGLIB$sleep$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$wakeup$1$Method; private static final MethodProxy CGLIB$wakeup$1$Proxy; private static final Method CGLIB$equals$2$Method; private static final MethodProxy CGLIB$equals$2$Proxy; private static final Method CGLIB$toString$3$Method; private static final MethodProxy CGLIB$toString$3$Proxy; private static final Method CGLIB$hashCode$4$Method; private static final MethodProxy CGLIB$hashCode$4$Proxy; private static final Method CGLIB$clone$5$Method; private static final MethodProxy CGLIB$clone$5$Proxy; static void CGLIB$STATICHOOK4() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("com.example.demo.cglibproxy.vo.Cat$$EnhancerByCGLIB$$8ca2de8b"); Class var1; Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$equals$2$Method = var10000[0]; CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2"); CGLIB$toString$3$Method = var10000[1]; CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3"); CGLIB$hashCode$4$Method = var10000[2]; CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4"); CGLIB$clone$5$Method = var10000[3]; CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5"); var10000 = ReflectUtils.findMethods(new String[]{"sleep", "()V", "wakeup", "()V"}, (var1 = Class.forName("com.example.demo.cglibproxy.vo.Cat")).getDeclaredMethods()); CGLIB$sleep$0$Method = var10000[0]; CGLIB$sleep$0$Proxy = MethodProxy.create(var1, var0, "()V", "sleep", "CGLIB$sleep$0"); CGLIB$wakeup$1$Method = var10000[1]; CGLIB$wakeup$1$Proxy = MethodProxy.create(var1, var0, "()V", "wakeup", "CGLIB$wakeup$1"); } final void CGLIB$sleep$0() { super.sleep(); } public final void sleep() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$sleep$0$Method, CGLIB$emptyArgs, CGLIB$sleep$0$Proxy); } else { super.sleep(); } } final void CGLIB$wakeup$1() { super.wakeup(); } public final void wakeup() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$wakeup$1$Method, CGLIB$emptyArgs, CGLIB$wakeup$1$Proxy); } else { super.wakeup(); } } final boolean CGLIB$equals$2(Object var1) { return super.equals(var1); } public final boolean equals(Object var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { Object var2 = var10000.intercept(this, CGLIB$equals$2$Method, new Object[]{var1}, CGLIB$equals$2$Proxy); return var2 == null " />
总结:cglib动态代理和jdk动态代理的区别显而易见,但是实现逻辑差不多,cglib代理类是通过实现MethodInterceptor,重写intercept方法,通过生成被代理类的子类来达到代理增强代码的目的;而Jdk代理是通过实现InvocationHandler,重写invoke方法,通过生成接口的代理类来达到代码增强的目的,所以jdk动态代理的实现需要接口,cglib则不需要,spring5.0以上以及springboot2.0以上默认采用cglib动态来实现AOP。后面将专门介绍AOP以及spring中是如何实现AOP的!不当之处欢迎大家批评并指正!