这两天没有什么工作任务,也不想去看所谓的业务,就利用闲来的时间复习一下java基础的东西吧。
下午复习了java动态代理,这里记下来。
java动态代理表面上并没有什么难的,难的是怎么去应用。
在讲动态代理之前,要问一个问题为什么要用动态代理?
考虑这样一个场景,在项目当中,我们通常有这样的需求,就是只有登陆的会员才具有下载,发表文章等权限,下载,发表文章等都对应着某些方法(如download()),在调用这些方法之前,我们都要先判断当前用户的是否有权去调用。最普通的做法是在download()中添加权限判断的代码:
download(){
权限判断.....
下载....
}
如果这样做,问题就来了,如果只有少数的几个操作需要权限判断那还行,只要挨个添加权限判断的代码就行了。但是一个项目中需要权限判断的地方海量去了,你一个个的去添加那还不累死?这个时候就要想办法减轻开发量,解决方法用两种,一种是通过过滤器(Filter),另外一种就是使用代理。
这里讲的是代理,代理又分为静态代理和动态代理,先说说静态代理:
我们提供一个代理类,该代理类拥有与目标类所要被调用的方法 相同的方法(方法签名),该代理类所生成的代理对象并含有目标对象的引用,调用代理对象的download方法时,会先判断权限,然后在调用目标对象的方法:
目标类:class TragetClass{
download(){
下载......
}
}
代理类:class ProxyClass{
TragetClass target;
download(){
权限判断......
下载......
}
}
静态代理和前面讲的常规方法有相同的弊端,就是代码量太大,有一万个目标方法,就要有一万个代理方法,太坑爹了。
不过幸好,java中利用reflect反射实现了动态代理,我们只需要写很少的代码就可以完成静态代理中一万个代理方法所实现的功能。
java动态代理用到了一个类(Proxy)和一个接口(InvocationHandler):
类Proxy.该类用于创建目标类的代理对象。
Proxy类常用的方法有:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) 该方法是一个static的方法,用于返回目标对象的代理对象。
参数loader就是加载目标类的classloader。
参数interfaces就是目标类所实现的接口。jdk动态代理是通过实现目标类所实现的接口来产生代理类并生成代理对象的。
参数handler,就是接口InvocationHandler实现类的对象,我们要自己去实现该接口,并实现其方法:
Object invoke(Object proxy, Method method, Object[] args) 。InvocationHandler用官方话说就是:是代理实例的调用处理程序 实现的接口。用我的话来说就是,代理对象调用某个方法时,就会调用handler的invoke方法,该方法中拥有目标对象的引用,而且该方法中拥有权限判断等需要在目标方法被调用之前或之后进行的操作。在该方法中,会先进行权限判断,然后再真真正正的去调用目标对象对应的方法。
要实现动态代理,首要条件是目标类是面向接口的。比如说鸭子Duck类,它实现了两个接口Brid和Animal。
public interface Animal {
public void eat();
public void run();
}
public interface Bird {
public void fly();
}
public class Duck implements Animal, Bird {
@Override
public void eat() {
System.out.println("duck eat");
}
@Override
public void run() {
System.out.println("duck run");
}
@Override
public void fly() {
System.out.println("duck fly");
}
}
目标类已经实现了,下面是动态代理的实现部分:
首先是InvocationHandler的实现类:
public class MyInvocationHandler implements InvocationHandler{
//target就是目标对象
private Object target;
public void setObject(Object target) {
this.target = target;
}
//proxy就是代理对象
//method就是对应于在代理对象上调用的接口方法的 Method 实例,调用method.invoke()方法时才真真的去调用目标类的方法
//args是要往目标方法中传入的参数,一般来源于代理对象调用方法时传入的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//method.getName()可以返回要调用的目标方法的名称
System.out.println("---------------目标方法:"+method.getName()+"执行之前do some thing---------------------");
//调用目标类的目标方法,returnObj是目标方法返回的值
Object returnObj = method.invoke(target, args);
System.out.println("---------------目标方法:"+method.getName()+"执行之后do some thing---------------------");
return returnObj;
}
}
下面是生成代理对象的方法类:
public class GetProxyObj {
private Object obj;
private MyInvocationHandler handler;
public GetProxyObj(Object obj,MyInvocationHandler handler){
this.obj = obj;
handler.setObject(obj);
this.handler = handler;
}
//用于返回目标对象的代理类
public Class getProxyClass(){
//obj.getClass().getClassLoader() 返回目标对象的classloader
// obj.getClass().getInterfaces()返回目标类所实现的接口数组
Class clazz = Proxy.getProxyClass(obj.getClass().getClassLoader(), obj.getClass().getInterfaces());
return clazz;
}
//用于返回目标对象的代理对象
//obj.getClass().getClassLoader() 返回目标对象的classloader
// obj.getClass().getInterfaces()返回目标类所实现的接口数组
//handler就是前面介绍的InvocationHandler实现类的实例对象
public Object getProxyObject(){
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
测试类:
public class TestUtil {
@Test
public void testGetObject(){
Duck duck = new Duck();
MyInvocationHandler handler = new MyInvocationHandler();
GetProxyObj obj = new GetProxyObj(duck,handler);
System.out.println("代理类:"+obj.getProxyClass());
//jdk动态代理是通过实现目标类所实现的接口来生成的,
//所以Proxy.newProxyInstance生成的对象只能被转换为接口类型比如Animal或Bird,而不是目标类duck
Animal animal = (Animal)obj.getProxyObject();
animal.run();
Bird bird = (Bird)obj.getProxyObject();
bird.fly();
}
}
输出信息:
代理类:class $Proxy4
---------------目标方法:run执行之前do some thing---------------------
duck run
---------------目标方法:run执行之后do some thing---------------------
---------------目标方法:fly执行之前do some thing---------------------
duck fly
---------------目标方法:fly执行之后do some thing---------------------