Spring中有两大核心的内容,IoC(控制反转)和AOP(面向切面编程),深入学习了Spring之后发现,这两者在Spring中的地位非常高,并且我认为想要学好Spring框架体系,必须从底层去理解和亲自测试IoC和AOP的原理。
IoC已经在笔者之前发布的文章中提及,今天要讲述的是AOP的实现原理。
AOP的概念
众所周知许多的编程语言都是面向对象的,如Java、C++,而如今 面向对象已经俨然成了我们编程的思想深入人心,但是现实中有一些内容并不是面向对象可以解决的,所以AOP所存在的意义也就展现了。
AOP可以拦截一些方法,然后把哥个对象组织成一个整体,如网站的交易记录需要记录日志,约定好了动态的流程之后那么就可以在交易前输出交易前的准备工作和打印日志,在完成交易的时候打印交易订单并且输入日志,同样,当这一笔交易发生异常时也能够回滚事物并且输出提示异常信息。
可能经过以上一番话你会想到拦截器,其实事实就是AOP的实现和拦截器有着密不可分的关系。
之前在学习这方面的知识的时候在书上看到了很不错的一个解释AOP运行流程的小游戏,它虽与真正的AOP有着一定的区别,但是大体上却能很好的体现AOP的实现流程。
游戏约定如下:
首先提供一个Interceptor接口:
public interface Interceptor {
//执行对应操作之前的方法
public void before(Object obj);
//执行完毕对应操作之后的方法
public void after(Object obj);
//正确执行无误,执行该方法
public void afterReturning(Object obj);
//执行方法异常,执行该方法
public void afterThrowing(Object obj);
}
顾名思义,这是一个拦截器接口,接下来是它的实现类:
public class UserInterceptor implements Interceptor{
@Override
public void before(Object obj) {
// TODO Auto-generated method stub
System.out.println("准备执行printMessage()方法-------");
}
@Override
public void after(Object obj) {
// TODO Auto-generated method stub
System.out.println("执行完毕printMessage()方法-------");
}
@Override
public void afterReturning(Object obj) {
// TODO Auto-generated method stub
System.out.println("无异常发生,正常返回-------------");
}
@Override
public void afterThrowing(Object obj) {
// TODO Auto-generated method stub
System.out.println("执行printMessage()方法出错请检查---------");
}
}
然后是POJO:
package test_01;
public class User {
private int id;
private String name;
private String note;
//构造方法
public User(int id,String name,String note) {
this.id = id;
this.name = name;
this.note = note;
}
//set和get方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
同样我们还需要Service接口和它的实现类:
public interface UserService {
public void printMessage(User user);
}
package test_01;
public class UserServiceImpl implements UserService{
@Override
public void printMessage(User user) {
// TODO Auto-generated method stub
System.out.println("id--------"+user.getId());
System.out.println("name------"+user.getName());
System.out.println("note------"+user.getNote());
}
}
接下来是具体类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyBeanUtil implements InvocationHandler{
//被代理对象
private Object obj;
//拦截器
private Interceptor interceptor = null;
public static Object getBean(Object obj,Interceptor interceptor) {
ProxyBeanUtil _this = new ProxyBeanUtil();
//保存被代理对象
_this.obj = obj;
//保存拦截器
_this.interceptor = interceptor;
//生成代理对象并绑定方法
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), _this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Object retObj = null;
//判断是否产生了异常
boolean exceptionFlag = false;
//执行先行方法
interceptor.before(obj);
try {
//反射所有方法
retObj = method.invoke(obj, args);
}catch(Exception ex) {
exceptionFlag = true;
}finally {
interceptor.after(obj);
}
if(exceptionFlag) {
interceptor.afterThrowing(obj);
}else {
interceptor.afterReturning(obj);
}
return retObj;
}
}
proxyBeanFactory:
public class ProxyBeanFactory {
public static <T> T getBean(T obj,Interceptor interceptor) {
return (T) ProxyBeanUtil.getBean(obj,interceptor);
}
}
当你尝试了解Spring AOP的时候我想你肯定了解过了动态代理模式,如果你已经知晓JDK动态代理,那么当你去看getBean方法时候肯定会发现什么有意思的事情,没错,其实AOP的实现和动态代理有着不可分割的联系。ProxyBeanFactory类中的getBean方法可以得到目标方法的代理对象,并通过反射来完成对象的传递。而上述“游戏”则是使用了JDK动态代理。
最后,我们需要编写一个测试类来让“游戏”开始,
package test_01;
public class Main {
public static void main(String[] agrs) {
UserService userService = new UserServiceImpl();
Interceptor interceptor = new UserInterceptor();
UserService proxy = ProxyBeanFactory.getBean(userService, interceptor);
User user = new User(1,"小华","销售部部长");
proxy.printMessage(user);
System.out.println("-------------------测试该方法---------------------");
user = null;
proxy.printMessage(user);
}
}
当测试类中的user不为空时,我们就能测试before,after,和afterReturning方法,既然是测试,我们就必须测试所有的方法,所以最后我们将user置空,测试afterThrowing方法测试结果如下:
|
通过控制台的输出信息,我们就能大致的了解AOP的运行流程,它并不好理解,我们可以通过打断点去一步一步验证它,不要怕麻烦,这是成为一个合格程序员的最低门槛。