代理模式
为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托类(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不实现具体服务,而是利用委托类来完成服务,并将执行结果封装处理。
简而言之就是代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类(前置处理),之后还能进行消息的后置处理。代理类和被代理类通常会存在关联关系(代理类持有的被代理对象的引用),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。
静态代理
创建一个接口声明一个抽象方法。然后创建一个被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口,在代理类中持有一个被代理对象的引用,而后在代理类方法中进行一些前置处理、调用该对象的方法、后置处理。最后直接通过代理类对象调用代理方法,完成代理。
缺点:静态代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。
动态代理
动态代理是程序在运行期间动态构建代理对象和动态调用代理方法的一种机制。
有点反射的意思,实际上,动态代理的常用实现方式就是反射,但动态代理不止有反射一种实现方式,例如, CGLib 是基于 ASM(一个 Java 字节码操作框架)而非反射实现的。动态代理是一种行为方式,而反射或 ASM 只是它的一种实现手段。
动态代理有两种方式,JDK Proxy 和 CGLib,它们的区别主要体现在以下几个方面:
- JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现;CGLib 是第三方提供的工具,需要引入三方包;
- Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新 JDK Proxy,例如 Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;CGLib 的性能则比较高。
- JDK Proxy 是通过拦截器加反射的方式实现的;CGLib 是基于 ASM 实现的;
- JDK Proxy 只能代理继承接口的类;CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的;
- JDK Proxy 实现和调用起来比较简单。
JDK Proxy 动态代理实现
JDK Proxy 动态代理的实现无需引用第三方类,只需要实现 InvocationHandler 接口,重写 invoke() 方法即可,简单实现:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxyExample {
static interface Animal {
void running();
}
static class Dog implements Animal {
@Override
public void running() {
System.out.println("The dog is running.");
}
}
static class Cat implements Animal {
@Override
public void running() {
System.out.println("The cat is running.");
}
}
static class JDKProxy implements InvocationHandler {
private Object target; //代理对象
// 获取到代理对象
public Object getInstance(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* 重写执行代理方法
* @param proxy 代理对象
* @param method 代理方法
* @param args 方法的参数
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理之前的业务处理.");
Object result = method.invoke(target, args);// 执行调用方法
System.out.println("动态代理之后的业务处理.");
return result;
}
}
public static void main(String[] args) {
// 执行JDKProxy
JDKProxy proxy = new JDKProxy();
Animal animal = (Animal) proxy.getInstance(new Cat());
animal.running();
}
}
执行结果:
动态代理之前的业务处理.
The cat is running.
动态代理之后的业务处理.
JDK Proxy 实现动态代理的核心是实现 InvocationHandler 接口,我们查看 Invocation 的源码,会发现里面其实只有一个 invoke() 方法,源码如下:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
这是因为在动态代理中有一个重要的角色也就是代理器,它用于统一管理被代理的对象,显然 InvocationHandler 就是这个代理器,而 invoke() 方法则是触发代理的执行方法,我们通过实现 InvocationHandler 接口来拥有动态代理的能力。
CGLib 动态代理实现
先引入三方包依赖,在 pom.xml 中添加如下配置:
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
简单实现:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibExample {
static class Car {
public void running() {
System.out.println("The car is running.");
}
}
/**
* CGLib 代理类
*/
static class CGLibProxy implements MethodInterceptor {
private Object target; // 代理对象
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
// 设置父类为实例类
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method,
Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法调用前业务处理.");
Object result = methodProxy.invokeSuper(o, objects); // 执行方法调用
System.out.println("方法调用后业务处理.");
return result;
}
}
// 执行 CGLib 的方法调用
public static void main(String[] args) {
// 创建 CGLib 代理类
CGLibProxy proxy = new CGLibProxy();
// 初始化代理对象
Car car = (Car) proxy.getInstance(new Car());
// 执行方法
car.running();
}
}
可以看出 CGLib 和 JDK Proxy 的实现代码比较类似,都是通过实现代理器的接口,再调用某一个方法完成动态代理的,唯一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。因此被代理类不能被关键字 final 修饰,如果被 final 修饰,再使用 Enhancer 设置父类时会报错,动态代理的构建会失败。
动态代理底层实现
动态代理具体步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。