动态代理
特点
字节码随用随创建,随用随加载
作用
不修改源码的基础上对方法增强
分类
基于接口的动态代理和基于子类的动态代理。
基于接口的动态代理涉及的类:Proxy。提供者JDK官方,也就是我们常说的JDK动态代理。
基于子类的动态代理第三方cglib库
JDK动态代理
使用Proxy类中的newProxyInstance方法创建代理对象,并且要求被代理对象最少实现一个接口,如果没有实现接口则没有办法使用。
newProxyInstance方法的参数:
ClassLoader
类加载器:它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
Class[]
字节码数组:它是用于让代理对象和被代理对象有相同方法。固定写法。
InvocationHandler
用于提供增强的代码:它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。此接口的实现类都是谁用谁写。
案例
以生产厂家卖东西为例,现在不需要厂家直接卖,而是通过一个代理对象,可以理解为现实生活中的经销商,他们来给我们提供销售服务。
首先定义一个接口,有两个方法,一个是销售方法,也就是拿到钱给货的方法,还有一个就是售后服务方法。我们只以销售方法为例来编写。
public interface IProducer {
public void saleProduct(float money);
public void afterService(float money);
}
然后定义一个生产商需要实现这个接口。
public class Producer implements IProducer{
public void saleProduct(float money){
System.out.println("拿到钱销售" +money);
}
public void afterService(float money){
System.out.println("提供售后服务,并拿到钱"+money);
}
}
最后我们可以模拟一个客户端进行客户消费,我们从代理对象那里买到商品。当然代理需要实现一些操作。一般代理商肯定会赚差价哈哈。我们可以理解为这是一个方法的增强逻辑,通过动态代理实现。
public class Client {
public static void main(String[] args) {
final Producer p = new Producer();
IProducer proxyInstance = (IProducer) Proxy.newProxyInstance(p.getClass().getClassLoader(),
p.getClass().getInterfaces(), new InvocationHandler() {
/**
* 作用:执行被代理对象的任何接口方法都会经过该方法
* 方法参数的含义
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象方法有相同的返回值
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
增强代码
*/
Object returnvalue=null;
float money =(Float)args[0];
// 这里只对销售方法进行增强,也就是代理商进行销售的时候从中截取20%的资金。
if ("saleProduct".equals(method.getName())){
returnvalue=method.invoke(p,money*0.8f);
}
return returnvalue;
}
});
//代理对象调用方法
proxyInstance.saleProduct(100000f);
}
}
程序运行输出结果如下:
拿到钱销售80000.0
cglib动态代理
涉及的类
Enhancer类 ,使用Enhancer类中的create方法进行创建
create方法参数
Class
字节码:用于指定被代理对象的字节码
Callback
用于提供增强的代码:它是让我们写如何代理。一般都是该接口的实现类,通常情况下使用匿名内部类,但不是必须的,此接口的实现类都是谁用谁写。
我们一般写的都是该接口的子接口的实现类:MethodInterceptor
========================
此时不要求被代理对象实现接口,但是要求被代理对象不是最终类,也就是说不能是final修饰,具体的底层原理目前还不太清楚。先把案例给出来。后期原理再慢慢补充。
案例
前提我们需要第三方库,因为我用的是maven创建的工程,直接导一个坐标就可以了。
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
只需要这么一个坐标就可以了。
同样模拟上述卖货的操作,只是此时我们创建的类不需要实现接口。
只需要有这么一个Producer类。
public class Producer {
public void saleProduct(float money){
System.out.println("拿到钱销售" +money);
}
public void afterService(float money){
System.out.println("提供售后服务,并拿到钱"+money);
}
}
客户端模拟销售操作。
public class Client {
public static void main(String[] args) {
final Producer p = new Producer();
Producer cglibporducer = (Producer) Enhancer.create(p.getClass(), new MethodInterceptor() {
/**
*执行此代理对象的任何方法都会经过该方法
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* 以上三个参数和基于接口中的动态代理中invoke方法参数是一样的
* @param methodProxy 当前执行方法的代理对象
* @return
* @throws Throwable
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
/*
增强代码
*/
Object returnvalue = null;
// 获取执行方法的参数
float money = (Float) args[0];
// 判断是否为销售
if ("saleProduct".equals(method.getName())) {
returnvalue = method.invoke(p, money * 0.8f);
}
return returnvalue;
}
});
cglibporducer.saleProduct(1000);
}
}
执行输出结果如下:
拿到钱销售800.0
这样我们就通过两种方式实现了动态代理。