tinySpring学习笔记(二)-实现AOP

  • tinySpring学习笔记系列之二
  • 主要介绍AOP实现过程
  • 并介绍不同的动态代理方式

AOP及其实现

AOP分为配置(Pointcut,Advice),织入(Weave)两部分工作,当然还有一部分是将AOP整合到整个容器的生命周期中。

step1-使用JDK动态代理实现AOP织入

git checkout step-7-method-interceptor-by-jdk-dynamic-proxy

  • 织入(weave)相对简单,我们先从它开始。Spring AOP的织入点是AopProxy,它包含一个方法Object getProxy()来获取代理后的对象。
public interface AopProxy {
    Object getProxy();
}

AOP 中两个重要角色:MethodInterceptor和MethodInvocation

  • 这两个角色都是AOP联盟的标准,它们分别对应AOP中两个基本角色:AdviceJoinpointAdvice定义了在切点指定的逻辑,而Joinpoint则代表切点。
public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation invocation) throws Throwable;
}
  • Spring的AOP只支持方法级别的调用,所以其实在AopProxy里,我们只需要将MethodInterceptor放入对象的方法调用即可。

  • 我们称被代理对象为TargetSource,而AdvisedSupport就是保存TargetSourceMethodInterceptor的元数据对象。这一步我们先实现一个基于JDK动态代理的JdkDynamicAopProxy,它可以对接口进行代理。于是我们就有了基本的织入功能。

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

    /**
     * AdvisedSupport
     * Fields: TargetSource targetSource
     *         [Object target,Class targetClass];//target 代理对象,targetClass 继承的接口
     *         
     *         MethodInterceptor methodInterceptor;//Advice切面逻辑
     * Methods:对应属性的getter、setter
     */
	private AdvisedSupport advised;

	public JdkDynamicAopProxy(AdvisedSupport advised) {
		this.advised = advised;
	}

    //为要代理的对象的的类对应的创建动态代理
    @Override
	public Object getProxy() {
	
	    /**
	     * public static Object newProxyInstance(ClassLoader loader,
         *                                       Class<?>[] interfaces,
         *                                       InvocationHandler h)
         * @param loader the class loader to define the proxy class
         * @param interfaces the list of interfaces for the proxy class to implement
	     * @param h the invocation handler to dispatch method invocations to
	     */
		return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { advised.getTargetSource()
				.getTargetClass() }, this);
	}

    /**
     * 重写 InvocationHandler 对应的 invoke() 方法
     * 调用拦截器对应的方法
     * (通过反射获取对应的切点,再根据切点指定的逻辑进行执行)
     * @Param Object proxy 代理
     * @Param Method method 对应的要调用方法
     * @Param Object[] args 方法需要的参数
     */
	@Override
	public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
		MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
		
		/**
		 * 其中 class MethodInterceptor extends Interceptor {
		 *           Object invoke(MethodInvocation invocation) throws Throwable;
		 *      }
		 *      class ReflectiveMethodInvocation implements MethodInvocation
		 * 故可通过继承 MethodInterceptor 重写相关 invoke() 方法实现 Advice 逻辑
		 */
		return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method,
				args));
	}
}
  • ReflectiveMethodInvocation.java
public class ReflectiveMethodInvocation implements MethodInvocation {

	private Object target;

	private Method method;

	private Object[] args;

	public ReflectiveMethodInvocation(Object target, Method method, Object[] args) {
		this.target = target;
		this.method = method;
		this.args = args;
	}

	@Override
	public Method getMethod() {
		return method;
	}

	@Override
	public Object[] getArguments() {
		return args;
	}

    // 反射,相当于 target.method(args);即调用代理对象对应的方法
	@Override
	public Object proceed() throws Throwable {
		return method.invoke(target, args);
	}

	@Override
	public Object getThis() {
		return target;
	}

	@Override
	public AccessibleObject getStaticPart() {
		return method;
	}
}

实现步骤:

  • 1、设置被代理对象,指定切点(JoinPoint)
// 创建AdvisedSupport对象,设置 Joinpoint
AdvisedSupport advisedSupport = new AdvisedSupport();
TargetSource targetSource = new TargetSource(helloWorldService, HelloWorldService.class);
advisedSupport.setTargetSource(targetSource);
  • 2、设置拦截器(Advice)
/**
 * // Advice 逻辑实现
 * public class TimerInterceptor implements MethodInterceptor {
 *	@Override
 *	public Object invoke(MethodInvocation invocation) throws Throwable {
 *		long time = System.nanoTime();
 *		System.out.println("Invocation of Method " + invocation.getMethod().getName() + " start!");
 *		Object proceed = invocation.proceed();
 *		System.out.println("Invocation of Method " + invocation.getMethod().getName() + " end! takes " + (System.nanoTime() - time) + " nanoseconds.");
 *		return proceed;
 *	 }
 * }
 */
