代理模式简介:
简介整理并摘自网络
1. 代理分为静态代理和动态代理。
2. 静态代理:
创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
类和接口都是在程序运行前就定义好了的,使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。
- 动态代理:
利用反射机制在运行时创建代理类。
接口、被代理类不变,我们构建一个handler类来实现InvocationHandler接口。
动态代理具体步骤:
通过实现 InvocationHandler 接口创建自己的调用处理器;
通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
以下为个人理解
如何实现动态代理?
前置条件:
1. 需要有一个接口类,负责方法的声明
2. 需要有一个目标类,实现接口类中的方法
3. 需要有一个代理类,实现接口InvocationHandler
实例讲解:
例如,我们要实现 销售U盘 的一个功能,其中供应商每个U盘定价85元,代理商会从供应商拿货,并加价25元卖出去。那么根据上面的条件,我们就需要:
- 一个接口UsbSell,声明sell方法。`
public interface UsbSell {
public float sell(int amount);
}
- 一个目标类UsbKingFactory,负责初步实现接口中的方法。
// 目标类,实现接口
public class UsbKingFactory implements UsbSell{
@Override
public float sell(int amount) {
return amount * 85.0f;
}
}
- 一个代理类,负责对目标类中方法的 调用 以及 增强
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MySellHandler implements InvocationHandler{
private Object target;
// 提供一个构造方法,来接收目标对象target,在本例中传入了对象 factory
public MySellHandler(Object target) {
this.target = target;
}
// 代理对象的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用目标对象里的方法
Object result = method.invoke(target,args);
if(result != null){
// 代理商从 目标对象 购入U盘后,加价25元
float price = (float) result;
price += 25.0 * (int)args[0];
result = price;
}
System.out.println("若出现此语句,表明执行的是MySellHandler中的invoke方法。");
// 返回处理结果
return result;
}
}
- 至此,我们还需要有一个Test类来运行测试实际效果。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class MainShop {
public static void main(String[] args) {
// 1.创建目标对象
UsbSell factory = new UsbKingFactory();
// 2.创建InvocationHandler对象,传入目标对象
InvocationHandler handler = new MySellHandler(factory);
// 3.创建代理对象,分别传入 目标对象的类加载器, 目标对象的接口, handler
// 个人理解:UsbSell proxy = new UsbKingFactory();
// 故 proxy可以调用 UsbSell 这个接口里的方法
// 前两个参数指明了目标对象的类加载器和接口
// 最后一个参数handler指明了要用handler里的invoke方法来代替factory里“任何要被执行“的方法
UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),handler);
// 这里的sell是代理类中的sell,即MySellHandler中的invoke方法
float price = proxy.sell(2);
System.out.println("通过动态代理对象,调用方法sell:" + price);
}
}
- 运行效果如下
若出现此语句,表明执行的是MySellHandler中的invoke方法。
通过动态代理对象,调用方法sell:220.0
此时我们已经成功达到了我们预期效果,最后简单总结一下就是,
- 首先有一个接口来声明方法,然后目标类去实现这个接口。
- 我们另外定义一个handler类,实现InvocationHandler接口,重写invoke方法即可,方法的参数由JDK自动帮我们完成,不需要我们管,我们只需要负责在invoke中将代理类要做的事写上去即可。
例如本例中代理商要从供货商拿货并加价25卖出去,那么最终售卖价格就是,原价+25,那么我们可以首先在Handler类中创建一个代理商对象以及声明一个构造方法,用来接收代理商,然后在invoke中调用代理商实现的sell方法获得其售价,然后我们加上25并return该最终结果出去。
- 创建对象传入的参数一般是固定的,例如
UsbSell proxy = (UsbSell)
Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),handler);
- 另外对于Handler类中invoke参数做一些解释: method指代目标类中执行的方法,我们就是要对该方法进行代理和增强,后面的args就是该方法需要的参数。如下。
// 调用目标对象里的方法
Object result = method.invoke(target,args);