代理模式
定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。
在上图中:
- RealSubject 是原对象(也称为”委托对象”),Proxy 是代理对象
- Subject 是委托对象和代理对象都共同实现的接口。
- Request() 是委托对象和代理对象共同拥有的方法。
生活中的代理模式:
远程代理:我们在国内因为GFW,所以不能访问 facebook,我们可以用翻墙(设置代理)的方法访问。访问过程是:
(1)用户把HTTP请求发给代理
(2)代理把HTTP请求发给web服务器
(3)web服务器把HTTP响应发给代理
(4)代理把HTTP响应发回给用户
Java 实现上面的UML图的代码(即实现静态代理)为:
public class ProxyDemo {
public static void main(String args[]){
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}
interface Subject{
void request();
}
class RealSubject implements Subject{
public void request(){
System.out.println("request");
}
}
class Proxy implements Subject{
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
System.out.println("PreProcess");
subject.request();
System.out.println("PostProcess");
}
}
代理的实现分为:
- 静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。
- 动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class文件,而是在运行时动态生成的类字节码,并加载到JVM中。
JDK动态代理
为了提高代理的灵活性和可扩展性,减少重复代码,我们可以使用JDK提供的动态代理。
实现JDK动态代理的步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..);
// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
实际使用过程更加简单,因为 Proxy 的静态方法 newProxyInstance 已经为我们封装了步骤 2 到步骤 4 的过程:
public class DynamicProxyDemo01 {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject(); //1.创建委托对象
ProxyHandler handler = new ProxyHandler(realSubject); //2.创建调用处理器对象
Subject proxySubject =(Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(),handler); //3.动态生成代理对象
proxySubject.request(); //4.通过代理对象调用方法
}
}
/**
* 接口
*/
interface Subject{
void request();
}
/**
* 委托类
*/
class RealSubject implements Subject{
public void request(){
System.out.println("====RealSubject Request====");
}
}
/**
* 代理类的调用处理器
*/
class ProxyHandler implements InvocationHandler{
private Subject subject;
public ProxyHandler(Subject subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("====before====");//定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}
Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。每次生成动态代理类对象时都需要指定一个类装载器对象。
基于CGLIB的动态代理
由于JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。
CGlib概述:
- cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
- cglib封装了asm,可以在运行期动态生成新的class。
cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
有一个Manager类:
public class Manager {
public void query() {
System.out.println("query...");
}
public void insert() {
System.out.println("insert...");
}
public String update() {
System.out.println("update....");
return "I'm update";
}
public void delete() {
System.out.println("delete....");
}
}
我们想要在这个类中的每个方法前面和后面都打印一句话。
public class AuthProxy implements MethodInterceptor{
@Override
public Object intercept(Object arg0, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("Before...");
Object result = proxy.invokeSuper(arg0, args);
System.out.println("After....");
return result;
}
}
如上,CGLIB实现的代理类必须实现MethodInterceptor接口,该接口中只有一个方法需要实现,即intercept方法。通过MethodProxy中的invokeSuper即可执行被代理类的方法。