代理模式在Java中十分常见,有如Spring实现AOP而使用动态代理,更有RPC实现中使用的调用端调用的代理服务。代理模型除了是一种设计模式之外,它更是一种思维,所以探讨并深入理解这种模型是非常有必要的。
1.简介
定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
代理模式包含如下角色:
ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。
RealSubject:真实主题角色,是实现抽象主题接口的类。
Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
2.静态代理
静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态。考虑这样的场景,管理员在网站上执行操作,在生成操作结果的同时需要记录操作日志,这是很常见的。此时就可以使用代理模式,代理模式可以通过聚合和继承两种方式实现,聚合实现方式中代理类聚合了被代理类,且代理类及被代理类都实现了同一个接口,可实现灵活多变。继承式的实现方式则不够灵活,一般而言我们更倾向于使用聚合实现方式来实现代理,图示就是聚合实现方式:
代码如下:
package java_base.data.proxy;
/**
* 静态代理
* @author 16437
*
*/
public class ProxyDemo1 {
public static void main(String[] args) {
AdminProxy ap = new AdminProxy(new Admin());
ap.doSomething();
AdminProxy2 ap2 = new AdminProxy2();
ap2.doSomething();
}
}
interface Manager {
void doSomething();
}
class Admin implements Manager{
@Override
public void doSomething() {
System.out.println("来自Admin方法doSomething!");
}
}
class AdminProxy implements Manager{
Admin admin;
public AdminProxy(Admin admin){
this.admin = admin;
}
@Override
public void doSomething() {
System.out.println("----开始了----");
admin.doSomething();
System.out.println("----结束了----");
}
}
class AdminProxy2 extends Admin{
@Override
public void doSomething() {
System.out.println("----2开始了----");
super.doSomething();
System.out.println("----2结束了----");
}
}
3.动态代理
代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(Proxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
3.1 JDK动态代理
代理步骤:
(1)定义一个事件管理器类实现invocationHandle接口,并重写invoke(代理类,被代理的方法,方法的参数列表)方法。
(2)实现被代理类及其实现的接口,
(3)调用Proxy.newProxyInstance(类加载器,类实现的接口,事务处理器对象);生成一个代理实例。
(4)通过该代理实例调用方法。
实现代码:
//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的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。
3.2 cglib动态代理
前面分析到,因为Java只允许单继承,而JDK生成的代理类本身就继承了Proxy类,因此,使用JDK实现的动态代理不能完成继承式的动态代理,但是我们可以使用cglib来实现继承式的动态代理。
//1.具体主题
public class Train{
public void move(){
System.out.println("火车行驶中…");
}
}
//2.生成代理
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 拦截所有目标类方法的调用
* 参数:
* obj 目标实例对象
* method 目标方法的反射对象
* args 方法的参数
* proxy 代理类的实例
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
//代理类调用父类的方法
System.out.println("日志开始");
proxy.invokeSuper(obj, args);
System.out.println("日志结束");
return null;
}
}
//3.测试
public class Test {
public static void main(String[] args) {
CGLibProxy proxy = new CGLibProxy();
Train t = (Train) proxy.getProxy(Train.class);
t.move();
}
}
4.小结
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样对每一个方法或方法组合进行处理。Proxy 很美很强大,但是仅支持 interface 代理。Java 的单继承机制注定了这些动态代理类们无法实现对 class 的动态代理。好在有cglib为Proxy提供了弥补。class与interface的区别本来就模糊,在java8中更是对interface进行了加强,使得interface越来越接近class,假如哪天java突破了单继承的限制,动态代理将会更加强大。