设计模式之动态代理模式

设计模式之动态代理模式

  • 动态代理模式应用场景条件
    • 如何自动生成代理
    • JDK代理
    • CGLib代理

动态代理模式应用场景条件

  1. 两个角色:执行类和被代理对象
  2. 注重程序的过程,必须要做(被代理对象的方法必然会被执行)
  3. 执行类必须拿到被代理对象的引用

代理模式总结:代理模式做了一件什么事情? 字节码重组。

如何自动生成代理

我们知道,一个类从编写,到运行时调用,中间大概会经过这几个步骤
在这里插入图片描述
所以生成代理可以有三个思路,一,在编译期修改源代码;二,在字节码加载前修改字节码;三,
在字节码加载后动态创建代理类的字节码
设计模式之动态代理模式_第1张图片

JDK代理

JDK的动态代理是通过接口来进行强制转换,生成以后的代理对象,可以强制转换为接口。

JDK代理需要定义一个接口,通过接口方式进行代理

package com.hejs.proxy.jdk;
/**
 * @Author: hejis
 * @Description:
 * @Date: Create in 12:31 2019/3/5
 * @Modified By:
 */
public interface House {
    /**
     * 找一个房子
     */
    void findHouse();

    /**
     * 获取房子的地址
     * @return
     */
    String getLocation();

    /**
     * 获取房子面积
     * @return
     */
    int getArea();

    /**
     * 房子楼层
     * @return
     */
    int getFloor();
}

创建一个被代理对象Bean

package com.hejs.proxy.jdk;
/**
 * @Author: hejis
 * @Description:
 * @Date: Create in 12:34 2019/3/5
 * @Modified By:
 */
public class FamilyHouse implements House {

    private String location = "江苏省南京市鼓楼区";
    private Integer area = 90;
    private Integer floor = 12;


    public void findHouse() {
        System.out.println("客户需要找一个家庭住房。");
        System.out.println("位置:" + this.location + "; 面积:" + this.area + "; 楼层:" + this.floor);
    }

    public String getLocation() {
        return null;
    }

    public int getArea() {
        return 0;
    }

    public int getFloor() {
        return 0;
    }
}

代理类需要实现JDK自带的InvocationHandler接口,该代理类需要添加instance方法,参数为被代理对象,添加被代理接口属性接收被代理对象引用

package com.hejs.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author: hejis
 * @Description: 房屋中介
 * @Date: Create in 12:36 2019/3/5
 * @Modified By:
 */
public class HouseIntermediary implements InvocationHandler {

    private House target;

    public House getNewInstance(House target) throws  Exception {
        // 将被代理对象引用赋值
        this.target = target;
        // 获取被带离对象class
        Class clazz = target.getClass();
        // 第一步:生成源代码
        // 第二步:编译成class文件
        // 第三步:加载到JVM中,生成并返回新对象
        return (House) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是中介:得给你找个合适的房源才行");
        System.out.println("----------------------------");
        // 调用被带离对象的方法
        this.target.findHouse();
        System.out.println("----------------------------");
        return null;
    }
}

测试类

package com.hejs.proxy;

import com.hejs.proxy.cglib.Meipo;
import com.hejs.proxy.cglib.Person;
import com.hejs.proxy.jdk.House;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

/**
 * @Author: hejis
 * @Description:
 * @Date: Create in 14:29 2019/3/9
 * @Modified By:
 */
public class CglibProxyTest {
    
