原文连接
简介
Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术。生活中的方方面面都可以虚拟到代码中。代理模式所讲的就是现实生活中的这么一个概念:中介。
代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
代理模式包含如下角色:
- ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。
- RealSubject:真实主题角色,是实现抽象主题接口的类。
- Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
实现动态代理的关键技术是反射
静态代理
代理模式有几种,虚拟代理,计数代理,远程代理,动态代理。主要分为两类,静态代理和动态代理。静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态。
考虑这样的场景,管理员在网站上执行操作,在生成操作结果的同时需要记录操作日志,这是很常见的。此时就可以使用代理模式,代理模式可以通过聚合和继承两种方式实现:
/**方式一:聚合式静态代理
* @author Goser (mailto:goskalrie@163.com)
* @Since 2016年9月7日
*/
//1.抽象主题接口
public interface Manager {
void doSomething();
}
//2.真实主题类
public class Admin implements Manager {
public void doSomething() {
System.out.println("Admin do something.");
}
}
//3.以聚合方式实现的代理主题
public class AdminPoly implements Manager{
private Admin admin;
public AdminPoly(Admin admin) {
super();
this.admin = admin;
}
public void doSomething() {
System.out.println("Log:admin操作开始");
admin.doSomething();
System.out.println("Log:admin操作结束");
}
}
//4.测试代码
Admin admin = new Admin();
Manager m = new AdminPoly(admin);
m.doSomething();
//方式二:继承式静态代理
//与上面的方式仅代理类和测试代码不同
//1.代理类
public class AdminProxy extends Admin {
@Override
public void doSomething() {
System.out.println("Log:admin操作开始");
super.doSomething();
System.out.println("Log:admin操作开始");
}
}
//2.测试代码
AdminProxy proxy = new AdminProxy();
proxy.doSomething();
聚合实现方式中代理类聚合了被代理类,且代理类及被代理类都实现了同一个接口,可实现灵活多变。继承式的实现方式则不够灵活。
比如,在管理员操作的同时需要进行权限的处理,操作内容的日志记录,操作后数据的变化三个功能。三个功能的排列组合有6种,也就是说使用继承要编写6个继承了Admin的代理类,而使用聚合,仅需要针对权限的处理、日志记录和数据变化三个功能编写代理类,在业务逻辑中根据具体需求改变代码顺序即可。
动态代理
一般来说,对代理模式而言,一个主题类与一个代理类一一对应,这也是静态代理模式的特点。
但是,也存在这样的情况,有n各主题类,但是代理类中的“前处理、后处理”都是一样的,仅调用主题不同。也就是说,多个主题类对应一个代理类,共享“前处理,后处理”功能,动态调用所需主题,大大减小了程序规模,这就是动态代理模式的特点。
JDK动态代理
实现
//1. 抽象主题
public interface Moveable {
void move() throws Exception;
}
//2. 真实主题
public class Car implements Moveable {
public void move() throws Exception {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中…");
}
}
//3.事务处理器
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
/**
* 参数:
*proxy 被代理的对象
*method 被代理对象的方法
*args 方法的参数
* 返回:
*Object 方法返回值
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶…");
method.invoke(target, args);
long stopTime = System.currentTimeMillis();
System.out.println("汽车结束行驶…汽车行驶时间:" + (stopTime - startTime) + "毫秒!");
return null;
}
}
//测试类
public class Test {
public static void main(String[] args) throws Exception{
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Class cls = car.getClass();
/**
*loader 类加载器
*interfaces 实现接口
*h InvocationHandler
*/
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);
m.move();
}
}
代码讲解:
在测试代码中,Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,事务处理器。所以先实例化Car,实例化InvocationHandler的子类TimeHandler,将各参数传入Proxy的静态方法newProxyInstance()即可获得Car的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。
JDK动态代理步骤
- 创建一个实现InvocationHandler接口的类,它必须实现invoke()方法
- 创建被代理的类及接口
- 调用Proxy的静态方法,创建一个代理类
- 通过代理调用方法
而为什么要进行如此操作,可以从Proxy和InvocationHandler的源码中找打答案。对源码不感兴趣的可以将下面的源码部分小节略过。
JDK动态代理原理与源码
newProxyInstance()方法的源码:
public static Object newProxyInstance(ClassLoader loader,
Class[] interfaces,
InvocationHandler h)
throws IllegalArgumentException{
if (h == null) {
throw new NullPointerException();
}
final Class[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*查找或生成指定的代理类*/
Class cl = getProxyClass0(loader, intfs);
/*用指定的调用处理程序调用它的构造函数.*/
try {
//获得类的构造函数
final Constructor cons =cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
//当需要代理的类实现了一个非public的接口时,因为这样的接口需要特殊的权限,因此调用doPrivilege(native 修饰的方法)创建代理实例。
return AccessController.doPrivileged(newPrivilegedAction