一,静态代理(房产中介)
在代理模式中的角色:
● 抽象对象角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
● 目标对象角色:定义了代理对象所代表的目标对象。
● 代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。
/** * 抽象角色,真实角色和代理角色都要实现的接口 * @author marcopan */ public interface IAbstractSubject { // 代理对象和真实角色共有的方法,租房() public void rentHouse(); } /** * 真实角色--房东 * @author marcopan * */ public class RealSubject implements IAbstractSubject { @Override public void rentHouse() { System.out.println("rentHouse from RealSubject"); } } package proxy.staticProxy; /** * 代理角色,中介,含有真实角色的引用 * @author marcopan * 代理角色做两件事: * 1. 关联真实角色对象 * 2. 调用真实对象方法 */ public class StaticProxySubject implements IAbstractSubject { // 含有真实角色的引用 private RealSubject realSubject = null; public StaticProxySubject(RealSubject subject) { this.realSubject = subject; } @Override public void rentHouse() { realSubject.rentHouse(); } } public class Client { public static void main(String[] args) { IAbstractSubject proxySubject = new StaticProxySubject(new RealSubject()); proxySubject.rentHouse(); } }
二,动态代理
public class DynamicProxySubject implements InvocationHandler {
private ISubject target = null;
public DynamicProxySubject(ISubject target) {
this.target = target;
}
public Object getProxyInstance() {
Class<?> clazz = target.getClass();
/**
* Proxy这个类的作用就是用来动态创建一个代理对象的类
* Proxy.newProxyInstance返回的是一个代理对象:com.sun.proxy.$Proxy0
*/
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
/**
* proxy:指被代理的对象,$Proxy0是系统自动生成的实现ISubject接口的代理类,并持有InvocationHandler引用
* method:指代的是我们所要调用真实对象的某个方法的Method对象
* args:指代的是调用真实对象某个方法时接受的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强消息......
return method.invoke(target, args);
// 增强消息......
}
}
/**
* // byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{IAbstractSubject.class});
* // try {
* // String filePath = IAbstractSubject.class.getResource("").getPath();
* // FileOutputStream fos = new FileOutputStream(filePath + "/$Proxy0.class");
* // fos.write(bytes);
* // fos.close();
* // } catch (Exception e) {
* // e.printStackTrace();
* // }
*/
public class Client {
public static void main(String[] args) {
ISubject target = new RealSubject();
ISubject dynamicInstance = (ISubject) new DynamicProxySubject(target).getProxyInstance();
dynamicInstance.requestHouse1();
dynamicInstance.requestHouse2();
}
}
三、Cglib动态代理:
public class CglibProxySubject implements MethodInterceptor {
public Object getCglibInstance(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// before
Object obj = methodProxy.invokeSuper(o, objects);
// after
return obj;
}
}
public class Client {
public static void main(String[] args) {
String filePath = ISubject.class.getResource("").getPath();
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, filePath);
ISubject target = new RealSubject();
ISubject cglibInstance = (ISubject) new CglibProxySubject().getCglibInstance(target.getClass());
System.out.println(cglibInstance);
cglibInstance.requestHouse1();
cglibInstance.requestHouse2();
}
}
CGLib 和 JDK 动态代理对比:
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM 框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法, CGLib 执行效率更高。