    public static void main(String[] args) {
        try {
            Person person = (Person) new Meipo().getNewInstance(Person.class);
            person.findLove();

            byte[] data = ProxyGenerator.generateProxyClass("$CGLibProxy0", new Class[]{Person.class});
            FileOutputStream fs = new FileOutputStream("E:\\workspace-design\\proxy-design\\classFile\\$CGLibProxy0.class");
            fs.write(data);
            fs.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

通过反编译工具,对生成的代理类进行编译,展示效果如下:

import com.hejs.proxy.jdk.House;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
  extends Proxy
  implements House
{
  private static Method m1;
  private static Method m6;
  private static Method m4;
  private static Method m2;
  private static Method m5;
  private static Method m3;
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int getFloor()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m6, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final void findHouse()
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String toString()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int getArea()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m5, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String getLocation()
  {
    try
    {
      return (String)this.h.invoke(this, m3, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m6 = Class.forName("com.hejs.proxy.jdk.House").getMethod("getFloor", new Class[0]);
      m4 = Class.forName("com.hejs.proxy.jdk.House").getMethod("findHouse", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m5 = Class.forName("com.hejs.proxy.jdk.House").getMethod("getArea", new Class[0]);
      m3 = Class.forName("com.hejs.proxy.jdk.House").getMethod("getLocation", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

CGLib代理

GGLib的动态代理是通过生成一个被代理对象的子类,然后重写父类的方法,生成以后的对象,可以强制转换为被代理对象的(也就是自己写的类)。子类引用复制给父类

定义一个被代理对象Bean

package com.hejs.proxy.cglib;

/**
 * @Author: hejis
 * @Description:
 * @Date: Create in 14:46 2019/3/9
 * @Modified By:
 */
public class Person {

    public void findLove(){
        System.out.println("肤白貌美大长腿");
    }

}

通过实现MethodInterceptor对象,重写intercept方法。
该对象需要提供一个new instance方法,提供生成以Person被代理对象的子类

package com.hejs.proxy.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @Author: hejis
 * @Description:
 * @Date: Create in 14:47 2019/3/9
 * @Modified By:
 */
public class Meipo implements MethodInterceptor {

    public Object getNewInstance(Class clazz) throws Exception{
        Enhancer enhancer = new Enhancer();
        // 设置父类是谁?
        // 告诉CGLib,生成的子类需要继承哪个类
        enhancer.setSuperclass(clazz);
        // 设置回调
        enhancer.setCallback(this);
        // 第一步:生成源代码
        // 第二步:编译成class文件
        // 第三步:加载到JVM中,生成并返回被代理对象
        return enhancer.create();
    }

    /**
     * 同样是做了字节码重组这样一件事情
     * 对于使用API的用户来说,是无感知的
     * @param obj
     * @param method
     * @param objects
     * @param proxy
     * @return
     * @throws Throwable
     */
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
        System.out.println("我是媒婆:得给你找个异性才行");
        System.out.println("----------------------------");
        // 这个obj引用是由GGLib给我们new出来的
        // cglib new 出来以后的对象,是被代理对象的子类(继承了我们自己写的那个类)
        // OPP, 在new子类之前,实际上默认先调用了我们super()方法
        // 也就是说:new了子类的同时,必须先new出来父类,这就相当于间接的持有了我们父类的引用
        // 子类重写了所有父类的方法
        // 我们改变子类对象的某些属性,是可以间接的操作父类的属性

        // 为什么调用的是父类的方法,调用子类的会出现死循环?
        // 因为子类重写了父类的该方法,若是调用子类,子类方法会调用当前this.intercept方法,所以只能调用父类该方法
        proxy.invokeSuper(obj, objects);
        return null;
    }
}

测试类

package com.hejs.proxy;

import com.hejs.proxy.cglib.Meipo;
import com.hejs.proxy.cglib.Person;
import com.hejs.proxy.jdk.House;
import javassist.ClassPool;
import javassist.CtClass;
import jdk.internal.org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.ByteVector;
import org.objectweb.asm.ClassWriter;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

/**
 * @Author: hejis
 * @Description:
 * @Date: Create in 14:29 2019/3/9
 * @Modified By:
 */
public class CglibProxyTest {

    public static void main(String[] args) {
        try {
            Person person = (Person) new Meipo().getNewInstance(Person.class);
            person.findLove();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(设计模式)