设计模式 - 代理模式详解
农夫三拳有点疼~ 人气:1代理模式介绍
代理模式提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
代理模式分为三类:
- 静态代理
- 动态代理
- Cglib 代理
静态代理(不推荐)
介绍
要求目标对象和代理对象实现同一个接口,调用的时候调用代理对象的方法,从而达到增强的效果
优点:
可以在不修改目标对象的前提下,增强目标对象方法的功能(所有代理模式都可以实现,因此不推荐使用此方法)
缺点:
① 冗余。目标对象和代理对象实现同一个接口,会产生过多的代理类。
② 不易维护。当接口方法增加,目标对象与代理对象都要进行修改。
代码实现
场景:厂家生产了商品,但是没有足够的精力、人力去销售,这时候就需要一个代理商帮他售卖,但是代理商需要从中抽取 20% 的利润。
公共接口
public interface IProducer {
void sale(float money);
}
被代理对象
public class Producer implements IProducer {
@Override
public void sale(float money) {
System.out.println("卖出产品,厂家获得" + money + "元");
}
}
代理对象
public class ProxyProducer implements IProducer{
private IProducer producer;
public ProxyProducer(IProducer producer) {
this.producer = producer;
}
@Override
public void sale(float money) {
producer.sale(money * 0.8f);
}
}
测试类
public class Client {
@Test
public void test(){
IProducer producer = new Producer();
ProxyProducer proxyProducer = new ProxyProducer(producer);
proxyProducer.sale(1000f);
}
}
运行结果
卖出产品,厂家获得800.0元
动态代理
介绍
动态代理也称:JDK 代理、接口代理,需要目标对象实现接口,否则不能用动态代理,利用 JDK 的 API(java.lang.reflect.Proxy),动态地在内存中构建代理对象。
静态代理和动态代理的区别:
- 静态代理在编译时就已经实现,编译完后的代理类是一个实际的 class 文件
- 动态代理实在运行时动态生成的,编译后没有实际的 class 文件,而是在运行时动态的生成类字节码,并加载到 JVM 中
代码实现
以静态代理的情景为例,我们只需要修改代理对象的代码,代理对象不需要实现公共接口了。
public class ProxyProducer {
/**
* 维护一个目标对象
*/
private Object target;
public ProxyProducer(Object target) {
this.target = target;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 执行被代理对象的任何接口方法都会经过这里
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法的参数
* @return 和被代理对象具有相同的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理过程中执行一些方法
float money = (float) args[0] * 0.8f;
//反射机制调用目标对象的方法
Object invoke = method.invoke(target, money);
return invoke;
}
});
}
}
Cglib 代理
介绍
Cglib 代理也叫子类代理,目标对象不需要实现任何接口,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
Cglib 是一个强大的高性能的代码生成包,它可以在运行期间扩展 Java 类与实现 Java 接口,它广泛地被许多 AOP 的框架使用,例如 Spring AOP,用于实现方法拦截。
Cglib 包底层实通过使用字节码处理框架 ASM 来转换字节码并生成新的类。
在 AOP 编程中选择哪种代理模式?
- 目标对象需要实现接口,用 JDK 代理
- 目标对象不需要实现接口,用 Cglib 代理
代码实现
使用之前需要导入相关 jar 包,可去 maven 仓库下载
被代理对象,无需实现接口
public class Producer {
public void sale(float money) {
System.out.println("卖出产品,厂家获得" + money + "元");
}
}
代理对象
public class ProxyProducer implements MethodInterceptor {
/**
* 维护一个目标对象
*/
private Object target;
public ProxyProducer(Object target) {
this.target = target;
}
/**
* 为目标对象生成代理对象
*/
public Object getProxyInstance(){
//创建一个工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建子类对象(代理对象)
return enhancer.create();
}
/**
* 会拦截被代理对象的所有方法
* @param obj 增强对象
* @param method 被代理对象的方法
* @param args 被代理对象方法的参数
* @param methodProxy 代理对象
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("obj:" + obj.getClass());
Object returnValue = null;
float money = (float) args[0] * 0.8f;
if("sale".equals(method.getName())){
returnValue = method.invoke(target, money);
}
return returnValue;
}
}
测试类
public class Client {
@Test
public void test() {
Producer producer = new Producer();
Producer proxyInstance = (Producer) new ProxyProducer(producer).getProxyInstance();
proxyInstance.sale(1000f);
}
}
加载全部内容