定义:
代理模式:使用代理对象完成用户请求,屏蔽用户对真实对象的访问。
在软件设计中,使用代理模式的场景很多,比如处于安全考虑,需要屏蔽用户直接访问真实对象;或者在远程调用中,需要使用代理类处理远程方法调用的技术细节;也可以为了提升系统性能,对象真实对象进行封装,从而达到延迟加载的目的。
结构:
角色 | 作用 |
---|---|
主题接口 | 定义代理类和真实主题对外方法,也是代理类代理真实主题的方法 |
真实主题 | 真正实现业务逻辑的类 |
代理类 | 用来代理和封装真实主题 |
Main | 客户端,使用代理类和主题接口完成工作 |
应用场景:
假设有个数据据查询数据功能,在查询数据前,需要获得数据库连接。软件启动时,初始化所有类,此时尝试获取数据库连接。当系统有大量类似操作叠加时,会使得系统启动速度变得非常缓慢。
在系统启动时,将消耗资源阿娜最多的方法使用代理模式分离,就可以加快系统启动速度,减少用户等待时间。而用户在真正查询数据时,再由代理类,单独去加载真实的数据库查询类。这个过程就是使用代理模式实现延迟加载。
UML:
代码:
public interface IDBQuery {
String request();
}
public class DBQuery implements IDBQuery {
public DBQuery() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String request() {
return "request string";
}
}
public class DBQueryProxy implements IDBQuery {
private DBQuery real;
@Override
public String request() {
//在真正需要时,才创建真实对象
if (real == null) {
real = new DBQuery();
}
return real.request();
}
}
public class Main {
public static void main(String[] args) {
IDBQuery query = new DBQueryProxy();
query.request();
}
}
动态代理:
动态代理是指在运行时,动态生成代理类。即,代理类的字节码将在运行时生成并加载到当前的ClassLoader。
好处:
1. 不需要为真实主题写一个形式上完全一样的封装类
2. 使用动态代理的生成方法可以在运行时指定代理类的执行逻辑,提升系统的灵活性
实现方式:
- JDK自带的动态代理
- CGLIB
- Javassist
- ASM
JDK动态代理使用:
public class JDKDBQueryHandler implements InvocationHandler{
IDBQuery real;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (real ==null) {
real = new DBQuery();
}
return real.request();
}
}
public static IDBQuery createJDKProxy() {
return (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{IDBQuery.class}, new JDKDBQueryHandler());
}
CDLIB使用:
public class CglibDBQueryInterceptor implements MethodInterceptor {
IDBQuery real;
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (real == null) {
real = new DBQuery();
}
return real.request();
}
}
public static IDBQuery createCglibProxy() {
Enhancer enhancer = new Enhancer();
//指定切入器,定义代理类逻辑
enhancer.setCallback(new CglibDBQueryInterceptor());
//指定实现接口
enhancer.setInterfaces(new Class[]{IDBQuery.class});
//生成代理类实例
return (IDBQuery) enhancer.create();
}
Javassist使用:
Javassist生成动态代理类可以使用两种方式:一种是使用代理工厂创建,另一种通过使用动态代码创建。
使用代理工厂:
public class JavassistDBQueryHandler implements MethodHandler {
IDBQuery real;
@Override
public Object invoke(Object o, Method method, Method method1, Object[] objects) throws Throwable {
if (real == null) {
real = new DBQuery();
}
return real;
}
}
public static IDBQuery createJavassistPRoxy() throws IllegalAccessException, InstantiationException {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setInterfaces(new Class[]{IDBQuery.class});
Class aClass = proxyFactory.createClass();
IDBQuery query = (IDBQuery) aClass.newInstance();
((ProxyObject) query).setHandler(new JavassistDBQueryHandler());
return query;
}
使用动态代码:
public static IDBQuery createJavassistByteCodeDynProxy() throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException {
ClassPool classPool = new ClassPool(true);
//定义类名
CtClass ctClass = classPool.makeClass(IDBQuery.class.getSimpleName() + "Javassist-ByteCodeProxy");
//需要实现接口
ctClass.addInterface(classPool.get(IDBQuery.class.getName()));
//添加构造函数
ctClass.addConstructor(CtNewConstructor.defaultConstructor(ctClass));
//添加类的字段信息
ctClass.addField(CtField.make("public " + IDBQuery.class.getName() + " real;", ctClass));
String name = DBQuery.class.getName();
//添加方法
ctClass.addMethod(CtMethod.make("public String request() {if(real ==null) real = new "
+ name + "(); return real.request();}", ctClass));
//基于以上信息,生成动态类
Class aClass = ctClass.toClass();
//生成动态类的实例
return (IDBQuery) aClass.newInstance();
}