代理模式的用处很多,有的是为了系统安全,有的是为了远程调用,这里我们,主要探讨下由于程序性能优化的延迟加载。
首先我们来看下代理模式设计
先首先简单阐述下什么叫代理模式吧
代理设计模式有一个接口,另外还有真实主题类和代理类,真实类和代理类都实现了接口,代理类和真实主题类是关联和聚合关系。客户端与接口关联。
代理分为静态代理和动代态代理所谓静态代理是为真实主题手动创建一个代理,而动态代理则是 jvm 在运行时运用字节码加载技术自动创建一个代理,并不用关心接口和真是主题类
具体如何实现
哦,对了差点忘了。代理模式到底是怎样优化程序的,我们具体来看下。
客户端测试代码如下:
public class TestDynamicProxy {
public static void main(String[] args) {
IDBQuery iy = JdkDBqueryHandler.createJdkProxy();
System.out.println(iy.request());
}
}
Ps: 客户端与接口关联
代理实现和逻辑处理类如下:
import java.lang.reflect.*;
public class JdkDBqueryHandler implements InvocationHandler{
IDBQuery real = null;
public Object invoke(Object object, Method method ,Object[] args ) {
if(real==null) {
real = new DBQuery();
}
return real.request();
}
public static IDBQuery createJdkProxy() {
IDBQuery jdkProxy =
(IDBQuery)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{IDBQuery.class},new JdkDBqueryHandler());
return jdkProxy;
}
}
Ps: 代理实现和逻辑处理类是实现延迟加载的关键代码
当客户端开始加载时会加载到代码实现和逻辑处理类并创建代理实例,但并不会初始化真实主题类。只有当调用 iy.request() 方法时才会执行代理实现和逻辑处理类的 invoke() 方法并加载并初始化真实主题类。这样才实现了延迟加载,减少系统初始化时间,提高用户体验。在一定程度上也可以节约内存空间,避免内存空间浪费,(因为用的时候才记载初始化的嘛,不用也开辟内存空间那不是浪费了嘛)
这里另外附上接口类和真实主题类的代码,方便大家测试
接口类
public interface IDBQuery {
String request();
}
真实主题类
public class DBQuery implements IDBQuery {
public DBQuery() {
try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
public String request() {
return "request coming";
}
}
以上是 jdk 自带的动态代理实现 另外还有较流行的 CGLIB 动态代理 ,javaassist 动态代理
这里我先介绍一下 CGLIB 动态代理。 CGLIB 动态代理和 jdk 的动态代理非常的相似,我们来看下具体的代码
客户端代码如下:
public class TestDynamicProxy {
public static void main(String[] args) {
IDBQuery iy = CglibDbQueryInterceptor.createCglibProxy();
System.out.println(iy.request());
}
}
这里不过多解释,看完上面的例子相信大家都明白了
代理实现和逻辑处理类如下
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibDbQueryInterceptor implements MethodInterceptor{
IDBQuery real = null;
public Object intercept(Object args0,Method arg1,Object[] args2,MethodProxy args3)throws Throwable {
if(real==null) {
real = new DBQuery();
}
return real.request();
}
public static IDBQuery createCglibProxy() {
Enhancer eh = new Enhancer();
eh.setCallback(new CglibDbQueryInterceptor());//指定切入器,定义代理类逻辑
eh.setInterfaces(new Class[]{IDBQuery.class});//指定接口
IDBQuery proxy = (IDBQuery)eh.create();//创建代理实例
return proxy;
}
}
Jdk 的动态代理和 CGLIB 实现动态代理大致都要指定代理类逻辑和代理接口这是共性
真实主题类和接口和上例一样,不再贴了
JavaAssist 的动态代理有两种实现方法,一种使用代理工厂,一种使用动态 java 代码生成字节码
这里就直接贴代码了:
图片上传失败~~~
Ps: 以上实例方法在在逻辑处理类中处理类 , 真实主题类和接口和上面例子相同
代理工厂指定接口后生成代理类对象,代理类对象再指定处理逻辑。
大致三者都相似, javaAssist 动态 java 代码生成字节码就不介绍了,赶觉较麻烦
另有 ASM 动态代理实现起来比较复杂,也不介绍