1. 代理模式
学习资料:
- 《Java程序性能优化》
使用代理对象完成用户请求,屏蔽用户对真实对象的访问
如同现实中代理,代理人被授权执行当事人的一些事宜,而无需当事人出面。处理事件时第三方只和代理人通信,以第三方看来的角度看,似乎当事人不存在。而事实是,代理人需要有当事人的授权,在核心问题上还需要请示当事人
1.1 代理模式的结构
角色 | 作用 |
---|---|
主题接口 | 定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法 |
真实主题 | 真正实现业务逻辑的类 |
代理类 | 用来代理和封装真实主题 |
Main | 客户端,使用代理类和主题接口完成一些工作 |
当一个客户端软件进行某一个操作需要到数据库查询一个数据,在查询数据前就必须先和数据库建立连接。假设软件开启时除了进行解析各种必要的xml
文件外,还要进行数据类的初始化,这就会影响软件的开启速度。此时可以使用代理模式,当系统开启时,使用代理类,在这个代理类中封装对数据库查询中的初始化操作,初始化代理类而不是真实的数据库查询类,代理类什么都不做,这样初始化就会快些,软件开启的速度也会快些。代理模式就起到了延迟加载的作用
除了延迟加载,代理模式还可以用于远程调用的网络代理、考虑安全因素的安全代理
延迟加载的核心:当前没有使用这个组件,则不需要真正地初始化,使用一个代理对象代替它原有的位置。在真正使用它的时候,再进行加载
延迟加载好处:
- 在时间轴上分散系统的压力,尤其是在系统启动时,不必完成所有的初始化工作,从而加速启动
- 有的真实主题,在软件启动直到关闭的整个过程,可能根本就不糊调用
1.2 代码实现
主题接口
public interface IDBQuery {
String request();
}
真实主题
public class DBQuery implements IDBQuery{
public DBQuery() {
try {
TimeUnit.SECONDS.sleep(1); // 模拟耗时操作
System.out.println("经过1秒 --> DBQuery 对象初始化完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String request() {
return "Hello , May";
}
}
代理类
public class DBQueryProxy implements IDBQuery {
private DBQuery real = null;
@Override
public String request() {
// 在真正需要的时候,才会创建真实对象,创建过程可能会很慢
System.out.println("在DBQueryProxy代理类中 --> 开始创建真正地初始化DBQuery对象");
//if (null == real) real = new DBQuery();
real = Optional.ofNullable(real).orElse(new DBQuery());
// 在多线程下,返回一个虚假类,类似Future模式,书上的这句注释还不是很明白
return real.request();
}
}
Main
public class ProxyTest {
public static void main(String[] args) {
IDBQuery q = new DBQueryProxy(); // 使用代理
String s =q.request();// 真正使用到时,才创建真实的对象
System.out.println(s);
}
}
运行结果:
在DBQueryProxy代理类中 --> 开始创建真正地初始化DBQuery对象
经过1秒 --> DBQuery 对象初始化完成
Hello , May
2. 动态代理
动态代理是指在运行时,动态生成代理类。即,代理类的字节码将在运行时生成并载入当前的classloader
与静态代理类相比的好处:
- 不需要为真实主题写一个形式上完全一样的封装类,尤其是在主题接口中有很多方法时,而且当接口有变动时,真实主题和代理类都需要修改,不利于系统维护
- 使用一些动态代理的生生方法甚至可以在运行时指定代理类的执行逻辑,提高灵活性
除了JDK
自带的动态代理,常见的还有GGLB
、Javassist
和ASM
。JDK
的方式不需要引入其他jar
包,但相对功能弱;GGLB
和Javassist
是高级的字节码生成库,性能比JDk
带的要好,功能也强大些;ASM
性能最好,但使用过于繁琐,可维护性也差
2.1 JDK自带的动态代理
IDBQuery,DBQuery同上
动态代理类:
public class JdkDbQueryHandler implements InvocationHandler {
private DBQuery real = null;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
real = Optional.ofNullable(real).orElse(new DBQuery());// 第一次调用,real为null,生成真实对象
return real.request();// 使用真实主题完成实际的操作
}
}
测试:
public class JdkProxyTest {
public static void main(String[] args) {
IDBQuery idbQuery = createJdkProxy();
String s = idbQuery.request();
System.out.println(s);
}
private static IDBQuery createJdkProxy() {
IDBQuery idbQuery = (IDBQuery) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{IDBQuery.class},
new JdkDbQueryHandler()
);
return idbQuery;
}
}
原理还没有搞懂,先了解使用方式
3. 最后
有错误请指出
共勉 :)