1、什么是动态代理?
动态代理就是在运行时生成一个类,这个类会实现你指定的一组接口,而这个类没有.java文件,是在运行时生成的,你也不用去关心它是什么类型的,你只需要知道它实现了哪些接口即可。
平常实现一个接口需要写一个具体的实现类,而动态代理技术能够在运行期间动态的生成实现指定接口的实现类对象,底层使用的反射原理。在框架中经常使用,例如:Struts1、Struts2、Spring和Hibernate等等。学习动态代理可以更好的理解框架内部的原理。
2、主要技术
创建动态代理的方法:
UserService proxyService = (UserService) Proxy.newProxyInstance( TestApp.class.getClassLoader(), new Class[]{UserService.class}, new MyInvocationHandler(userService));
上面代码中,Proxy类的静态方法newProxyInstance()方法生成了一个对象,这个对象实现了数组中指定的接口。Proxy类的静态方法newProxyInstance()方法返回的是实现了指定接口的实现类对象。
newProxyInstance()方法的参数:
ClassLoader loader:它是类加载器类型,MyInterface.class.getClassLoader()就可以获取到ClassLoader对象,只要有一个Class对象就可以获取到ClassLoader对象;
Class[] interfaces:指定newProxyInstance()方法返回的对象要实现哪些接口,可以指定多个接口,例如上面例子我们只指定了一个接口;
InvocationHandler h:它是最重要的一个参数,是一个接口!叫做调用处理器,无论你调用代理对象的什么方法,都是在调用InvocationHandler的invoke()方法!
InvocationHandler的invoke()方法:
InvocationHandler的invoke()方法的参数有三个:
Object proxy:代理对象,也就是Proxy.newProxyInstance()方法返回的对象,通常不用;
Method method:表示当前被调用方法的反射对象;
Object[] args:表示当前被调用方法的参数,没有参数的args是一个零长数组。
invoke()方法的返回值为Object类型,它表示当前被调用的方法的返回值,没有返回值的invoke()返回的就必须是null。
3、动态代理的用途
动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,动态代理的用途与装饰模式很相似,就是为了对某个对象进行增强。所有使用装饰者模式的案例都可以使用动态代理来替换。
在java类加载器中也应用了代理模式,例如我们自己写的Person类,一定是存放到CLASSPATH中,那么一定是由系统类加载器来加载。当系统类加载器来加载类时,它首先把加载的任务交给扩展类加载去,如果扩展类加载器加载成功了,那么系统类加载器就不会再去加载。这就是代理模式了!
代理模式保证了JDK中的类一定是由引导类加载器加载的!这就不会出现多个版本的类,这也是代理模式的好处。
4、动态代理的两种表现形式
给出InvocationHandler接口具体实现类:
接口:
package proxy; /** * 这是一个接口,用于在动态代理中被实现 * @author 胡根得 * */ public interface UserService { public void addUser(); public void updateUser(); }
接口实现类(待增强对象所属类):
package proxy; public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("user add"); } @Override public void updateUser() { System.out.println("user update"); } }
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * InvocationHandler:调用处理器 * 调用处理器中的invoke()方法是对代理对象所有方法的唯一实现 * 我认为:动态代理的代理就是 * @author 胡根得 * */ public class MyInvocationHandler implements InvocationHandler { //因为在这里边需要使用反射调用method对象的invoke方法,需要使用method所属对象 private UserService userService; //通过构造函数对userService进行赋值 public MyInvocationHandler(UserService userService) { this.userService = userService; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //无论我们调用代理对象中的哪个方法,其实都是在调用invoke()方法 System.out.println("我是动态代理哦!"); String name = method.getName(); if ("addUser".equals(name)) { System.out.println("我的名字是:"+name); }else if ("updateUser".equals(name)) { System.out.println("我的名字是:"+name); } return method.invoke(userService,args); } }
package proxy; /* * 本类是使用普通动态代理的类(我手动写出了InvokeHandler的实现类) * 动态代理内容:接口、被增强的类。不需要实际写出增强类,因为这个增强类是动态创建的 */ import java.lang.reflect.Proxy; import org.junit.Test; /* * 本实例为动态代理第一种形式,使用了InvocationHander的具体实现类 */ public class TestApp { @Test public void test(){ //得到被实现的接口实现类对象 UserService userService = new UserServiceImpl(); //创建动态代理对象 //三个参数: //1、类加载器将加载进入内存中 //2、创建出的动态代理对象需要实现哪几个接口 //3、调用处理器 UserService proxyService = (UserService) Proxy.newProxyInstance( TestApp.class.getClassLoader(), new Class[]{UserService.class}, new MyInvocationHandler(userService)); //这个时候已经有了动态代理对象,直接调用即可 proxyService.addUser(); proxyService.updateUser(); } }
使用内部类形式:
package proxy; /* * 在本示例中,我使用匿名内部类来创建InvokeHandler,而不是写一个它的实现类出来 */ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.junit.Test; /* * 本实例为动态代理第二种形式:未使用InvocationHandler的具体实现类 * 这两种方式都是动态代理,都省略了创建具体的增强类,也正是因此才用到了反射。(动态代理区别于静态代理) */ public class TestApp2 { @Test public void test(){ //创建待实现的接口的引用 final UserService userService = new UserServiceImpl(); //创建动态代理对象 UserService proxyUserService = (UserService) Proxy.newProxyInstance(TestApp2.class.getClassLoader(), new Class[]{UserService.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //调用方法 System.out.println("我是棒棒哒的动态代理哦哦哦!"); return method.invoke(userService, args); } }); //使用方法 proxyUserService.addUser(); proxyUserService.updateUser(); } }因为这个没有给出InvocationHandler接口实现类,所以需要使用匿名内部类的形式。
5、动态代理各个角色分析
抽象角色:声明真实对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
代理角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象,同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:定义了代理对象所代表的目标对象,代理角色所代表的真实对象,是我们最终要引用的对象,定义了代理对象所代表的目标对象。
6、动态代理的优点
减少编程的工作量:假如需要实现多种代理处理逻辑,只要写多个代理处理器就可以了,无需每种方式都写一个代理类。
系统扩展性和维护性增强,程序修改起来也方便多了(一般只要改代理处理器类就行了)。
做方法的增强,可以在不修改源码的情况下,增强一些方法,实现无侵入式的代码扩展。