在学习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方法,那么就成功对被代理类的方法进行了织入。所以上面的问题也就解决了。从而动态代理的接口方法也就实现了。这也就是动态代理的过程啦。
如有不对的地方大家一起交流。