文章目录
  1. 1. AOP简介
  2. 2. spring AOP简介
  3. 3. 创建代理bean
  4. 4. 执行代理bean

AOP简介

什么是AOP?这可能是学习AOP时,最想知道的问题,在研究spring aop这块查阅了大量的资料,即使AOP联盟当初指定的规范也没有解释这个概念,如同OOP一样,AOP只是一种编程思想了,水平有限我只能这样解释了。OOP代码中的体现就是类了,那AOP呢?其实也是类(也可以理解为模块化),所谓的特殊的横切组织,参考下图:

一般项目里面的函数会包含业务性处理与非业务性处理两种,随着业务的发展里面可能会包含很多非业务的处理,这些非主业务的处理有可能在其他地方继续调用,这样代码的耦合性就非常高了。灰色的切面就是AOP的体现了,这些非业务的处理从代码中解耦出来就优雅多了。如果要实现自己的AOP组件要包含拦截器框架、字节码解析、织入组件(如动态代理)、配置组件,摘自AOP联盟

All these projects have their onw goals and speficities. However, several common basic components are still usefull (and sometimes required) to build a full AO system. For instance, a component that is able to add metadata on the base components, an interception framework, a component that is able to perform code translation in order to advice the classes, a weaver component, a configuration component, and so on.

spring AOP简介

如上面所述,spring实现AOP就包含了其中一些组件,如下图所示:

Spring AOP提供了几个重要概念性的东西,参考文档,其中Joinpoint与Advice是aop联盟提供的aop接口,Advice就广义的理解为拦截器,Joinpoint就是方法的执行(spring采用动态代理执行),Pointcut匹配的执行方法集合。
spring内部AOP实现包含了动态代理(JDK proxy、cglib proxy)与静态代理(AspectJ实现参考这里,在编译期间织入),
spring AOP只指针对方法级别,如果想粒度更细(如改变属性或者构造函数之类的)就要AspectJ了,但是spring解析Pointcut方法匹配还是用的AspectJ相关的包。

创建代理bean

下面以JDK动态代理实现AOP为例进行分析,配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="foo" class="mytest.aop.FooImpl"/>

    <bean id="aroundBean" class="mytest.aop.config.AroundBean"/>

    <bean id="myAdvice" class="mytest.aop.config.MyAdvice"/>

    <bean id="myAspect" class="mytest.aop.config.MyAspect"/>

    <aop:config>
        <aop:pointcut expression="execution(* mytest.aop.config.*.*(..))" id="pc"/>

        <aop:advisor advice-ref="myAdvice" pointcut-ref="pc"/>
        <aop:aspect ref="myAspect">
            <aop:around method="aroundAdvice" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

</beans>

参考《spring源码分析之基于注解配置的bean初始化分析》,跟分析context:component-scan/标签差不多,使用aop:config就由AopNamespaceHandler解析命名空间,然后ConfigBeanDefinitionParser解析标签,最后把AspectJAwareAdvisorAutoProxyCreator注册到容器中:

从这个图中就可以看出,AspectJAwareAdvisorAutoProxyCreator是BeanPostProcessor的一个子类,这个类就是执行创建代理bean的入口。整个创建流程参考下图:

初始化bean之前AspectJAwareAdvisorAutoProxyCreator将排除一些类不会生成代理类,如Advisor、Advice、Aspect以及Pointcut不匹配的类,参考AbstractAutowireCapableBeanFactory:

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    //执行initializeBean,bean是否有其他操作(如代理对象)
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                invokeAwareMethods(beanName, bean);
                return null;
            }
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
//排除Advice、Advise、Aspect切面相关的类,并创建拦截器链
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }

    if (mbd == null || !mbd.isSynthetic()) {
        //AspectJAwareAdvisorAutoProxyCreator执行创建代理对象
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

参考applyBeanPostProcessorsAfterInitialization方法,创建代理bean就有AspectJAwareAdvisorAutoProxyCreator的父类AbstractAdvisorAutoProxyCreator执行:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    //postProcessBeforeInstantiation方法已经将排除的bean放置到advisedBeans
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    //获取Alliance中实现的Interceptors
    //一般bean如果不包含在PointCut表达式中,返回为null
    // Create proxy if we have advice.
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        //将拦截器链传递给ProxyFactory
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

前面的配置文件没有特殊的配置,spring内部默认使用JDK动态代理,参考DefaultAopProxyFactory:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    mylog.debug("创建JdkDynamicAopProxy还是ObjenesisCglibAopProxy");
    //检查是不是接口,或者是不是强制设置ProxyTargetClass为true,是就使用Cglib代理
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }

        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

然后JdkDynamicAopProxy通过getProxy就获取到代理对象了。再些一个测试类,使用上面的配置文件:

@Test
public void testProxy() {
    ITestBean aroundBean = (ITestBean) context.getBean("aroundBean");
    IFoo foo = (IFoo)context.getBean("foo");

    System.out.println("aroundBean:" + AopUtils.isAopProxy(aroundBean));
    System.out.println("foo:" + AopUtils.isAopProxy(foo));
}

输入结果为:

aroundBean:true
foo:false

aroundBean在拦截表达式内,所以是代理对象,foo不在该范围内就不是代理对象。

执行代理bean

由于跟bean相关的拦截器初始化时就已经配置好了,JdkDynamicAopProxy实现了InvocationHandler,所以执行bean的方法时就直接调用invoke方法,执行流程如下:

参考ReflectiveMethodInvocation(Joinpoint的一个实现类)执行拦截器链以及目标函数:

public Object proceed() throws Throwable {
    //    We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            return proceed();
        }
    }
    else {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

执行测试代码

@Test
public void testAround() {
    //如果是JDK代理只能声明接口,获取到的实例是JDK产生的代理bean
    ITestBean aroundBean = (ITestBean) context.getBean("aroundBean");
    aroundBean.print();
}

最后打印输出:

MyAspect: around before
AroundBean's print
MyAspect: around after
文章目录
  1. 1. AOP简介
  2. 2. spring AOP简介
  3. 3. 创建代理bean
  4. 4. 执行代理bean