TimerInterceptor timerInterceptor = new TimerInterceptor();
// 设置Advice
advisedSupport.setMethodInterceptor(timerInterceptor);
  • 3、创建代理(Proxy)
JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport);
HelloWorldService helloWorldServiceProxy = (HelloWorldService) jdkDynamicAopProxy.getProxy();
  • 4、基于AOP的调用
helloWorldServiceProxy.helloWorld();

step2-使用AspectJ管理切面

git checkout step-8-invite-pointcut-and-aspectj

  • 完成了织入之后,我们要考虑另外一个问题:对什么类以及什么方法进行AOP?对于“在哪切”这一问题的定义,我们又叫做“Pointcut”。Spring中关于Pointcut包含两个角色:ClassFilterMethodMatcher,分别是对类和方法做匹配。Pointcut有很多种定义方法,例如类名匹配、正则匹配等,但是应用比较广泛的应该是和AspectJ表达式的方式。
public interface Pointcut {

    ClassFilter getClassFilter();

    MethodMatcher getMethodMatcher();

}


public interface ClassFilter {

    boolean matches(Class targetClass);
}

public interface MethodMatcher {

    boolean matches(Method method, Class targetClass);
}
  • AspectJ是一个“对JavaAOP增强”。它最早是其实是一门语言,我们跟写Java代码一样写它,然后静态编译之后,就有了AOP的功能。下面是一段AspectJ代码:
aspect PointObserving {
    private Vector Point.observers = new Vector();
    public static void addObserver(Point p, Screen s) {
        p.observers.add(s);
    }
    public static void removeObserver(Point p, Screen s) {
        p.observers.remove(s);
    }
    ...
}
  • 这种方式无疑太重了,为了AOP,还要适应一种语言?所以现在使用也不多,但是它的Pointcut表达式被Spring借鉴了过来。于是我们实现了一个AspectJExpressionPointcut:
public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher {

    // 对用户自定义的相关关键字子集合,可进行切点表达式PointcutExpression的构造
	private PointcutParser pointcutParser;

    // 表达式字符串
	private String expression;

    // 要构造的切点表达式
	private PointcutExpression pointcutExpression;

    // 存储表达式相关关键字
	private static final Set<PointcutPrimitive> DEFAULT_SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>();

	static {
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
	}
    
    // 进行关键字集合初始化
	public AspectJExpressionPointcut() {
		this(DEFAULT_SUPPORTED_PRIMITIVES);
	}

    // 初始化表达式构造器
	public AspectJExpressionPointcut(Set<PointcutPrimitive> supportedPrimitives) {
		pointcutParser = PointcutParser
				.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives);
	}

	protected void checkReadyToMatch() {
		if (pointcutExpression == null) {
			pointcutExpression = buildPointcutExpression();
		}
	}

    // 字符串转换为切点表达式
	private PointcutExpression buildPointcutExpression() {
		return pointcutParser.parsePointcutExpression(expression);
	}

	public void setExpression(String expression) {
		this.expression = expression;
	}

    // 对类做匹配
	@Override
	public ClassFilter getClassFilter() {
		return this;
	}

    // 对方法做匹配
	@Override
	public MethodMatcher getMethodMatcher() {
		return this;
	}

    // 将表达式和类做匹配,返回匹配结果
	@Override
	public boolean matches(Class targetClass) {
		checkReadyToMatch();
		return pointcutExpression.couldMatchJoinPointsInType(targetClass);
	}

    // 将表达式和方法做匹配,返回匹配结果
	@Override
	public boolean matches(Method method, Class targetClass) {
		checkReadyToMatch();
		ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
		if (shadowMatch.alwaysMatches()) {
			return true;
		} else if (shadowMatch.neverMatches()) {
			return false;
		}
		// TODO:其他情况不判断了!见org.springframework.aop.aspectj.RuntimeTestWalker
		return false;
	}
}

实现测试:

// 对类做匹配 返回匹配结果
@Test
public void testClassFilter() throws Exception {
    String expression = "execution(* us.codecraft.tinyioc.*.*(..))";
    AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
    aspectJExpressionPointcut.setExpression(expression);
    boolean matches = aspectJExpressionPointcut.getClassFilter().matches(HelloWorldService.class);
    Assert.assertTrue(matches);
}

// 对方法做匹配 返回匹配结果
@Test
public void testMethodInterceptor() throws Exception {
    String expression = "execution(* us.codecraft.tinyioc.*.*(..))";
    AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
    aspectJExpressionPointcut.setExpression(expression);
    boolean matches = aspectJExpressionPointcut.getMethodMatcher().matches(HelloWorldServiceImpl.class.getDeclaredMethod("helloWorld"),HelloWorldServiceImpl.class);
    Assert.assertTrue(matches);
}

