设计模式之动态代理模式
设计模式之动态代理模式
动态代理模式应用场景条件
- 两个角色:执行类和被代理对象
- 注重程序的过程,必须要做(被代理对象的方法必然会被执行)
- 执行类必须拿到被代理对象的引用
代理模式总结:代理模式做了一件什么事情? 字节码重组。
如何自动生成代理
我们知道,一个类从编写,到运行时调用,中间大概会经过这几个步骤
所以生成代理可以有三个思路,一,在编译期修改源代码;二,在字节码加载前修改字节码;三,
在字节码加载后动态创建代理类的字节码
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();
}
}
}