代理模式(Proxy Pattern)

一种结构型设计模式,这种设计模式对外界提供了一种代理对象用来控制对委托对象的访问。


代理模式就是使用一个类代表另一个类的功能。

代理模式

1
2
3
public interface Car {
void run();
}
1
2
3
4
5
6
public class MyCar implements Car {
@Override
public void run() {
System.out.println("MyCar is running...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyCarProxy implements Car {
private MyCar myCar;
public MyCarProxy(MyCar myCar) {
this.myCar = myCar;
}
@Override
public void run() {
//添加其他功能。。。
myCar.run();
//添加其他功能。。。
}

}

使用代理对象执行MyCar的run()方法,在run()方法执行之前和之后都可以做一些其他工作,比如记录日志,记录时间等。这就是代理模式的基本思想。

静态代理

上面对代理模式的实现就是静态代理,静态代理看上去非常简单直接,但是存在一个问题,如果需要对多个类进行代理,我们需要为每一个类都实现一个代理类,如果这些代理类实现相同的功能,就会出现很多重复代码。如果我们需要为很多类进行代理,而且代理的功能相同,我们就需要用到动态代理。

动态代理

动态代理可以动态地生成各个委托类的代理对象,只需要为一类代理行为写一个具体的实现类。动态代理类的字节码文件是在程序运行时动态生成的,这是动态代理和静态代理的最大区别。

JDK动态代理

1
2
3
4
public interface Car {
void run();
void stop();
}
1
2
3
4
5
6
7
8
9
10
11
public class MyCar implements Car {
@Override
public void run() {
System.out.println("MyCar is running...");
}

@Override
public void stop() {
System.out.println("MyCar is stop...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TestDynamicProxy {
public static void main(String[] args) {
Car car = new MyCar();
Car proxyInstance = (Car)Proxy.newProxyInstance(car.getClass().getClassLoader(), car.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("do something before...");
Object result = method.invoke(car, args);
System.out.println("do something after...");
return result;
}
});
proxyInstance.run();
proxyInstance.stop();
}
}
1
2
3
4
5
6
7
输出:
do something before...
MyCar is running...
do something after...
do something before...
MyCar is stop...
do something after...

上面是JDK动态代理,可以看到JDK动态代理是使用Proxy.newProxyInstance(ClassLoader, Interfaces, InvocationHandler)来得到新的代理对象,该静态方法中的三个参数分别是:

  • ClassLoader:委托类的类加载器,可以通过Car.getClass().getClassLoader()得到。
  • Interfaces:委托类实现的接口,可以通过Car.getClass().getInterfaces()得到。
  • InvocationHandler:调用委托类方法时的处理器。

使用代理对象调用委托对象的方法时,都会进入InvocationHandler的publci Object invoke(Object proxy, Method method, Object[] args)方法中,在这个方法里,可以自定义在执行委托方法前后,要执行的操作,在invoke方法中可以使用method.invoke(委托类对象, args);来执行使用代理对象调用的委托对象方法。invoke函数中的三个参数分别代表:

  • proxy:代理对象。
  • method:代理对象执行的委托对象中的方法。
  • args:方法中的传入参数。

注意:在使用method.invoke()方法调用委托对象方法的时候第一个参数传入的是原委托对象,而不是代理对象。

CGLIB动态代理

JDK动态代理利用反射机制,基于接口生成代理对象,如果委托对象没有实现接口,我们就需要使用CGLIB动态代理。CGLIB (Code Generation Library)是一个基于ASM的字节码生成库,CGLIB通过继承产生子类覆盖非final方法来进行代理,由于采用继承实现动态代理所以CGLIB不能代理一个final类或者final方法。CGLIB比使用Java反射的JDK动态代理方法更快。

首先定义一个委托类SayHello,这个类里面只有一个sayHello方法

1
2
3
4
5
public class SayHello {
public String sayHello(String str) {
return "SayHello: Hello " + str;
}
}

我们可以通过CGLIB的Enhancer对象来指定委托对象和调用委托对象方法时的处理函数,然后通过Enhancer的create方法得到代理对象。对代理对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法,在intercept()方法中可以加入任意操作。

1
2
3
4
5
6
7
public void testCGLIBProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SayHello.class);
enhancer.setCallback(new MyMethodInterceptor());
SayHello sayHello = (SayHello) enhancer.create();
System.out.println(sayHello.sayHello("I LOVE YOU"));
}

Enhancer中的回调函数需要我们实现MethodInterceptor接口然后重写里面的intercept方法:

1
2
3
4
5
6
7
8
9
10
11
12
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("this is MethodIntercetpor->intercept()");
return methodProxy.invokeSuper(o, objects);
}
}

在intercept方法中有4个参数,含义如下:

  • o:代表委托对象。
  • method:代表代理对象执行的委托对象方法。
  • objects:代表方法中传入的参数。
  • methodProxy:代表代理对象。

在intercept方法中可以使用methodProxy.invokeSuper(o, objects);来调用委托对象中的原生方法。

总结

代理模式就是给外界提供一个代理类,来控制对委托类的访问或者实现其他功能。代理模式有两种实现方法:静态代理和动态代理。静态代理是手动编写或者使用工具生成代理类,特点是在程序运行前代理类的.class文件就已经存在,代理不同的类就需要编写不同的代理类。而动态代理是在程序运行期间动态的生成代理类的.class文件,对于一类代理操作,只需要实现一个代理类即可代理不同的类。实现动态代理有两种方法,一种是使用Java反射机制,通过实现和委托类相同的接口,来生成代理类的字节码文件,这种方式是JDK动态代理。另一种是通过继承产生子类覆盖委托方法的方式实现代理,这种方式是CGLIB动态代理。