step3-将AOP融入Bean的创建过程中

git checkout step-9-auto-create-aop-proxy

  • 在step1 中已经能够进行 weave 织入,step2 中实现了 Pointcut 的匹配。现在需要在 Spring 中整合这两者。Spring给了一个巧妙的答案:使用 BeanPostProcessor
  • BeanPostProcessorBeanFactory提供的,在Bean初始化过程中进行扩展的接口。只要你的Bean实现了BeanPostProcessor接口,那么Spring在初始化时,会优先找到它们,并且在Bean的初始化过程中,调用这个接口,从而实现对BeanFactory核心无侵入的扩展。
  • AOP的xml配置
<aop:aspectj-autoproxy/>

<!-- 等价 -->
<bean id="autoProxyCreator" class="org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator"></bean>
  • AspectJAwareAdvisorAutoProxyCreator就是AspectJ方式实现织入的核心。它其实是一个BeanPostProcessor。在这里它会扫描所有Pointcut,并对bean做织入。
  • BeanPostProcessor:
public interface BeanPostProcessor {

	Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception;

	Object postProcessAfterInitialization(Object bean, String beanName) throws Exception;

}
  • AspectJAwareAdvisorAutoProxyCreator:
public class AspectJAwareAdvisorAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware {

	private AbstractBeanFactory beanFactory;

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
		if (bean instanceof AspectJExpressionPointcutAdvisor) {
			return bean;
		}
        if (bean instanceof MethodInterceptor) {
            return bean;
        }
        
        /**
         * class AspectJExpressionPointcutAdvisor implements PointcutAdvisor
         * Fields: AspectJExpressionPointcut pointcut
         *         Advice advice
         * Methods: Advice getter/setter
         *          Pointcut getter
         *          void setExpression(String expression) {this.pointcut.setExpression(expression);}
         * 
         */
        // 根据 Type 获取所有的 PointCut 和 Advice 组成的 bean
		List<AspectJExpressionPointcutAdvisor> advisors = beanFactory
				.getBeansForType(AspectJExpressionPointcutAdvisor.class);
		for (AspectJExpressionPointcutAdvisor advisor : advisors) {
		    
		    // 判断是否是要拦截的类
			if (advisor.getPointcut().getClassFilter().matches(bean.getClass())) {
				
				/**
				 * Class AdvisedSupport
				 * Fields: TargetSource targetSource;
				 *         MethodInterceptor methodInterceptor;
				 *         MethodMatcher methodMatcher;
				 * Methods: getter/setter
				 */
				AdvisedSupport advisedSupport = new AdvisedSupport();
				
				// 从扫描出的 bean 中获取 Advice 逻辑并注入
				// 设置 Advice
				advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
				
				/**
				 * MethodMatcher getMethodMatcher() {return this;}
				 * AspectJExpressionPointcut 实现了 MethodMatcher 接口。
				 */
				// 设置切点 Pointcut,即哪些方法需要做拦截
				advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());

                // 设置要代理的对象
				TargetSource targetSource = new TargetSource(bean, bean.getClass().getInterfaces());
				advisedSupport.setTargetSource(targetSource);

                // 创建动态代理
				return new JdkDynamicAopProxy(advisedSupport).getProxy();
			}
		}
		return bean;
	}

    // 获取容器的引用,进而获取容器中所有的切点对象
	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws Exception {
		this.beanFactory = (AbstractBeanFactory) beanFactory;
	}
}

  • 此时的JdkDynamicAopProxy
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

	private AdvisedSupport advised;

	public JdkDynamicAopProxy(AdvisedSupport advised) {
		this.advised = advised;
	}

	@Override
	public Object getProxy() {
		return Proxy.newProxyInstance(getClass().getClassLoader(), advised.getTargetSource().getTargetClass(), this);
	}

	@Override
	public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
		MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
	    
	    // invoke 时判断是否为要拦截的方法,是则执行 Advice 逻辑
		if (advised.getMethodMatcher() != null
				&& advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
			return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(),
					method, args));
		} else {
			return method.invoke(advised.getTargetSource().getTarget(), args);
		}
	}
}
  • 为了简化xml配置,在tiny-spring中直接使用Bean的方式,而不是用aop前缀进行配置:
<bean id="autoProxyCreator" class="us.codecraft.tinyioc.aop.AspectJAwareAdvisorAutoProxyCreator"></bean>

<bean id="timeInterceptor" class="us.codecraft.tinyioc.aop.TimerInterceptor"></bean>

