首页 > 代码库 > Spring源码阅读:使用标准AOP的API模拟Spring AOP + AspectJ的设计与实现
Spring源码阅读:使用标准AOP的API模拟Spring AOP + AspectJ的设计与实现
在上一篇博客中,提到了标准AOP与Spring AOP。这一篇就来把他们模拟出来。
在模拟之前,还需要提及的是,在Spring框架中,对于AOP的支持:
Spring 支持的AOP
AspectJ是另外一个有名的AOP框架,Spring也集成AspectJ,同时Spring AOP与AspectJ有一定程度的集成,这样一来Spring中就支持两种AOP:1)Spring AOP、2)AspectJ。而使用AOP的方式却又三种:
1)完全使用Spring AOP
2)完全使用AspectJ(分为注解方式、XML配置方式,这与Spring IOC有关)
3)使用SpringAOP与AspectJ集成(其中AspectJ的配置分注解和XML两种)
这些内容在Spring官方提供的参考文档中写的清清楚楚的。
采用AOP标准接口来模拟SpringAOP与AspectJ注解集成
注解也是自定义的,不是使用AspectJ的。
设计:
先看看标准AOP的API:
这是AOP的标准API,由AOP联盟制定,定义在aopalliance.jar中。
左侧:Advice代表建议,Interceptor就是一种Advice。Interceptor可以分为多种:构造拦截器,方法拦截器,字段拦截器。
右侧:JoinPoint代表连接点,其实就是执行的方法。Invocation就代表对target方法的调用,它把这个调用抽象成接口Invocation。Invocation与Interceptor呈对应关系。
既然AOP联盟定义了接口,我们就需要对其进行实现处理:
Advice的加入:可以在Interceptor的Invoke方法中加入,也可以在Invocation的proceed方法中加入。
处理方式可以参照J2EE的Filter和Struts2的ActionInvocation,因为它们都是采用这种机制。
@Before, @After 是两个自定义注解,模拟AspectJ注解。
接下来是使用JDK动态代理产生代理的设计:
代码实现:
1)AbstractMethodInterceptor,在AOP标准接口MethodInterceptor上扩展了两个方法。并实现了invoke方法。
package com.fjn.aop.core;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;public abstract class AbstractMethodInterceptor implements MethodInterceptor{ public final Object invoke(MethodInvocation invocation) throws Throwable { Object result=invocation.proceed(); return result; } public abstract void beforeAdvice(); public abstract void afterAdvice(); }
2)DefaultMethodInvocation,是对AOP标准接口MethodInvocation的实现。
package com.fjn.aop.core;import java.lang.reflect.AccessibleObject;import java.lang.reflect.Method;import java.util.List;import org.aopalliance.intercept.MethodInvocation;import com.fjn.aop.annotation.After;import com.fjn.aop.annotation.Before;import com.fjn.aop.bean.DefaultProxy;/** * 方法调用的具体实现 * @author fjn * */public class DefaultMethodInvocation implements MethodInvocation{ List<AbstractMethodInterceptor> interceptors; private DefaultProxy proxy; private Method method; private Object target; private Object[] args; int index=0; private boolean executed=false; public DefaultMethodInvocation(DefaultProxy proxy, Method method, Object target, Object[] args, List<AbstractMethodInterceptor> interceptorChain){ this.interceptors=interceptorChain; this.method=method; this.target=target; this.args=args; this.proxy=proxy; } public Object proceed() throws Throwable { AbstractMethodInterceptor interceptor=null; Object result=null; if(interceptors.size()>0 && index<interceptors.size()){ interceptor=interceptors.get(index++); if(new AdviceMatcher(interceptor, this).match(Before.class,"beforeAdvice")){ interceptor.beforeAdvice(); // 执行前置建议 } proceed(); // 执行下一个拦截器 } // 执行真正的方法调用 if(!executed){ executed=true; try { result=method.invoke(target, args); } catch (RuntimeException e) { System.out.println(e.getMessage()); } } if(index>0){ interceptor=interceptors.get(--index); if(new AdviceMatcher(interceptor, this).match(After.class,"afterAdvice")){ interceptor.afterAdvice(); // 执行后置建议 } } return result; } public Object getThis() { return target; } public AccessibleObject getStaticPart() { return null; } public Method getMethod() { return method; } public DefaultProxy getProxy(){ return proxy; } public Object[] getArguments() { return args; }}
3)AdviceMatcher,在拦截器处理时,要判断要执行的方法是否可以被拦截器拦截。
package com.fjn.aop.core;import java.lang.reflect.Method;import org.aopalliance.intercept.MethodInvocation;import com.fjn.aop.annotation.After;import com.fjn.aop.annotation.Before;/** * method 与 PointCut 匹配器 * 支持的方法名表达式有: * 1)*xxx, 以*开头 * 2)xxx*, 以*结尾 * 3)* 所有 * 4)没有*,指定方法名 * * @author fjn */@SuppressWarnings("rawtypes")public class AdviceMatcher { private AbstractMethodInterceptor interceptor; private MethodInvocation invocation; AdviceMatcher(AbstractMethodInterceptor interceptor, MethodInvocation invocation){ this.interceptor=interceptor; this.invocation=invocation; } public boolean match(Class adviceAnnotationType, String joinPoint){ // 要执行的方法 String methodName=invocation.getMethod().getName(); try { Method adviceMethod=interceptor.getClass().getDeclaredMethod(joinPoint, new Class[]{}); if(adviceAnnotationType==Before.class){ Before before=adviceMethod.getAnnotation(Before.class); if(before==null) return false; String pointcut=before.expression(); return beforeOrAfterMatch(pointcut, methodName); }else if(adviceAnnotationType==After.class){ After after=adviceMethod.getAnnotation(After.class); if(after==null) return false; String pointcut=after.expression(); return beforeOrAfterMatch(pointcut, methodName); } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); return false; } return true; } /** * 支持的方法名表达式有: * 1)*xxx, 以*开头 * 2)xxx*, 以*结尾 * 3)* 所有 * 4)没有*,指定方法名 * * @param pointcut * @param methodName * @return */ private boolean beforeOrAfterMatch(String pointcut, String methodName){ int indexOfStar=pointcut.indexOf("*"); if(indexOfStar!=-1){// 方法名中有*号 if(indexOfStar==0){// 以*开头 if("*".equals(pointcut)){// 只有* return true; } else{ return methodName.endsWith(pointcut.substring(1)); } }else {// 以*结尾,中间有*也算以*结尾 return methodName.startsWith(pointcut.substring(0, indexOfStar)); } }else{ return methodName.equals(pointcut); } }}
4)两个模拟AspectJ的注解:@Before,@After
package com.fjn.aop.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(value={ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface After{ String expression() default "";}package com.fjn.aop.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(value={ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface Before { String expression() default "";}
这两个注解,其实是借鉴了AspectJ的注解,但是又不想完全使用,因为与AspectJ的注解有:
@Aspect:声明切面
@PointCut:声明切点,设置表达式
@Before、@After、@Around等,这些是Advice,是要引用@PointCut中的表达式的,所以为了简便,只是自定定义了两个注解,并且拥有的@PointCut定义匹配表单式的作用。
================================================================
这个分割线以上的是AOP,下面代码是通过代理相关的设计:
5)默认的代理DefaultProxy设计:
package com.fjn.aop.bean;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.List;import com.fjn.aop.core.AbstractMethodInterceptor;import com.fjn.aop.core.DefaultMethodInvocation;public class DefaultProxy implements InvocationHandler{ Object target; List<AbstractMethodInterceptor> interceptorChain=new ArrayList<AbstractMethodInterceptor>(); public DefaultProxy(Object target, List<AbstractMethodInterceptor> interceptorChain){ this.target=target; if(interceptorChain!=null && interceptorChain.size()>0){ this.interceptorChain.addAll(interceptorChain); } } public final Object invoke(Object proxy, Method method, Object[] args) throws Throwable { DefaultMethodInvocation methodInvocation=new DefaultMethodInvocation(this, method, target, args, interceptorChain); return methodInvocation.proceed(); } public Object getProxy(){ Object proxy=null; try { proxy=Proxy.newProxyInstance(DefaultProxy.class.getClassLoader(), target.getClass().getInterfaces(), this); } catch (Exception e) { e.printStackTrace(); } return proxy; } public Object getTarget(){ return target; } }
在invoke()内部会与AOP衔接起来。
6)模拟ProxyBeanFactory:
package com.fjn.aop.bean;import java.util.List;import com.fjn.aop.core.AbstractMethodInterceptor;import com.fjn.aop.exception.NotFoundInterfaceException;@SuppressWarnings("rawtypes")/** * 单例的ProxyBeanFactory * @author Administrator * */public class ProxyBeanFactory { private ProxyBeanFactory(){} private static ProxyBeanFactory beanFactory=null; public synchronized static ProxyBeanFactory getBeanFactory(){ if (beanFactory==null) { beanFactory=new ProxyBeanFactory(); } return beanFactory; } /** * * @param targetClazz 创建指定类target实例 * @param chain 系统的拦截器链 * @return */ public Object createProxy(Class targetClazz,List<AbstractMethodInterceptor> chain){ Object proxyBean=null; if(targetClazz.getInterfaces().length==0){ throw new NotFoundInterfaceException(targetClazz); } try { Object target=targetClazz.newInstance(); proxyBean=new DefaultProxy(target, chain).getProxy(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return proxyBean; } public Object createProxy(Object target,List<AbstractMethodInterceptor> chain){ Object proxyBean=null; if(target.getClass().getInterfaces().length==0){ throw new NotFoundInterfaceException(target.getClass()); } proxyBean=new DefaultProxy(target, chain).getProxy(); return proxyBean; }}
可以根据Target.class,也可以根据target对象,来创建代理动态代理对象,返回值就是动态代理对象。
参数中的List< AbstractMethodInterceptor> 就相当于系统中配置好的那些拦截器。
==============================================================
测 试
有了这些以后,基本上就成型了,现在可以做测试了:
Target设计:
因为使用的Java动态代理,所以必须得为Target设计接口。
package com.fjn.aop;public interface TargetInterface { void helloAop(String str); void noUseAop(String str);}package com.fjn.aop;import com.fjn.aop.TargetInterface;/** * Target可以是Service层,也可以是DAO层 * @author fjn */public class Target implements TargetInterface{ public void helloAop(String str) { System.out.println("hello, "+str+ ", welcome you use aop in your application"); } public void noUseAop(String str){ System.out.println("hello, "+str+ ", the method has‘t use AOP ."); }}
接下来设计两个拦截器,要作为系统拦截器配置:
LoggingInterceptor:
package com.fjn.aop.interceptor;import com.fjn.aop.annotation.After;import com.fjn.aop.annotation.Before;import com.fjn.aop.core.AbstractMethodInterceptor;public class LoggingInterceptor extends AbstractMethodInterceptor{ @Before(expression="hello*") public void beforeAdvice(){ System.out.println("Logging: before advice invoked ..."); } @After(expression="*Aop") public void afterAdvice(){ System.out.println("Logging: after advice invoked ..."); } }
这个类中的两个建议说明:
beforeAdvice,只能匹配到方法名是以hello开头的。
afterAdvice,只能匹配到方法名以Aop结尾的。
AnotherInterceptor:
package com.fjn.aop.interceptor;import com.fjn.aop.annotation.After;import com.fjn.aop.annotation.Before;import com.fjn.aop.core.AbstractMethodInterceptor;public class AnotherInterceptor extends AbstractMethodInterceptor{ @Override @After(expression="*") public void afterAdvice() { System.out.println("another: after"); } @Override @Before(expression="*") public void beforeAdvice() { System.out.println("another: before"); }}
*表示所有方法都匹配。所以系统中如果使用这个拦截器,所有的方法调用时,都要用到这 两个advice。
现在就可以写测试用例了:
package com.fjn.aop;import java.util.ArrayList;import java.util.List;import com.fjn.aop.bean.ProxyBeanFactory;import com.fjn.aop.core.AbstractMethodInterceptor;import com.fjn.aop.interceptor.AnotherInterceptor;import com.fjn.aop.interceptor.LoggingInterceptor;public class Client { public static void main(String[] args) { // 初始化配置的拦截器链。 // 这里【相当于】Spring的在XML文件中配置AOP或者在拦截器上指定@AspectJ List<AbstractMethodInterceptor> interceptorChain=new ArrayList<AbstractMethodInterceptor>(); interceptorChain.add(new LoggingInterceptor()); interceptorChain.add(new AnotherInterceptor()); // 这里相当于Spring的通过IOC创建的代理Bean TargetInterface target=(TargetInterface)ProxyBeanFactory.getBeanFactory().createProxy(Target.class, interceptorChain); // 我们在使用Spring时,只需要写下面的代码就可以了: target.helloAop("zhang san"); System.out.println("--------------------------"); target.noUseAop("zhang san"); System.out.println("--------------------------"); target.helloAop("zhang san"); }}
上面的程序执行结果:
Logging: before advice invoked ...another: beforehello, zhang san, welcome you use aop in your applicationanother: afterLogging: after advice invoked ...--------------------------another: beforehello, zhang san, the method has‘t use AOP .another: afterLogging: after advice invoked ...--------------------------Logging: before advice invoked ...another: beforehello, zhang san, welcome you use aop in your applicationanother: afterLogging: after advice invoked ...
程序执行的流程如下:
1-3 :获取target的代理对象,中间过程使用的是JDK的动态代理。
4-16:target.xxMethod执行其实就是在Proxy的invoke方法内部调用MethodInvocation.proceed()的过程。
而proceed执行就是一个拦截器连执行的过程。
以上,就是模拟的SpringAOP流程了。