定义
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。有点类似于装饰者模式。但是与装饰者模式的区别是代理模式最终不一定调用目标对象的目标方法。但装饰者一定会。
图示:
理解
通俗一点说,就是代理对象持有目标对象的引用,并对外提供所有目标对象的业务方法,在该方法中添加一些逻辑处理并决定是否调用目标对象方法。代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会根据情况调用目标对象。代理模式又分为静态代理和动态代理。下面我们分别做介绍。
静态代理
角色:
静态代理的内容比较简单,这里我们先借鉴网上的一个很贴切的例子来说明,然后通过例子中总结一下。
实例
public interface ICoder {
public void implDemands(String demandName);
}
2、定义真实角色类:开发人员
public class JavaCoder implements ICoder{
private String name;
public JavaCoder(String name){
this.name = name;
}
@Override
public void implDemands(String demandName) {
System.out.println(name + " implemented demand:" + demandName + " in JAVA!");
}
public class CoderProxy implements ICoder{
private ICoder coder;
public CoderProxy(ICoder coder){
this.coder = coder;
}
@Override
public void implDemands(String demandName) {
coder.implDemands(demandName);
}
}
4、为代理的方法添加额外的业务逻辑
public class CoderProxy implements ICoder{
private ICoder coder;
public CoderProxy(ICoder coder){
this.coder = coder;
}
@Override
public void implDemands(String demandName) {
if(demandName.startsWith("Add")){
System.out.println("No longer receive 'Add' demand");
return;
}
coder.implDemands(demandName);
}
}
4、测试
public class Customer {
public static void main(String args[]){
//定义一个java码农
ICoder coder = new JavaCoder("Zhang");
//定义一个产品经理
ICoder proxy = new CoderProxy(coder);
//让产品经理实现一个需求
proxy.implDemands();
}
}
- Subject:抽象主题角色。可以是接口,也可以是抽象类。
- RealSubject:真实主题角色。业务逻辑的具体执行者。
- ProxySubject:代理主题角色。内部含有RealSubject的引用,负责对真实角色的调用,并在真实主题角色处理前后做预处理和善后工作。
- 职责清晰 真实角色只需关注业务逻辑的实现,非业务逻辑部分,后期通过代理类完成即可。
- 高扩展性 不管真实角色如何变化,由于接口是固定的,代理类无需做任何改动。
动态代理
public class CoderDynamicProxy implements InvocationHandler{
//被代理的实例
private ICoder coder;
public CoderDynamicProxy(ICoder _coder){
this.coder = _coder;
}
//调用被代理的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(System.currentTimeMillis());
Object result = method.invoke(coder, args);
System.out.println(System.currentTimeMillis());
return result;
}
}
public class DynamicClient {
public static void main(String args[]){
//要代理的真实对象
ICoder coder = new JavaCoder("Zhang");
//创建中介类实例
InvocationHandler handler = new CoderDynamicProxy(coder);
//获取类加载器
ClassLoader cl = coder.getClass().getClassLoader();
//动态产生一个代理对象
ICoder proxy = (ICoder) Proxy.newProxyInstance(cl, coder.getClass().getInterfaces(), handler);
//通过代理类,执行doSomething方法;这里就走到了CoderDynamicProxy.invoke()中
proxy.implDemands("Modify user management");
}
}
执行结果如下:
1501728574978
Zhang implemented demand:Modify user management in JAVA!
1501728574979
- 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
- 第二个参数coder.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
- 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
总结一下,一个典型的动态代理可分为以下五个步骤:
1、创建抽象角色
2、创建真实角色
3、通过实现InvocationHandler接口创建中介类
4、通过Proxy.newProxyInstance()生成一个动态代理对象
5、通过代理对象调用业务方法
动态代理的使用基本就是这样,接下来我们深入JDK看看java是如何做到动态生成一个代理类,代码又是如何执行到中介类InvocationHandler.invoke()里面的。
进入Proxy.newProxyInstance()源码中查看主要逻辑如下:
//生成代理类class,并加载到jvm中,获取Class对象
Class<?> cl = getProxyClass0(loader, interfaces);
//获取代理类参数为InvocationHandler的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
//生成代理类的实例对象,并返回
return newInstance(cons, ih);
我们一步一步分析上述几行代码
第一步生成代理类,该例中生成的便是实现ICoder接口的类,并将该类加载到jvm中,进入getProxyClass0()中核心逻辑如下:
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
通过Class.forName()生成代理类并加载到jvm中,返回Class对象。Calss.forName()中调用的是native方法
第二步获取构造函数
我们可以看到constructorParams是Proxy维护的静态变量,其中包括InvocationHandler,源码如下:
/** parameter types of a proxy class constructor */
private final static Class[] constructorParams =
{ InvocationHandler.class };
第三步通过构造函数创建实例,参数为构造器和构造时需要使用的中介类InvocationHandler
private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
try {
return cons.newInstance(new Object[] {h} );
} catch (IllegalAccessException | InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString());
}
}
}
这样,就解决了我们提出的第一个问题:如何生成代理对象。其实这部分涉及到类加载机制相关的知识,不熟悉的同学可以先学习这部分内容。那么,为什么我们调用代理对象的业务方法,就会执行到中介类InvocationHandler.invoke()方法中呢?这就有从生成的代理类代码中寻找答案了。我们可以手动模拟生成代理类,如下:
public class CodeUtil {
public static void main(String[] args) throws IOException {
byte[] classFile = ProxyGenerator.generateProxyClass("TestProxyGen", JavaCoder.class.getInterfaces());
File file = new File("D:/aaa/TestProxyGen.class");
FileOutputStream fos = new FileOutputStream(file);
fos.write(classFile);
fos.flush();
fos.close();
}
}
注意这里的类名TestProxyGen是我们模拟的,真正的类名还是$Proxy0
我们查看生成代理类的代码,其中,业务方法implDemands如下:
这里调用了h也就是InvocationHandler的invoke方法,这就能解释为何我们调用代理对象的业务方法,最终走到了中介类的invoke()方法中了