动态代理
java.lang.reflect.Proxy
通过"Proxy"类提供的一个newProxyInstance方法用来创建一个对象的代理对象
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
newProxyInstance方法用来返回一个代理对象,这个方法总共有3个参数:
- ClassLoader loader 指明生成代理对象使用哪个类装载器 【指定 代理对象 】
- Class<?>[] interfaces 指明生成哪个对象的代理对象,通过接口指定. 【指定 被代理对象----使用接口指定,这应就能使用这个接口的所有实现类】
- InvocationHandler h 指明产生的这个代理对象要做什么事情。【这是一个接口,通过实现它的 invoke 方法,来指定动态代理要做什么事情】
所以我们只需要调用newProxyInstance方法就可以得到某一个对象的代理对象了。
注意:在java中规定,要想产生一个对象的代理对象,那么这个对象必须要有一个接口,原因不难分析,如果是指定一个类而不是接口,就只能代理一个类(它的子类),那就一个类,动态代理了个寂寞呀。指定接口就能代理这个接口所有的实现类。
出租房子事例:
定义接口——租房子
抽象行为,出租房子
public interface Rent {
public void rent();
}
定义实现类——房东
这里定义了三个实现类,对应了三个房东
public class Host implements Rent {
@Override
public void rent() {
System.out.println("晓军东莞好地段大量房屋出租");
}
}
public class Host2 implements Rent{
@Override
public void rent() {
System.out.println("凯文大量出租深圳楼盘!");
}
}
public class Host3 implements Rent{
@Override
public void rent() {
System.out.println("老van黄牛二手转租,凯文晓军 的大房子");
}
}
实现代理类
代理类干的事情,在这里是 房屋中介 帮房东去 干的事情
// 使用反射导入的一些方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyProxy{
// 定义一个 出租房子的接口对象
private Rent rent;
// setter 方法
public void setRent(Rent rent) {
this.rent = rent;
}
// 获取一个代理对象
public Object getProxy() {
// 使用 Proxy.newProxyInstance(...) 方法获取代理对象
return Proxy.newProxyInstance( this.getClass().getClassLoader(), rent.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHost();
Object result = method.invoke(rent, args);
face();
return result;
}
}
);
}
// 代理帮房东干的事情
public void seeHost(){
System.out.println("看房");
}
// 代理帮自己干的事情
public void face(){
System.out.println("收中介费");
}
}
Proxy.newProxyInstance(…) 的 参数解析说明:
-
this.getClass().getClassLoader() 获取代理类自身的类加载器
-
rent.getClass().getInterfaces() 获取代理对象的接口(虽然rent本来就是接口引用,但是在使用的时候,会把 接口对象引用 rent 指向它的实现类,所以需要这样子来获取接口)
-
new InvocationHandler() 这是一个匿名内部类,它没有类名,默认是抽象类和接口 的实现类 (所以匿名内部类在实现调用接口上非常方便)
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHost(); Object result = method.invoke(rent, args); face(); return result; }
在匿名内部类中,实现了 接口 InvocationHandler 的 invoke 方法,我们来看一下它的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy 被代理对象,method 被代理对象的方法,args 方法的参数列表。
**看房–seeHost() 和 收中介费 face() **
seeHost(); Object result = method.invoke(rent, args); face();
因为是内部类,所以可以访问 外部类所有 的 私有的 private 或者 公有 public 的 属性 和 方法。所以在执行被代理对象的方法的前后,可以直接使用方法名,执行代理类内部实现的一些 代理方法
被代理的方法
method.invoke(rent, args);
相当于 proxy.method(args); 这里是反射的调用方式。
为什么要用反射来写这个方法呢?而不是直接写?
因为这个重写了 InvocationHandler 接口的 invoke 方法后,调用被代理对象的 所有方法都将被 代理类所代理 ,这个概念有点类似于我们之前使用到的过滤器,即 invoke方法拦截到了 代理对象的 所有方法调用,并像过滤器一样给它前后进行了一些 代理操作。
就像我们之前用过滤器 把所有请求体的 字符编码 都改成 UTF-8 来防止乱码一样。
测试类
public class Client {
public static void main(String[] args) {
Host host = new Host();
// Proxy proxy = new Proxy(host);
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host);
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
可能你会问为什么不用 有参构造的 方式 来使代码更加 简洁,因为 在 Spring 中 使用 Setter 注入 会 远比 使用 构造器 注入 多
// 有参构造 注入代理对象 host
ProxyInvocationHandler pih = new ProxyInvocationHandler(host);
// 无参构造 + setter 方法 注入代理对象 host
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host);
习惯性使用容器的 Setter 注入方法
获取代理对象 proxy
Rent proxy = (Rent) pih.getProxy();
使用 代理对象 proxy 调用 被代理对象 host 的方法
proxy.rent();
通过代理对象执行的方法,都会通过我们在 接口 InvocationHandler 中 实现的 invoke 方法 去调用,结果相当于
proxy.seeHost();
host.rent();
proxy.face();
执行结果截图:
拓展
invoke 方法是通过反射去调用的,因此你能在 invoke 方法中通过 反射,类名,方法名,属性名,方法参数的值,方法的注解(注解值),方法参数列表的值 等等,获取所有 反射能获取到的 信息(类的全部信息)。