在学习spring的aop的时候,老师叫我们使用java的proxy和InvocationHandler来模拟spring的aop。
首先要了解什么是代理:所谓代理就是我想让小王去买包烟,但是我又不想直接通知小王,因为那样我感觉自己非常的掉价。所以我就叫小李去通知小王,让小王完成这件事。在这个过程中,我是一个主动方,小王是一个行为执行方,而小李就是一个代理。因为小李负责我和小王之间的关系,甚至小李也可以叫小王给自己再买一包烟(实际这就是动态代理的最大用处)。
动态代理模式有代理对象,被代理对象。而在代理模式中还需要注意的一点就是,我们生成的代理对象,一般是要和被代理对象使用相同的接口的,这样就可以面向接口编程,所有被代理对象有的方法,我们生成的代理对象也都有,并且方法的功能还增强了。
下面就用代码模拟一下:
package cn.wsy.dao; /** * * 项目名称 spring_aop * 作者 tim * 创建时间 2014-7-25 * 类描述 用户dao接口 */ public interface UserDao { /** * 保存 */ public void save(); /** * 删除 */ public void delete(); }
package cn.wsy.dao.impl; import cn.wsy.dao.UserDao; /** * * 项目名称 spring_aop * 作者 tim * 创建时间 2014-7-25 * 类描述 UserDao接口的实现类 */ public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("UserDaoImpl save start..."); } @Override public void delete() { // TODO Auto-generated method stub System.out.println("UserDaoImpl delete start..."); } }
package cn.wsy.interceptor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Date; /** * * 项目名称 spring_aop * 作者 tim * 创建时间 2014-7-25 * 类描述 将需要织入的方法织入到被代理对象调用方法的前后 */ public class LogInterceptor implements InvocationHandler{ /**被代理对象**/ private Object target; /**需要织入的方法**/ public void beforeMethod(Method method){ System.out.println(method.getName()+"start"+new Date()); } /** * proxy 代理实例,一般用不到 * method 代理实例的方法,通过它可以对目标代理类进行发射调用 * args 代理实例的方法的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub //织入方法 beforeMethod(method); //执行被代理对象的方法 method.invoke(target, args); return null; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } }
package cn.wsy.test; import java.lang.reflect.Proxy; import cn.wsy.dao.UserDao; import cn.wsy.dao.impl.UserDaoImpl; import cn.wsy.interceptor.LogInterceptor; /** * 项目名称 spring_aop * 作者 lenovo * 创建时间 2014-7-25 * 类描述 使用jdk 的proxy和 InvocationHandler模拟spring的aop */ public class AopTest { public static void main(String[] args) { UserDao userDao = new UserDaoImpl(); LogInterceptor log = new LogInterceptor(); log.setTarget(userDao); /** * 第一个参数是被代理对象的加载器,必须和被代理对象使用一个类加载器 * 第二个参数是代理对象实现的接口,该接口被代理对象也实现,保证返回的是面向接口的编程 * 第三个参数是派生出来的代理处理程序 */ UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance( UserDao.class.getClassLoader(), new Class[] { UserDao.class }, log); userDaoProxy.delete(); userDaoProxy.save(); } }
代码如上所示。下面是自己的一些见解,在main方法中我们使用Proxy的newProxyInstance()静态方法。在这个方法中有三个参数,上面已经说明,不在累赘。现在说明一下这个userDaoProxy对象 的方法调用是如何实现方法增强的。
首先我们的Proxy代理对象一个实现了和被代理对象相同的接口的,由此可知,我们的代理对象也拥有被代理对象的方法。那么问题在于代理对象的这些方法是怎么实现的呢。
我们肯定会想到它一定是调了InvocationHandler中的invoke方法。而在该方法中最主要的就是Method和args两个参数。如果得到这两个参数,那么代理对象调用InvocationHandler中的invoke方法就没有问题了。在Proxy的参数中有一个类启动器的参数,通过这个参数我们就可以得到调用的方法。例如:
userDaoProxy中也有一个save方法,那么
UserDao.class.getMethod就可以得到调用的方法名,那得到方法当然就可以得到参数了,从而就可以调用InvocationHandler中的invoke方法,那么就成功对被代理类的方法进行了织入。所以上面的问题也就解决了。从而动态代理的接口方法也就实现了。这也就是动态代理的过程啦。
如有不对的地方大家一起交流。