什么是代理
- 一个类(代理类) 作为另外一个类的代理(被代理类)
- 为某些对象的某种行为提供一个代理对象,并由代理对象完全控制该行为的实际执行。
- 代理的好处
- 可以隐藏委托类的实现
- 可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理
静态代理
- 若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理
Example
定义一个接口和一个接口的实现类也就是委托类
public interface Sell {
void sell();
void ad();
}
public class Vendor implements Sell {
public void sell() {
System.out.println("In sell method");
}
public void ad() {
System,out.println("ad method")
}
}
定义一个代理类: BusinessAgent, 增加一个过滤功能,只卖货给大学生
通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码
public class BusinessAgent implements Sell {
Sell vendor;
public BusinesAgent (Sell vendor) {
this.vendor = vendor;
}
public void sell() {
if (isCollegeStudent()) {
vendor.sell();
System.out.println("In sell method");
}
}
public void ad() {
System,out.println("ad method")
}
}
静态代理的缺点
- 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
- 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
- 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
- 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护
动态代理
- 代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
委托类
- 和静态代理一样, 需要定义需要代理的接口和实现类
public interface Sell {
void sell();
void ad();
}
public class Vendor implements Sell {
public void sell() {
System.out.println("In sell method");
}
public void ad() {
System,out.println("ad method")
}
}
中介类
- 在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口,这个接口的定义如下:
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args);
}
- 当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,
- proxy: 代理类对象,Method: 代理类的某个方法,args: 这个方法的参数
- 这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用
- 我们可以在invoke方法中添加统一的处理逻辑(也可以根据method和参数对不同的代理类方法做不同的处理)
//下面对委托类的所有方法加上"before"和"after"
public class DynamicProxy implements InvocationHandler {
private Object obj; //obj为委托类对象;
public DynamicProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result = method.invoke(obj, args); //这里用到了Java的反射机制
System.out.println("after");
return result;
}
}
动态生成代理类 — 使用Proxy类
public static void main(String[] args) {
//创建中介类实例
DynamicProxy inter = new DynamicProxy(new Vendor());
//加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
//获取代理类实例sell
Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {
Sell.class}, inter));
//通过代理类对象调用代理类方法,实际上会转到invoke方法调用
sell.sell();
sell.ad();
}
- 我们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器。这个方法的声明如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
方法的三个参数含义分别如下:
- loader:定义了代理类的ClassLoder;
- interfaces:代理类实现的接口列表
- h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例
动态代理的主要作用
- 动态代理的主要用来做方法增强, 可以再不修改源代码的情况下,增强一些方法
- 远程调用, 比如现在有Java接口, 这个接口的实现部署在其他服务器上. 在编写客户端代码的时候, 没办法直接调用接口方法, 因为接口是不能直接生成对象的, 这个时候可以考虑动态代理
- 可以实现AOP