学习动态代理之前需要先搞清楚静态代理,可以回看我之前在Mybatis里写的代理那一篇文章
MyBatis入门基础3 ---代理
动态代理使用反射机制,在程序执行过程中,创建代理类对象,无需创建类文件,代理的目标类是活动的,可以设计的。
动态代理需要的三个类需要使用反射包 java.lang. reflect里面有三个类:
InvocationHandler(接口):表示代理要干什么(功能增强)
Method:表示目标类的方法
Proxy:创建代理对象
InvocationHandler接口InvocationHandler是一个接口,在这个接口中只有一个invoke()方法,我们将代理类要完成的功能写在invoke()中,我们来看一下invoke()方法
Object invoke(Object proxy,Method method,0bject[] args)
invoke有三个参数
proxy:jdk创建的代理对象,无需赋值
method:目标类中的方法,也是jdk提供的
0bject[] args:目标类中方法的参数,jdk提供的
用法:首先创建一个类实现InvocationHandler接口,然后重写invoke(),我们将代理要完成的功能写在invoke()中,代理要实现的功能有两个:1.调用目标方法,2,完成对目标方法的增强
Method类通过method可以执行某个目标类的方法。
- method.invoke(目标对象,方法参数),这里的invoke和上述接口中的invoke碰巧同名而已。
我们通过调用Proxy里的静态方法newProxyInstance()创建代理对象,返回值就是代理对象
public static Obiect newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
newProxyInstance方法依然是有三个参数
ClassLoader loader:目标对象的类加载器,通过反射获取
Class[] interfaces:目标对象实现的接口,通过反射获取的
InvocationHandler h:我们自己写的代理类要完成的功能
实现动态代理的步骤:- 创建接口,定义目标类要完成的功能
- 创建目标类实现接口
- 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
-
- 调用目标方法
- 增强功能
- 使用Proxy类的静态方法,创建代理对象。并把返回值转为接口类型。(目标类一定是继承接口的,所以代理类对象可以返回接口类型)
我们来演示一下,我们以卖U盘为例,先看结构
厂家为目标类:定义U盘初始价格。商家为代理,首先获取厂家的初始价格,然后增加价格,我们以商店为测试,查看我们的购买价格是多少。
先定义目标接口:service.UserSell 这里我们定义了此次测试的主要功能:sell方法
package DynamicProxy.service;
public interface UsbSell {
Integer sell(int amount);
}
定义目标类实现目标接口:factory.UsbFactory 厂家重写sell方法,定义出厂价为85元。
import DynamicProxy.service.UsbSell;
public class UsbFactory implements UsbSell {
@Override
public Integer sell(int amount) {
System.out.println("目标类执行的sell目标方法");
return 85;
}
}
接下来实现带来要完成的功能:handler.SellHandler ---- 增加价格
package DynamicProxy.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class SellHandler implements InvocationHandler {
private Object target = null;
public SellHandler(Object target) {
//此时target就是目标对象
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
//proxy:目标对象;method:目标方法;args:目标方法的参数;
//method.invoke(target,args); ----- 调用目标方法
res = method.invoke(target,args);
//增强功能 :就是加个价格,目标方法卖85,代理完之后卖95
if(res != null){
Integer price = (Integer)res;
price += 10;
res = price;
}
System.out.println("目标方法被增强了");
return res;
}
}
最后在商店中进行测试:MainShop
package DynamicProxy;
import DynamicProxy.factory.UsbFactory;
import DynamicProxy.handler.SellHandler;
import DynamicProxy.service.UsbSell;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class MainShop {
public static void main(String[] args) {
//使用proxy创建代理对象
//1.创建目标对象(多态)
UsbSell factory = new UsbFactory();
//2.创建InvocationHandler对象 -- 把factory传给SellHandler,相当于我们要给factory对象增强
InvocationHandler handler = new SellHandler(factory);
//3.创建代理对象,返回值是接口类型,目标对象实现了接口,所以可以转成接口类型
UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces (),handler);
//4.测试:通过代理对象执行方法
System.out.println("动态代理增强后的价格是:"+proxy.sell(1));
}
}
查看结果
我们发现,当我们以代理类调用sell方法时,目标方式执行了,代理增强的功能也执行了。
在静态代理中,一个代理类只能代理一个目标类,同时可能有多个代理类代理同一个目标类,当我们修改了目标类中的目标方法时,所有的代理类都会受到影响,需要重写目标方法。
而动态代理无需指定目标类,在上述案例中 实现代理增强功能时,我们以Object target来接收目标类。理论上我们可以接收无限多个目标类,当后续修改或是添加目标方法时,我们只需要在这里修改增强方法即可,减少了后期维护的成本。



