1. Aop是一种思想
我们知道代理分为静态代理和动态代理,在讲解AOP这代理之前,我们先从生活中的例子出发,来讲解一下代理的由来。
看过《大话设计模式》的童鞋们应该都知道代理模式,书中以一个很生活化的例子进行讲解的,一个男生喜欢一个女生,出于羞涩不敢表达,想送人家花表达爱意,结果还是说不出口,无奈想了个办法,找个人替自己转达爱意,找一个传达自己的话,并且送上自己的礼物。那么中间的这个人就是代理。
说到这里,我们必须把代理和代替分开,代理不是代替,代理转达的是目标对象的意图而不是自己的意图,最终效果是让目标对象的愿望得以实现;而代替是用别人的礼物来完成的是自己的意愿,从而摒弃了目标对象的愿望,实现了自己愿望,这时候的目标对象只能用自己的礼物为他人做嫁衣裳,而这个目的不是我们想要的。
在最初的时候,是因为有这样一个羞涩的男生才出来一个这样的代理,这样明确知道目标对象的我们称之为静态代理;后来代理发现,我可以替一个人干这个活,是不是也可以帮更多的人干这个活呢?只是最初的时候我们不知道我们究竟要为谁代理,故而这时候只能是谁来就帮谁,这也是今天乡村中的媒婆行业以及信息化时代交友网站比如百合网等等产生的原因。
谁来就帮谁,这是一个只有在有人来的时候才知道,也就是当具体人来找的时候才能具体知道帮谁的机制就是我们的动态代理。
不知道这样的例子是不是恰当,希望帮助大家有个形象的理解,下面来在程序中进行实践吧。
2. 实战
静态代理
建立接口类UserManager:
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>public interface UserManager {
public void addUser(String username, String password);
public void delUser(int userId);
}
</strong></span>
建立实现类UserManagerImpl
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>public class UserManagerImpl implements UserManager {
public void addUser(String username, String password) {
//checkSecurity();
System.out.println("---------UserManagerImpl.add()--------");
}
public void delUser(int userId) {
//checkSecurity();
System.out.println("---------UserManagerImpl.delUser()--------");
}
}
</strong></span>
建立代理类UserManagerImplProxy
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>public class UserManagerImplProxy implements UserManager {
private UserManager userManager;
public UserManagerImplProxy(UserManager userManager) {
this.userManager = userManager;
}
public void addUser(String username, String password) {
checkSecurity();
userManager.addUser(username, password);
}
public void delUser(int userId) {
checkSecurity();
userManager.delUser(userId);
}
private void checkSecurity() {
System.out.println("-------checkSecurity-------");
}
}
</strong></span>
注意:代理不改变之前的接口,但是可以控制之前的目标,可以控制但是不能改变或者比如说分销商可以卖东西,但是不可以制造东西,在代理类同样实现和实现类相应的接口,并且将检查性的方法写在此处,这是我们添加一些与业务逻辑无关的方法检查。代理的好处是不用更改目标对象实现。
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>public UserManagerImplProxy(UserManager userManager) {
this.userManager = userManager;
}
</strong></span>
构造方法是访问接口对象的入口,如果通过了相关的验证,此时调用真正的实现类。
public voidaddUser(String username, String password) {
checkSecurity();
userManager.addUser(username,password);
}
至此一个代理完成了,虽然完成了,但是我们想一想,假如我们有好多个类或者好多个方法,都需要执行安全性检查,那么我们需要在每个方法中添加安全性检查方法,如果再加一个事务或者日志的处理呢,我们又得再次进行相应的添加,使得我们的程序特别的臃肿,而且重复性特别的大,但是检查安全性和添加的功能是独立的服务。不应该紧密耦合,
不改变还好说,一旦需要修改又是一个大的工程量,而这些日志或者说事务再者说检查安全性属于横切面的,是横向的关注点,并且一般是独立工作的。那么怎么办?动态代理应时而生,巧妙的解决了这个方法。
动态代理
新建的接口类和真正的实现类同上面是一样的,这里不再书写了,此时我们建立不再是上面的静态代理类,而是添加一个实现一个处理器为handler,可以为多个服务进行服务,可以将遍布于各个角落的东西拿出来,当运行的时候放进去即可,运行完释放的一个单独的类。这个类需要实现一个接口为InvocationHandler接口。
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SecurityHandler implements InvocationHandler {
private Object targetObject;
public Object createProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
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-------");
}
}
</strong></span>
解析这段代码:
代理的创建
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>private Object targetObject;
public Object createProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
</strong></span>
private ObjecttargetObject;定义一个目标对象,createProxyInstance方法创建目标代理对象,Object比较通用,方法的参数是目标文件,这时对目标对象进行赋值。
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);</strong></span>
targetObject.getClass().getClassLoader(),类加载器将类(代理类进行引入)通过目标类的字节码。也是通过类加载器加载目标对象;
getInterfaces(),可以拿到目标类的所有的接口,通过目标类的获得字节码对象,并且调用其获得接口的方法,获得所有的接口。
This:将代理交给谁去处理也就是第三个参数h(此处是this)也是类本身。
做的事情在哪里进行实现呢?没错在InvocationHandler中进行实现,因为InvocationHandler接口中存在一个Invoke方法,代理如何调用Invoke方法呢?我们的代理类中第三个参数是当前的类实现了Invoke方法,只要拿到当前对象即可调用。
Invoke方法的执行
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
checkSecurity();
// 调用目标方法
Object ret = method.invoke(targetObject, args);
return ret;
}
</strong></span>
在此处将我们要用的方法进行传入,比如执行检查性的方法checkSecurity();Object ret = method.invoke(targetObject, args);这个方法是真正的方法,比如增删改,后面的是目标对象,而最后的是参数,无论有还是没有都是参数。并且客户调用此方法必须有返回值,如果存在返回值是object否则为null。
客户端的测试
<span style="font-family:Microsoft YaHei;font-size:14px;"><strong>public class Client {
public static void main(String[] args) {
SecurityHandler hander = new SecurityHandler();
UserManager useraManager = (UserManager)hander.createProxyInstance(new UserManagerImpl());
useraManager.addUser("张三", "123");
}
}</strong></span>
这时的是指向一个代理类,他拥有和目标类的所有的方法,即使你切换代理和目标类,用户是不知道的,这是面向接口编程的好处。
执行的过程我们发现先调的是代理,之后才是目标。
好处是动态的代理的代码量会减少把遍布与系统中独立的服务拿出来,当我们运行的时候进行调用,就是springaop的实现。3. 小结
这时静态和动态代理完成,是不是觉得现在明朗多了呢?当然代理的知识点还有好多,在接下来的学习中会逐渐的深刻理解这块知识,期待下篇吧~