<!-- Creator 将对 Advisor 类型的 bean 进行扫描和处理 -->
<bean id="aspectjAspect" class="us.codecraft.tinyioc.aop.AspectJExpressionPointcutAdvisor">
    <!-- 调用对应的 setter 方法进行 Property 的注入 -->
    <property name="advice" ref="timeInterceptor"></property>
    <property name="expression" value="execution(* us.codecraft.tinyioc.*.*(..))"></property>
</bean>
动态代理的步骤
  • AutoProxyCreator(实现了 BeanPostProcessor 接口)在实例化所有的 Bean 前,最先被实例化。postProcessBeforeInitialization
  • 其他普通 Bean 被实例化、初始化,在初始化的过程中,AutoProxyCreator 加载 BeanFactory 中所有的 PointcutAdvisor(这也保证了 PointcutAdvisor 的实例化顺序优于普通 Bean。),然后依次使用 PointcutAdvisor 内置的 ClassFilter,判断当前对象是不是要拦截的类。
  • 如果是,则生成一个 TargetSource(要拦截的对象和其类型),并取出 AutoProxyCreatorMethodMatcher(对哪些方法进行拦截)、Advice(拦截的具体操作),再交给 AopProxy 去生成代理对象。
  • AopProxy 生成一个 InvocationHandler,在它的 invoke 函数中,首先使用 MethodMatcher 判断是不是要拦截的方法,如果是则交给 Advice 来执行(Advice 由用户来编写,其中也要手动/自动调用原始对象的方法),如果不是,则直接交给 TargetSource 的原始对象来执行。

step4-使用CGLib进行类的织入

git checkout step-10-invite-cglib-and-aopproxy-factory

  • 前面的JDK动态代理只能对接口进行代理,对于类则无能为力。这里我们需要一些字节码操作技术。这方面大概有几种选择:ASMCGLibjavassist,后两者是对ASM的封装。Spring中使用了CGLib
  • 在这一步,我们还要定义一个工厂类ProxyFactory,用于根据TargetSource类型自动创建代理,这样就需要在调用者代码中去进行判断。
  • TargetSource:
public class TargetSource {

	private Class<?> targetClass;

    private Class<?>[] interfaces;

	private Object target;

	public TargetSource(Object target, Class<?> targetClass,Class<?>... interfaces) {
		this.target = target;
		this.targetClass = targetClass;
        this.interfaces = interfaces;
	}

	public Class<?> getTargetClass() {
		return targetClass;
	}

	public Object getTarget() {
		return target;
	}

    public Class<?>[] getInterfaces() {
        return interfaces;
    }
}
  • ProxyFactory : 策略模式和工厂模式的结合使用
public class ProxyFactory extends AdvisedSupport implements AopProxy {

    // 在 creator 中,生成相应的动态代理的时候就可以使用工厂类的 getProxy()
	@Override
	public Object getProxy() {
		return createAopProxy().getProxy();
	}

	protected final AopProxy createAopProxy() {
		return new Cglib2AopProxy(this);
	}
	
	//...可以根据 TargetSource 决定使用不同的动态代理
	protected final AopProxy createAopProxy(TargetSource targetSource) {
	    return new JdkDynamicAopProxy(this);
	}
}
  • Cglib2AopProxy
public class Cglib2AopProxy extends AbstractAopProxy {

	public Cglib2AopProxy(AdvisedSupport advised) {
		super(advised);
	}

	@Override
	public Object getProxy() {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(advised.getTargetSource().getTargetClass());
		enhancer.setInterfaces(advised.getTargetSource().getInterfaces());
		enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
		Object enhanced = enhancer.create();
		return enhanced;
	}

	private static class DynamicAdvisedInterceptor implements MethodInterceptor {

		private AdvisedSupport advised;

		private org.aopalliance.intercept.MethodInterceptor delegateMethodInterceptor;

		private DynamicAdvisedInterceptor(AdvisedSupport advised) {
			this.advised = advised;
			this.delegateMethodInterceptor = advised.getMethodInterceptor();
		}

		@Override
		public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
			if (advised.getMethodMatcher() == null
					|| advised.getMethodMatcher().matches(method, advised.getTargetSource().getTargetClass())) {
				return delegateMethodInterceptor.invoke(new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, args, proxy));
			}
			return new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, args, proxy).proceed();
		}
	}

	private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

		private final MethodProxy methodProxy;

		public CglibMethodInvocation(Object target, Method method, Object[] args, MethodProxy methodProxy) {
			super(target, method, args);
			this.methodProxy = methodProxy;
		}

		@Override
		public Object proceed() throws Throwable {
			return this.methodProxy.invoke(this.target, this.arguments);
		}
	}

}