Java动态代理-Proxy
目录
- 一、概念
- 二、构成
- 三、作用与特点
四、分类
- 4.1 基于接口的动态代理
- 4.2 基于子类的动态代理
一、概念
动态代理提供一种抽象,能够将对象中不同方法的调用重定向到一个统一的处理函数,做自定义的逻辑处理。但是对于调用者,对此毫无察觉,就好像调用的方法是用传统方式实现的一般。Java动态代理是利用Java的反射技术,在运行时创建动态代理类及其对象。
动态代理类:一个实现某些给定接口的新类
二、构成
基于接口的动态代理的组成:
① 接口:定义真实对象和代理对象需要实现的接口
② 真实对象:被代理的对象,必须实现接口。
③ 代理对象:增强代码部分
基于子类的动态代理
① 真实对象:被代理的对象
② 代理对象:增强代码部分
三、作用与特点
作用:不修改源码的基础上,对方法增强。
动态代理的特点:字节码随用随创建,随用随加载
它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
装饰者模式就是静态代理的一种体现。
基于接口的动态代理,代理的是接口(Interface),而不是类(Class)。
四、分类
4.1 基于接口的动态代理
需求:
定义打折接口(接口)
定义超市真实类实现打折接口(真实对象)
在不改动超市真实类内部代码的前提现,增强超市类实现的接口的功能。(使用动态代理)
定义接口:
public interface Discount {
String sale(double price);
void today();
}
定义真实类,超市:
public class Supermarket implements Discount {
@Override
public void today() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("今天日期:" + sdf.format(date));
}
@Override
public String sale(double price) {
System.out.println("总共消费了"+price+"钱");
return "购买的商品";
}
}
句柄:
public class DiscountInvocationHandler implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
System.out.println("target=" + target);
// 绑定该类实现的所有接口,取得代理类
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 判断method是否是我们需要增强的方法sale
if ("sale".equals(method.getName())) {
// 获取需要增强的参数
double price = (double)args[0];
price *= 0.8;
// 增强method的方法体
System.out.println("增强方法体。。。");
// 增强method的返回值
String value = (String) method.invoke(this.target, price);
value += "+赠送礼品一件";
return value;
}
return method.invoke(this.target, args);
}
}
其中:
Proxy的静态方法newProxyInstance参数讲解:
ClassLoader loader:类加载器。它是用于加载代理对象字节码。和被代理对象使用相同的类加载器。固定写法。
Class<?>[] interfaces:接口数组,保证了代理接口和真实接口实现了相同的接口。真实对象实现的接口。也是固定写法。
InvocationHandler h 匿名内部类。核心代码在此处处理。
Object 返回值。称为代理对象。强转为接口。
接口invoke方法,代理对象调用的所有方法都会触发该方法执行。参数:
Object proxy:代理对象。一般不用该对象!!
Method method:方法对象。代理对象(proxyLenovo)调用的接口方法封装而成的对象。
Object[] args:代理对象调用方法时,传递的实际参数,封装到args数组了
Object:返回值。return null 表示没有返回值。
4.2 基于子类的动态代理
需要使用第三方jar包:cglib
、asm
注意:创建代理对象的要求:被代理类不能是最终类!!!!
一个生产者Producer.java,如何增强他的方法?
/** * 一个生产者 **/
public class Producer {
/** * 销售 * @param money */
public void saleProduct(float money) {
System.out.println("销售商品,价格:" + money);
}
/** * 售后 * @param money */
public void afterService(float money) {
System.out.println("提供售后服务,服务费:" + money);
}
}
/** * @description: 模拟消费者 **/
public class Client {
public static void main(String[] args) {
final Producer producer = new Producer();
Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
/** * 执行被代理对象的任何方法都会经过该方法。 * @param proxy * @param method * @param args * 以上三个参数和基于接口的动态代理中invoke方法的参数是一样的 * @param methodProxy 当前执行方法的代理对象 * @return 和被代理对象有相同的返回值 * @throws Throwable */
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
// 1.获取方法执行的参数
Float money = (Float)args[0];
// 2.判断当前方法是否是销售
if ("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money * 1.2f);
}
return returnValue;
}
});
cglibProducer.saleProduct(10000f);
}
}
推荐博文:
传送门
还没有评论,来说两句吧...