`

SpingAOP原理小实现

阅读更多

      简单描述Sping的AOP就是动态代理加拦截器链实现面向切面编程。当然AOP肯定没有这么简单,但是如果能自己实现AOP然后再去看源码的话是不是会更轻松点呢。

 

      众所周知,Sping中AOP之所以能起作用就是我们在目标类的基础上定义了很多通知,例如before(前置通知),after-returning(后置通知),after(最终通知),after-throwing(异常通知),around(后置通知)等,那么我们就可以先从这里入手定义自己的通知

public interface FilterChain {

	/**
	 * 目标方法执行之前执行
	 * 
	 * @timestamp Feb 17, 2016 2:41:59 PM
	 */
	public void before(Object target, Matcher[] matchers);

	/**
	 * 目标方法执行之后执行
	 * 
	 * @timestamp Feb 17, 2016 2:42:12 PM
	 */
	public void after(Object target, Matcher[] matchers);
}

 target是目标类,matchers是匹配规则,它是个Matcher数组。之后可以再定义它的子接口扩展一下通知的功能

public interface FilterChainSub extends FilterChain {

	/**
	 * 处理catch中的异常
	 * 
	 * @timestamp Feb 17, 2016 5:57:09 PM
	 * @param exception
	 * @param target
	 */
	public void operateException(Exception exception, Object target);

	/**
	 * 释放资源,相当于finally中的逻辑
	 * 
	 * @timestamp Feb 17, 2016 4:25:53 PM
	 * @param target
	 */
	public void destroy(Object target);
}

 

现在通知定义好了,下一步就要决定是什么机制调用了实现这些接口的实体。

 

ApplicationFilterChain.java 由于这个源码比较多就不贴了,把链接放这了。下面来看一下这个类的主要字段

	/**
	 * 实现过滤器链
	 */
	private List<FilterChain> filterChainList = new ArrayList<>();
	/**
	 * 索引计数器
	 */
	private int index = 0;
	/**
	 * 目标方法
	 */
	private Method method = null;
	/**
	 * 目标对象
	 */
	private Object target = null;
	/**
	 * 参数
	 */
	private Object[] args = null;

	/**
	 * 匹配规则
	 */
	private Matcher[] matchers;

 

这个其实就是一个过滤器链,在Spring中是拦截器链。拦截器与过滤器的最主要的区别就是拦截器是动态代理实现的,而过滤器是函数的回调实现的。所以总感觉这里Filter好一些。也许设计AOP的大牛们是从整体上来看的,他就是一个Interceptor。不管什么了能实现功能再说。filterChainList是之后要添加的通知集合。下面来看一下核心代码

public Object invoke(ApplicationFilterChain chain) {
		if (filterChainList.size() < 0)
			return null;
		if (chain.size() == chain.index()) {
			Object obj = null;
			try {
				obj = method.invoke(target, args);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			return obj;
		}
		// 获取过滤链中当前索引所指向的节点
		FilterChain f = chain.get(chain.index);
		Object obj = null;
		f.before(target, matchers);
		// 过滤链中当前索引+1
		this.index++;
		obj = invoke(this);
		f.after(target, matchers);
		return obj;
	}

 

外界要调用这个invoke方法就要传进来一个ApplicationFilterChain实例,如果传入的这个chain中通知集合元素的个数等于它的index的话就执行目标方法。否则就根据索引从链中获取通知然后执行前置通知,执行this.index++;重新调用invoke方法,再调用后置通知。这里为什么可以成this.index++不是chain吗?是因为我在外面调invoke的对象和传入的参数是一个对象,所以chain和this就是一个对象。使用哪个都可以,如果感觉别扭的话可以把this换成chain。再者就是invoke方法的调用会形成一个递归,知道index==size时就会跳出递归。现在知道了通知是怎么被调用的。下一步就可以讨论整体结构是怎么形成的了。

 

public class Interceptor implements InvocationHandler {
	/**
	 * 目标对象
	 */
	private Object target = null;
	/**
	 * 过滤器链
	 */
	private ApplicationFilterChain filterChain = null;

	private String[] regular;

	/**
	 * 
	 * @param target
	 *            目标类
	 * @param filterChain
	 *            过滤器链
	 * @param regular
	 *            匹配规则
	 */
	public Interceptor(Object target, ApplicationFilterChain filterChain, String... regular) {
		super();
		this.target = target;
		this.filterChain = filterChain;
		this.regular = regular;
	}

	/**
	 * 执行目标方法
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		try {
			// 设置目标方法,目标类,参数
			filterChain.setMethod(method);
			filterChain.setTarget(target);
			filterChain.setArgs(args);
			filterChain.setRegular(regular);
			return filterChain.invoke(filterChain);
		} catch (Exception e) {
			filterChain.operateException(e);
		} finally {
			filterChain.destroy(target);
		}
		return null;
	}

}

 

这里我定义了一个拦截器类然后让它实现了InvocationHandler接口,如果不知道InvocationHandler是什么作用的话可以先看一下动态代理,这里就不说了。这个拦截器类有一个构造函数接收三个参数分别是被代理的目标类,过滤器链,匹配规则。之后在代理方法中分别对ApplicationFilterChain的必要参数进行了设置然后调用filterChain.invoke(filterChain);现在知道了上面的那个invoke是在哪被调用了,再者就是这个代理方法中的try,catch块也是很有必要的,在invoke抛出异常时,会被catch接住,然后调用filterChain的operateException。看一下这个方法。

	public void operateException(Exception e) {
		for (FilterChain f : filterChainList) {
			if (f instanceof FilterChainSub) {
				((FilterChainSub) f).operateException(e, target);
			}
		}
	}

 它会在通知链中找到可以处理异常的通知然后处理掉他。这是一个很大的隐患,时间原因就不改了,有兴趣的朋友可以试着改改。例如可以使用FilterChain出错时的index作索引,取出通知实例然后调用它的处理异常的方法。destroy是同样的思路也有同样的毛病。下面是测试代码

public class TestAop {

	public static void main(String[] args) {
		ApplicationFilterChain chain = new ApplicationFilterChain();
		chain.addChain(new Transactional());
		ServiceImpl impl = new ServiceImpl();
		Interceptor inte = new Interceptor(impl, chain, "^save*");
		Service s = (Service) Proxy.newProxyInstance(//
				TestAop.class.getClassLoader(), //
				impl.getClass().getInterfaces(), //
				inte);
		s.save();
	}
}

 源码地址:SpingAOP小实现

有兴趣的话也可以看一下这个小demo的具体应用场景:its智能交通系统。还有这个系统的反思。下一步SpingAOP源码走起!

 

1
4
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics