上班的路上看到一篇关于代理模式的博客,上班前做下自己的总结,从代理模式的简介–>用法–>优缺点
用一个通俗易通的例子来介绍下代理模式的场景,就比如在北京,一个房子的主人想要卖掉房子,他不想自己做这个事情,而是将这个事情交给房产中介来做,房产中介再找客户,签合同,走相关流程。这里出现几个点:房子主人、房产中介、卖房子、客户;房子主人就是被代理类,房产中介就是代理类、而卖房子是前两者需要做的事情,客户就是客户端。也就是说,房子主人和房产中介需要共同继承一个类或者接口来完成卖房子的事情。而代理模式又分为静态代理和动态代理,接下来我们从这几种类型出发来完成上述的事情
静态代理的话,就是,代理类和被代理类实现一个共同的接口,完成相应的操作,这里的代理类需要开发者去手动写出,在编译期这个代理类就已经确定了,说到这里可能大家有点不明白,我把代码附上,做些解释
卖房子接口:
package com.markus.proxy.staticproxy;
public interface SaleHouse {
public void sale();//卖房子
}
房子主人
package com.markus.proxy.staticproxy;
public class HomeHost implements SaleHouse {
public void sale() {
System.out.println("卖房子");
}
}
房产中介
package com.markus.proxy.staticproxy;
public class HomeAgent implements SaleHouse {
private SaleHouse saleHouse;
public HomeAgent(SaleHouse saleHouse){
this.saleHouse = saleHouse;
}
public void sale() {
preSale();
saleHouse.sale();
postSale();
}
private void preSale(){
System.out.println("找客户");
}
private void postSale(){
System.out.println("签合同,拿佣金");
}
}
客户(测试类):
package com.markus.proxy.staticproxy;
public class Client {
public static void main(String[] args) {
SaleHouse homeHost = new HomeHost();
SaleHouse homeAgent = new HomeAgent(homeHost);
homeAgent.sale();
}
}
测试结果:
小结:
整个逻辑中,可以看到代理类和被代理类都去实现一个接口,而被代理类不需要主动去执行,而是将这个事情交给代理类去实现,并且还可以在方法执行前后可以执行其他东西
优点:
缺点:
在代理模式中会有代理类,在静态代理中,需要开发者写出大量的代理类,如果接口新增一个方法的话,会很难维护。动态代理是采用反射的方法,在运行期间动态的生成代理类,它在简化了程序开发的工作,也提高了软件系统的可扩展性,因为java反射可以在运行时动态的生成任何类型的动态代理类
在jdk动态代理中,java.lang.reflect包中的proxy类和InvocationHandler接口提供了生成动态代理类的能力
动作接口:
package com.markus.proxy.dynamicproxy;
public interface SaleHouse {
public void sale();
}
被代理类:
package com.markus.proxy.dynamicproxy;
public class HomeHost implements SaleHouse {
public void sale() {
System.out.println("卖房子");
}
}
自定义实现InvocationHandler的类:
package com.markus.proxy.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object instance;
public MyInvocationHandler(Object instance){
this.instance = instance;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
preInvoke();
Object invoke = method.invoke(instance, args);
aftInvoke();
return invoke;
}
private void preInvoke(){
System.out.println("中介找客户");
}
private void aftInvoke(){
System.out.println("中介签合同,领佣金");
}
}
测试:
package com.markus.proxy.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
HomeHost homeHost = new HomeHost();
InvocationHandler invocationHandler = new MyInvocationHandler(homeHost);
//三个参数
//classLoader:需要被代理类的类加载期
//interface:需要被代理类实现的接口
//自定义实现的InvocationHandler
//得到被代理类实现的接口
SaleHouse dynamicproxy = (SaleHouse) Proxy.newProxyInstance(HomeHost.class.getClassLoader(),HomeHost.class.getInterfaces(), invocationHandler);
dynamicproxy.sale();
System.out.println(dynamicproxy.getClass());
}
}
测试结果:
当我们卖的房子是高档房子的话,其中复杂性比卖普通房子高,在静态代理情况下,我们是不是应该再去写一个代理类,来处理这个工作,而动态代理的话,只需要实现一次就可以了,代理类自动生成。
静态代理模式这里不做演示。我来写下动态代理模式下的实现吧。
看看扩展后的架构
程序开发上没有自己写代理类,就扩展了一个新功能
小插曲:现在博客上的内容咋啥都有,正不正确也都往上写,也不标注什么时候用,见别人这么写他也这么写。害的我找老半天,最终在一篇博客下看到了正解的评论,感谢!!!
言归正传:我们如何去查看自动生成的动态代理类呢?
首先在你的测试类里输入这样一行代码:一定要看清我代码中的注释
//1、jdk1.8版本的朋友们,你们先在测试类里复制上这行代码,如果没效果那就用第二行代码!!!粘贴一个就行
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//2、记住,只有在上一行代码不好使得情况下再去尝试下这行代码
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
//ps:我是第一行代码就好使
这时候我们可以看到idea里生成了一个包
这里面的两个类就是动态生成的代理类,点进去看一下
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import com.markus.proxy.dynamicproxy.SaleHouse;
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 SaleHouse {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void sale() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.markus.proxy.dynamicproxy.SaleHouse").getMethod("sale");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
看完这个代码是不是明白jdk的动态代理为什么需要被代理对象必须要有一个接口了吗?
这里需要继承一个Proxy类,然后实现了被代理类的接口,因为java是单继承的,所以必须要实现接口
jdk动态代理必须要实现一个接口才能成功生成代理类,那如果没有接口呢,那么基于cglib动态代理就不需要实现接口,而是基于类实现的动态代理
首先这是一个外部文件,我们先要引入cglib依赖
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
被代理类:
package com.markus.proxy.dynamicproxy1;
public class VillaHost {
public void sale(){
System.out.println("我要卖别墅");
}
}
cglib拦截器
package com.markus.proxy.dynamicproxy1;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyInterceptor implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
start();
Object invokeSuper = methodProxy.invokeSuper(o, objects);
end();
return invokeSuper;
}
private void start(){
System.out.println("卖别墅前");
}
private void end(){
System.out.println("卖别墅后");
}
}
测试:
package com.markus.proxy.dynamicproxy1;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
//在指定目录下生成动态代理类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\myProject\\myStudy");
Enhancer enhancer = new Enhancer();
//设置目标类的class文件
enhancer.setSuperclass(VillaHost.class);
//设置回调函数
enhancer.setCallback(new CglibProxyInterceptor());
//创建代理类
VillaHost proxy = (VillaHost) enhancer.create();
proxy.sale();
System.out.println("卖房子的人是谁:"+proxy.getClass());
}
}
代理分为静态代理和动态代理,而动态代理又分为jdk动态代理和cglib动态代理
静态代理的话,它需要开发者自己实现定义代理类,也就是说,在编译期,代理类的字节码文件就已经存在了,如果想要扩展新的事情的话,就要拓展一个代理类,难以维护。
动态代理的话,就能解决上面的问题,它是在程序运行期间动态的生成代理类,不需要开发者自己拓展。在实现动态代理的时候,有两种实现方式,一种是jdk动态代理,它要求被代理类必须实现接口,原因我们在观看代理类源码的时候已经分析了。第二种方式就是cglib动态代理,它不需要有接口。