代理模式是指:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户对象和目标对象之间起到中介的作用。
package proxy_pattern.static_proxy;
public interface UsbSell {
float sell(int amount);
}
(2)创建目标类,实现接口中的方法
package proxy_pattern.static_proxy;
/**
* 定义目标类, 实现接口中的方法
*/
public class UsbFactory implements UsbSell{
@Override
public float sell(int amount) {
return 30f * amount;
}
}
(3)创建代理类,也实现接口中的方法,把目标对象作为一个参数,传入代理类的构造方法中,作为代理对象的一个属性;在方法中调用目标对象的方法,同时可以做一些功能增强。
package proxy_pattern.static_proxy;
/**
* 本质上就是为代理类传入一个目标类对象, 或者直接在代理类中 new 一个目标类对象
*
* 然后在方法中调用目标对象的方法, 同时也可以做一些功能增强 / 控制访问
*/
public class UsbProxy implements UsbSell{
//使用构造方法从外面传入一个目标类的对象
private UsbFactory usbFactory;
public UsbProxy(UsbFactory usbFactory){
this.usbFactory = usbFactory;
}
//直接在代理类中创建一个目标类对象
// private UsbFactory usbFactory = new UsbFactory();
@Override
public float sell(int amount) {
float price = usbFactory.sell(amount); //调用目标对象的方法
price += 20; //使用代理做功能增强
return price;
}
}
(4)创建客户类
package proxy_pattern.static_proxy;
/**
* 1. 创建一个目标对象
* 2. 创建一个目标对象的代理对象,把目标对象作为一个参数传入
* 3. 通过调用代理对象的sell方法,完成对目标对象sell方法的调用
*/
public class Test {
public static void main(String[] args) {
UsbFactory usbFactory = new UsbFactory(); // 1.
UsbProxy usbProxy = new UsbProxy(usbFactory); //2.
System.out.println("使用代理,购买10个: "+usbProxy.sell(10)); // 3.
}
}
使用Java.reflect包里的类和接口实现动态代理,具体使用到了InvocationHandler、Method、Proxy,具体创建接口,创建目标类与前面的静态代理一样,不同的是如何创建代理对象。
package proxy_pattern.dynamic_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 在 invoke()方法中需要做的:
* 1. 完成目标对象方法的回调
* 2. 完成功能增强(如果需要的话)
*/
public class SellHandler implements InvocationHandler {
private Object target;
public SellHandler(Object target){ //创建 handler对象的时候, 需要把目标对象(被代理对象)作为参数传进来
this.target =target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 参数1: 代理对象本身
// 参数2: 被代理的方法对象
// 参数3: 被代理方法应该传入的参数
Object res = method.invoke(target, args); // 回调目标对象的方法, 使用res保存
if(res != null){
Float price = (Float) res;
price += 20;
res = price;
}
return res;
}
}
(4)在测试类中调用Proxy的静态方法,创建代理对象,并调用代理类的方法
这里其中三个参数
第一个:ClassLoader loader类加载器,是我们目标接口(target)实现类的类加载器
通过 target.getClass().getClassLoader() 获取
第二个:Class[] interfaces为我们的目标接口
通过target.getClass().getInterfaces() 获取
第三个:是一个InvocationHandler 类型的对象,这只是一个接口,我们需要实现这个接口,实现接口中的invoke()方法
package proxy_pattern.dynamic_proxy;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
UsbFactory usbFactory = new UsbFactory();
SellHandler handler = new SellHandler(usbFactory); //把目标对象传入
UsbSell usbSell = (UsbSell) Proxy.newProxyInstance(usbFactory.getClass().getClassLoader(),
usbFactory.getClass().getInterfaces(), handler);
System.out.println("使用动态代理,购买10个:"+ usbSell.sell(10));
}
}
使用Proxy类生成代理对象的过程:
注意:
1.在JDK动态代理中,要求目标类实现接口,如果目标类没有实现接口,那么在上面的target.getClass().getInterfaces()中,将 获取不到任何内容
2.JDK动态代理,是在程序运行过程中创建代理对象的,这与静态代理先生成代理类的.class文件,再使用代理类对象完全不同!
使用JDK的Proxy创建代理对象,要求目标类和代理类实现相同的接口。若目标类不存在接口,则无法使用该方式实现,那么也就不能使用JDK动态代理。这个时候,可以使用CGLib动态代理,但它需要引入对应的jar包。
CGLib代理的生成原理是生成目标类的子类,而子类是通过增强过的,这个子类对象就是代理对象,所以,使用cglib生成动态代理,要求目标类必须能够被继承,即不能是final的类。
1、静态代理需要手动写代理类,并且在代理类中传入对应的目标类对象( 直接在代理类中new 或者使用构造方法传入);如果有多个目标类,需要在代理类中传入对应的目标对象;或者直接为每个目标类创建一个代理类。代码重用性极差!
2、JDK动态代理是JDK里自带的, 要求目标类实现接口,对于没有实现任何接口的目标类,不能使用JDK动态代理。
3、CGLib动态代理,它是在内存中构建一个子类对象,从而实现对目标对象功能的扩展;是基于继承来实现代理,所以 无法对final类、private方法和static方法进行代理;需要引入第三方的jar包。