java 代理、静态代理、动态代理
代理
在某些情况下,客户不想或者不能直接引用另一个对象,这时候代理对象可以在客户端和目标对象之间起到中介的作用。
代理对象提供一种代理以控制对委托对象的访问控制,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
通常情况下代理类和委托类实现相同的接口或继承相同的父类,代理类中通过聚合方式持有委托类的对象,代理类对象本身并不真正实现服务,而是通过调用委托类对象的相关方法来提供相关服务,并做一些额外操作。
代理的优点:
一、可以隐藏委托类的实现,实现客户与委托类之间的解耦;
二、在不修改委托类代码的情况下能够做一些额外的处理;
按照代理类的创建时间,代理分为静态代理和动态代理。
静态代理
代理类在代码编译之前已经存在叫做静态代理,一般由程序员创建。
静态代理类通常只代理一个委托类。
/**
* 静态代理类和委托类需要共同实现的接口
* */
public interface Subject {
void sell();
void print(String msg);
}
/**
* 委托类,实现了接口
* */
public class RealSubject implements Subject {
@Override
public void sell() {
System.out.println("in sell method");
}
@Override
public void print(String msg) {
System.out.println("in print method, msg " + msg);
}
}
/**
* 静态代理类
* 也实现了接口,但接口方法和实现是调用了委托类的实现
* 静态代理类在方法中对委托类的实现进行了增强,甚至过滤
* */
public class ProxySubject implements Subject {
private Subject subject; // 代理类通过聚合持有委托类实现对象的引用
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void sell() {
System.out.println("before");
subject.sell(); // 代理类中调用委托类的实现
System.out.println("after");
}
@Override
public void print(String msg) {
System.out.println("before");
subject.print(msg); // 代理类中调用委托类的实现
System.out.println("after");
}
}
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject(); // 事先已存在的委托类对象
ProxySubject proxySubject = new ProxySubject(realSubject); // 创建代理类,传入已存在的委托类对象
proxySubject.sell(); // 调用代理类方法,代理类中调用委托类对象相关方法实现服务
proxySubject.print("this is message");
}
}
动态代理
代理类在程序运行时创建的代理方式叫做动态代理。
首先通过newProxyInstance方法创建代理类并获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法,在invoke方法中我们调用委托类的相应方法,并且可以添加自己的处理逻辑。
动态代理的接口和委托类与静态代理相同。
/**
* 中介类
* */
public class DynamicProxy implements InvocationHandler {
// 中介类通过聚合方式持有委托类对象,subject为委托类对象
// 外部对代理类对象的调用转化为对invoke的调用,invoke内再调委托类对象
// 中介类与委托类构成了静态代理关系,在这里,中介类就是代理类,委托类就是委托类
private Object subject;
public DynamicProxy(Object obj) {
this.subject = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result = method.invoke(subject, args);
System.out.println("after");
return result; // 可以返回 null
}
}
public class Main {
public static void main(String[] args) {
// 创建被委托类对象
RealSubject vendor = new RealSubject();
// 创建中介类实例
DynamicProxy inter = new DynamicProxy(vendor);
// 加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
// 获取代理类实例sell
// 动态生成代理类,代理类的方法由接口指定,代理类方法的调用由handler处理,handler中方法的处理由委托类处理
Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[] {Subject.class}, inter);
// 通过代理类对象调用代理类方法,实际上会转到invoke方法调用
proxySubject.sell();
proxySubject.print("this is message");
}
}
首先,通过Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[] {Subject.class}, handler);
在程序运行时生成动态代理类。参数1是生成动态代理类的类加载器;参数2是生成的代理类要实现的接口;参数3是调用代理类方法后实际处理方法调用的handler。
当调用代理类对象的方法时,这个“调用”会转送到handler的invoke(Object proxy, Method method, Object[] args)
方法中,代理类对象作为proxy
参数传入,参数method
标识了我们具体调用的是代理类的哪个方法,args
为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。
中介类通过聚合方式持有一个委托类对象引用,在invoke方法中调用了委托类对象的相应方法,把外部对invoke的调用最终都转为对委托类对象的调用。这就像上面的静态代理的实现方式,实际上,中介类与委托类构成了静态代理关系,在这个关系中,中介类是代理类,委托类就是委托类;代理类与中介类也构成一个静态代理关系,在这个关系中,中介类是委托类,代理类是代理类。也就是说,动态代理关系由两组静态代理关系组成,这就是动态代理的原理。
动态代理与静态代理区别
一、动态代理类代理接口下的所有类,而静态代理通常只代理一个类;
二、动态代理只能代理接口,而静态代理还可以代理普通共同父类;
三、动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。而静态代理在代码编写时就知道;
动态代理的优势
相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。