设计模式学习(六):代理模式
作者:Grey
原文地址:
代理模式
代理模式是结构型模式,分为静态代理和动态代理。
静态代理
举个例子,假设需要在某个类的某段代码的前后加上日志记录,就可以通过静态代理的方式实现,代码如下
public class Main {
public static void main(String[] args) {
new Tank().move();
}
}
假设需要在move
方法的前后都加上日志记录,我们可以设置一个代理类
public class TankLogProxy implements Moveable {
private Moveable m;
public TankLogProxy(Moveable m) {
this.m = m;
}
@Override
public void move() {
System.out.println("log before");
m.move();
System.out.println("log after");
}
}
通过上述改造,原先的调用就改成了
public class Main {
public static void main(String[] args) {
new TankLogProxy(new Tank()).move();
}
}
即可实现在 move 方法调用前后加入日志记录的操作。
UML图如下:
动态代理
JDK 自带方式
即实现InvocationHandler
接口。
还是以上例说明,如果需要通过 JDK 自带的方式来完成上述功能,可以这样来做
public class MovableProxy implements InvocationHandler {
private Movable movable;
public MovableProxy(Movable movable) {
this.movable = movable;
}
public void before() {
System.out.println("before , do sth");
}
public void after() {
System.out.println("after , do sth");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object o = method.invoke(movable, args);
after();
return o;
}
}
主方法调用的时候:
public class Main {
public static void main(String[] args) {
Movable tank = new Tank();
//reflection 通过二进制字节码分析类的属性和方法
Movable m = (Movable) Proxy.newProxyInstance(Movable.class.getClassLoader(),
new Class[]{Movable.class},
new MovableProxy(tank)
);
m.move();
m.go();
}
}
UML图如下:
Cglib
JDK 自带的方式实现动态代理需要被代理对象实现一个接口, Cglib 不需要,使用示例:
需要引入 Cglib 依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
其中被代理的 Tank 类无需实现接口
public class Tank {
public void move() {
System.out.println("tank move");
}
public void go() {
System.out.println("tank go");
}
}
import net.sf.cglib.proxy.Enhancer;
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//设置目标类的字节码文件
enhancer.setSuperclass(Tank.class);
//设置回调函数
enhancer.setCallback(new MyMethodInterceptor());
//这里的creat方法就是正式创建代理类
Tank m = (Tank) enhancer.create();
m.move();
m.go();
}
}
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 obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
Object o = proxy.invokeSuper(obj, args);
after();
return o;
}
public void before() {
System.out.println("before , do sth");
}
public void after() {
System.out.println("after , do sth");
}
}
无论是 JDK 自带动态代理还是 Cglib 实现动态代理,底层都是基于ASM操作二进制码,基于Java Instrumentation机制。
代理模式的实际应用场景如下
场景一
在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志、缓存等,我们将这些附加功能与业务功能解耦,放到代理类中统一处理。
场景二
RPC 框架可以看成一种代理模式。
场景三
Spring 中的 JdkDynamicAopProxy 和 CglibAopProxy
可以使用<aop:aspectj-autoproxy proxy-target-class="true">
配置强制使用 Cglib 动态代理