欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpingAOP原理小实现

程序员文章站 2022-03-03 09:14:23
...

      简单描述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源码走起!

 

相关标签: Sping AOP java