今天说和小张哥一起讨论AOP,正好看到了相关的视频,今天就总结一下AOP是如何使用动态代理来实现的。
AOP对JAVA程序员来说并不陌生,他是spring的一个核心内容——面向切面编程,先把概念放在这里,因为这一篇博客不会展开讲述AOP是什么,而是讲一讲他的来源——动态代理。我们先来看一个例子:有一个接口:UserManager,还有就是接口的实现类UserManagerImpl
UserManager:
public interface UserManager {
public void addUser(String username, String password);
public void delUser(int userId);
public String findUserById(int userId);
public void modifyUser(int userId, String username, String password);
}
UserManagerImpl
public class UserManagerImpl implements UserManager {
public void addUser(String username, String password) {
System.out.println("---------UserManagerImpl.add()--------");
}
public void delUser(int userId) {
System.out.println("---------UserManagerImpl.delUser()--------");
}
public String findUserById(int userId) {
System.out.println("---------UserManagerImpl.findUserById()--------");
return "张三";
}
public void modifyUser(int userId, String username, String password) {
System.out.println("---------UserManagerImpl.modifyUser()--------");
}
}
如果现在我们要添加安全性检查,在每执行代码之前,都加入安全性检查,那么我们就要打开已经写好的接口实现类,进行修改:
//安全性检查
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
public void addUser(String username, String password) {
checkSecurity();
System.out.println("---------UserManagerImpl.add()--------");
。。。。。//别的方法不在赘述
}
这样做我们发现不但违反了开闭原则,而且相同的方法调用到处都是,代码十分冗余,一直在“重复自己”,代码变得一点也不干(dry:dont repeat yourself!!!),如何解决这两个问题呢??
我们想到了使用代理模式,还记得第一次看大话设计模式,代理模式就是一个男孩子帮“小菜”追女孩儿,他不好意思做的事情,让代理帮他去做,从而达到解耦的功效,但是代理不能修改实际的方法,只起到控制的作用。接下来我们来修改上边这段代码:创建UserManager的代理类:UserManagerImplProxy
public class UserManagerImplProxy implements UserManager {
private UserManagerImpl userManagerImpl;
public UserManagerImplProxy(UserManagerImpl userManagerImpl) {
this.userManagerImpl= userManagerImpl;
}
public void addUser(String username, String password) {
checkSecurity();
userManagerImpl.addUser(username, password);
}
public void delUser(int userId) {
checkSecurity();
userManagerImpl.delUser(userId);
}
public String findUserById(int userId) {
checkSecurity();
return userManagerImpl.findUserById(userId);
}
public void modifyUser(int userId, String username, String password) {
checkSecurity();
userManagerImpl.modifyUser(userId, username, password);
}
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
}
创建好了之后,我们发现如果想要让客户端调用添加用户的方法,就要通过调用代理类的addUser,如下:
public void addUser(String username, String password) {
checkSecurity();//实现安全性检查
userManagerImpl.addUser(username, password);
}
通过代理类,我们就不需要对写好的接口实现类进行修改了,但是这样做依然没有解决checkSecurity方法到处出现的问题,因为静态代理是一对一的关系,也就是有一个接口,就对应有一个代理类,解决这个问题就需要使用动态代理(上边是通过静态代理实现的),我们只需要创建一个代理类,它就可以在运行时给我们想要的方法,动态代理的实现应用到了反射
首先我们要创建一个SecurityHandler(可以把他理解为创建代理的工厂,你要什么样的代理,这个类就能在运行的时候给你什么样的代理):
首先这个类要实现 InvocationHandler, 然后我们定义一个产生代理实例的方法:createProxyInstance
//定义一个私有成员:目标对象
private Object targetObject;
//产生代理对象
public Object createProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this);
}
这个方法其实实现了java.lang.reflect.Proxy中的Proxy类的静态方法:newProxyInstance(具体实现过程看下边的解析),说白了就是你丢进来一个UserManager的接口,他就能返回一个实现了我UserManager接口所有方法的代理类
动态代理其实就是java.lang.reflect.Proxy类动态的根据您指定的所有接口生成一个class byte,该class会继承Proxy类,并实现所有你指定的接口(您在参数中传入的接口数组);然后再利用您指定的classloader将 class byte加载进系统,最后生成这样一个类的对象,并初始化该对象的一些值,如invocationHandler,以即所有的接口对应的Method成员。 初始化之后将对象返回给调用的客户端。这样客户端拿到的就是一个实现你所有的接口的Proxy对象。
接着我们来看看checkSecurity写在哪里,我们在写一个invoke方法,这个方法实现了InvocationHandler接口的invoke方法,这个类就是最终Proxy调用的固定的接口的方法。Proxy不管客户端的业务方法是如何实现的,当客户端调用Proxy时,他只会调用InvocationHandler的invoke接口,所以我们的安全性检查只能放到invoke方法中
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
checkSecurity();
//调用目标方法
Object ret = method.invoke(targetObject, args);
return ret;
}
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
客户端代码:
public class Client {
public static void main(String[] args) {
SecurityHandler hander = new SecurityHandler();
UserManager useraManager = (UserManager)hander.createProxyInstance(new UserManagerImpl());
useraManager.addUser("张三", "123");
}
}
结果输出:
至于对类Proxy的深入理解,我参考了:http://blog.csdn.net/rokii/article/details/4046098(深入理解Java Proxy机制)这篇博客,博主自己写了两个静态的方法,可以对Proxy一探究竟,有兴趣的朋友可以去了解一下
that’s all!