1. AOP 概念

AOP(Aspect Oriented Programming),即面向切面编程。

  • 连接点(JoinPoint)
    程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点植入增强。

  • 切点(Pointcut)
    每个类具有多个连接点,如果一个类拥有15个方法,那么这些方法都是连接点,连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。

  • 增强(Advice)
    增强是植入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息切点信息,我们就可以找到特定的连接点。

  • 通知器(Advisor)
    当我们完成切面增强设计(Advice)和切入点的设计(Pointcut),需要一个对象把他们结合起来,Advisor 就是起到这个作用,通过Advisor ,可以确定在哪个Pointcut 使用哪个Advice。所以一个Advisor包含一个Advice 和 一个Pointcut 信息。



我们先来了解一下,bean实例化过程,便于我们后续理解aop。

下面有很简单的一段代码可以作为Spring代码加载的入口:


 ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); ac.getBean(XXX.class);


ClassPathXmlApplicationContext用于加载CLASSPATH下的Spring配置文件,可以看到,第二行就已经可以获取到Bean的实例了,那么必然第一行就已经完成了对所有Bean实例的加载,因此可以通过ClassPathXmlApplicationContext作为入口。为了后面便于代码阅读,先给出一下ClassPathXmlApplicationContext这个类的继承关系:

spring aop源码分析_java




ClassPathXmlApplicationContext存储内容

为了更理解ApplicationContext,拿一个实例ClassPathXmlApplicationContext举例,看一下里面存储的内容,加深对ApplicationContext的认识,以表格形式展现:

对象名类  型作  用归属类
configResourcesResource[]配置文件资源对象数组ClassPathXmlApplicationContext
configLocationsString[]配置文件字符串数组,存储配置文件路径AbstractRefreshableConfigApplicationContext
beanFactoryDefaultListableBeanFactory上下文使用的Bean工厂AbstractRefreshableApplicationContext
beanFactoryMonitorObjectBean工厂使用的同步监视器AbstractRefreshableApplicationContext
idString上下文使用的唯一Id,标识此ApplicationContextAbstractApplicationContext
parentApplicationContext父级ApplicationContextAbstractApplicationContext
beanFactoryPostProcessorsList<BeanFactoryPostProcessor>存储BeanFactoryPostProcessor接口,Spring提供的一个扩展点AbstractApplicationContext
startupShutdownMonitorObjectrefresh方法和destory方法公用的一个监视器,避免两个方法同时执行AbstractApplicationContext
shutdownHookThreadSpring提供的一个钩子,JVM停止执行时会运行Thread里面的方法AbstractApplicationContext
resourcePatternResolverResourcePatternResolver上下文使用的资源格式解析器AbstractApplicationContext
lifecycleProcessorLifecycleProcessor用于管理Bean生命周期的生命周期处理器接口AbstractApplicationContext
messageSourceMessageSource用于实现国际化的一个接口AbstractApplicationContext
applicationEventMulticasterApplicationEventMulticasterSpring提供的事件管理机制中的事件多播器接口AbstractApplicationContext
applicationListenersSet<ApplicationListener>Spring提供的事件管理机制中的应用监听器AbstractApplicationContext

 

ClassPathXmlApplicationContext构造函数

看下ClassPathXmlApplicationContext的构造函数:


 public ClassPathXmlApplicationContext(String configLocation) throws BeansException {     this(new String[] {configLocation}, true, null); } public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)         throws BeansException {      super(parent);     setConfigLocations(configLocations);     if (refresh) {         refresh();     } }


从第二段代码看,总共就做了三件事:

  1、super(parent)

    没什么太大的作用,设置一下父级ApplicationContext,这里是null

  2、setConfigLocations(configLocations)

    代码就不贴了,一看就知道,里面做了两件事情:

    (1)将指定的Spring配置文件的路径存储到本地

    (2)解析Spring配置文件路径中的${PlaceHolder}占位符,替换为系统变量中PlaceHolder对应的Value值,System本身就自带一些系统变量比如class.path、os.name、user.dir等,也可以通过System.setProperty()方法设置自己需要的系统变量

  3、refresh()

    这个就是整个Spring Bean加载的核心了,它是ClassPathXmlApplicationContext的父类AbstractApplicationContext的一个方法,顾名思义,用于刷新整个Spring上下文信息,定义了整个Spring上下文加载的流程。



了解完,bean的加载流程,接下来,我们正式开始AOP的学习了

  有关于AOP,我们在面试中也被无数次问到,AOP是什么?AOP有什么作用与优势?AOP在项目中是如何用到的?


    这些还都是比较简单的,有些可能会问你AOP的实现是怎样的?


    哪怕没有看过源码的同学也应该知道,AOP是通过动态代理实现的,动态代理又分为两个部分:JDK动态代理和CGLIB动态代理


    确实,Spring也就是通过这两种方式来实现AOP相关功能,下面就通过源码来简单求证下

1.AOP功能简单实现

    1)引入maven依赖(笔者使用SpringBoot开发)

<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>1.5.3.RELEASE</version></parent>     <dependency>          <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-aop</artifactId>  </dependency>

   2)创建接口及其实现类

public interface Person {  void say();}public class Student implements Person{   public void say(){    System.out.println("这是一个苦逼的程序员");  }}

3)创建切面类

@Aspectpublic class AspectJTest {   @Pointcut("execution(* *.say(..))")  public void test(){}   @Before("test()")  public void before(){    System.out.println("before test..");  }   @After("test()")  public void after(){    System.out.println("after test..");  }   @Around("test()")  public Object around(ProceedingJoinPoint p){    System.out.println("before1");    Object o = null;    try {      o = p.proceed();    } catch (Throwable e) {      e.printStackTrace();    }    System.out.println("after1");    return o;  }}

  4)创建beans.xml

<?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">   <aop:aspectj-autoproxy/>    <bean id="student" class="test.Student"/>  <bean class="test.AspectJTest"/></beans>

 5)测试类

public class Test {   public static void main(String[] args) {     ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");    Person bean2 = (Person)ac.getBean("student");    bean2.say();  }    // 结果如下:    before1    before test..    这是一个苦逼的程序员    after1    after test..

总结:AOP功能的使用还是比较简单的,把相关bean注入到Spring容器中,编写好相应的Aspect类即可


    1)在使用ApplicationContext相关实现类加载bean的时候,会针对所有单例且非懒加载的bean,在构造ApplicationContext的时候就会创建好这些bean,而不会等到使用的时候才去创建。这也就是单例bean默认非懒加载的应用


    2)读者需要了解BeanPostProcessor的相关使用,所有实现BeanPostProcessor接口的类,在初始化bean的时候都会调用这些类的方法,一般用于在bean初始化前或后对bean做一些修改。而AOP的功能实现正式基于此,在bean初始化后创建针对该bean的proxy,然后返回给用户该proxy


 


    3)结合以上两点,被代理后的bean,实际在ApplicationContext构造完成之后就已经被创建完成,getBean()的操作直接从singletonObjects中获取即可



3.AOP源码架构分析

    1)寻找 <aop:aspectj-autoproxy/> 注解对应的解析器

      但凡注解都有对应的解析器,以用来解析该注解的行为。全局搜索之后可发现。

org.springframework.aop.config.AopNamespaceHandler类中有对应的解析行为,代码如下:public class AopNamespaceHandler extends NamespaceHandlerSupport {  @Override  public void init() {    // In 2.0 XSD as well as in 2.1 XSD.    registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());    registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());// 就是该段代码    registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());     // Only in 2.0 XSD: moved to context namespace as of 2.1    registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());  }}

   2)了解AspectJAutoProxyBeanDefinitionParser对应的行为

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {   @Override  public BeanDefinition parse(Element element, ParserContext parserContext) {        // 1.注册proxy creator    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);    extendBeanDefinition(element, parserContext);    return null;  }    ...     // registerAspectJAnnotationAutoProxyCreatorIfNecessary()    public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(      ParserContext parserContext, Element sourceElement) {        // 注册行为主要内容    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(        parserContext.getRegistry(), parserContext.extractSource(sourceElement));    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);    registerComponentIfNecessary(beanDefinition, parserContext);  }     // registerAspectJAnnotationAutoProxyCreatorIfNecessary()  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {        // 主要就是为了注册AnnotationAwareAspectJAutoProxyCreator类    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);  }     // 注册类相关代码    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {      BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);      if (!cls.getName().equals(apcDefinition.getBeanClassName())) {        int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());        int requiredPriority = findPriorityForClass(cls);        if (currentPriority < requiredPriority) {          apcDefinition.setBeanClassName(cls.getName());        }      }      return null;    }         // 类似于我们在使用BeanFactory.getBean()时候的操作,生成一个RootBeanDefinition,然后放入map中    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);    beanDefinition.setSource(source);    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);    return beanDefinition;  }

  总结:通过以上的代码分析,可知,AspectJAutoProxyBeanDefinitionParser主要的功能就是将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中,把bean交给Spring去托管。


    AnnotationAwareAspectJAutoProxyCreator的功能我们大胆猜测一下:应该也就是生成对象的代理类的相关功能,这个我们接下来再看。


    


    问题:


    那么问题来了,我们最开始的类AopNamespaceHandler.init()方法是在什么时候被调用的呢?什么时候生效的?这个决定了我们注册到Spring的AnnotationAwareAspectJAutoProxyCreator的生效时间?读者可自行思考下。


 


    3)分析AnnotationAwareAspectJAutoProxyCreator主要行为

        通过查看AnnotationAwareAspectJAutoProxyCreator的类层次结构,可知,其实现了BeanPostProcessor接口,实现类为AbstractAutoProxyCreator


        类层次结构如下:

spring aop源码分析_java_02

   4)AbstractAutoProxyCreator主要方法

  @Override  public Object postProcessBeforeInitialization(Object bean, String beanName) {    return bean;  }     // 主要看这个方法,在bean初始化之后对生产出的bean进行包装  @Override  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {    if (bean != null) {      Object cacheKey = getCacheKey(bean.getClass(), beanName);      if (!this.earlyProxyReferences.contains(cacheKey)) {        return wrapIfNecessary(bean, beanName, cacheKey);      }    }    return bean;  }     // wrapIfNecessary  protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {      return bean;    }    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;    }     // Create proxy if we have advice.        // 意思就是如果该类有advice则创建proxy,    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);    if (specificInterceptors != DO_NOT_PROXY) {      this.advisedBeans.put(cacheKey, Boolean.TRUE);            // 1.通过方法名也能简单猜测到,这个方法就是把bean包装为proxy的主要方法,      Object proxy = createProxy(          bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));      this.proxyTypes.put(cacheKey, proxy.getClass());             // 2.返回该proxy代替原来的bean      return proxy;    }     this.advisedBeans.put(cacheKey, Boolean.FALSE);    return bean;  }

总结:


    1)通过AspectJAutoProxyBeanDefinitionParser类将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中


    2)AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization()方法将所有有advice的bean重新包装成proxy


 


 


4.创建proxy过程分析

    通过之前的代码结构分析,我们知道,所有的bean在返回给用户使用之前都需要经过AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization()方法,而该方法的主要作用也就是将所有拥有advice的bean重新包装为proxy,那么我们接下来直接分析这个包装为proxy的方法即可,看一下bean如何被包装为proxy,proxy在被调用方法时,是具体如何执行的


 


    以下是AbstractAutoProxyCreator.wrapIfNecessary(Object bean, String beanName, Object cacheKey)中的createProxy()代码片段分析

  protected Object createProxy(      Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {     if (this.beanFactory instanceof ConfigurableListableBeanFactory) {      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);    }         // 1.创建proxyFactory,proxy的生产主要就是在proxyFactory做的    ProxyFactory proxyFactory = new ProxyFactory();    proxyFactory.copyFrom(this);     if (!proxyFactory.isProxyTargetClass()) {      if (shouldProxyTargetClass(beanClass, beanName)) {        proxyFactory.setProxyTargetClass(true);      }      else {        evaluateProxyInterfaces(beanClass, proxyFactory);      }    }         // 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到ProxyFactory中    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);    for (Advisor advisor : advisors) {      proxyFactory.addAdvisor(advisor);    }     proxyFactory.setTargetSource(targetSource);    customizeProxyFactory(proxyFactory);     proxyFactory.setFrozen(this.freezeProxy);    if (advisorsPreFiltered()) {      proxyFactory.setPreFiltered(true);    }         // 3.调用getProxy获取bean对应的proxy    return proxyFactory.getProxy(getProxyClassLoader());  }

1)创建何种类型的Proxy?JDKProxy还是CGLIBProxy?

    // getProxy()方法  public Object getProxy(ClassLoader classLoader) {    return createAopProxy().getProxy(classLoader);  }      // createAopProxy()方法就是决定究竟创建何种类型的proxy  protected final synchronized AopProxy createAopProxy() {    if (!this.active) {      activate();    }        // 关键方法createAopProxy()    return getAopProxyFactory().createAopProxy(this);  }     // createAopProxy()  public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {        // 1.config.isOptimize()是否使用优化的代理策略,目前使用与CGLIB        // config.isProxyTargetClass() 是否目标类本身被代理而不是目标类的接口        // hasNoUserSuppliedProxyInterfaces()是否存在代理接口    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.");      }             // 2.如果目标类是接口或者是代理类,则直接使用JDKproxy      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {        return new JdkDynamicAopProxy(config);      }             // 3.其他情况则使用CGLIBproxy      return new ObjenesisCglibAopProxy(config);    }    else {      return new JdkDynamicAopProxy(config);    }  }

2)getProxy()方法

    由1)可知,通过createAopProxy()方法来确定具体使用何种类型的Proxy

    针对于该示例,我们具体使用的为JdkDynamicAopProxy,下面来看下JdkDynamicAopProxy.getProxy()方法

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable// JdkDynamicAopProxy类结构,由此可知,其实现了InvocationHandler,则必定有invoke方法,来被调用,也就是用户调用bean相关方法时,此invoke()被真正调用    // getProxy()    public Object getProxy(ClassLoader classLoader) {    if (logger.isDebugEnabled()) {      logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());    }    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);         // JDK proxy 动态代理的标准用法    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  }

   3)invoke()方法

    以上的代码模式可以很明确的看出来,使用了JDK动态代理模式,真正的方法执行在invoke()方法里,下面我们来看下该方法,来看下bean方法如何被代理执行的

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    MethodInvocation invocation;    Object oldProxy = null;    boolean setProxyContext = false;     TargetSource targetSource = this.advised.targetSource;    Class<?> targetClass = null;    Object target = null;     try {            // 1.以下的几个判断,主要是为了判断method是否为equals、hashCode等Object的方法      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {        // The target does not implement the equals(Object) method itself.        return equals(args[0]);      }      else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {        // The target does not implement the hashCode() method itself.        return hashCode();      }      else if (method.getDeclaringClass() == DecoratingProxy.class) {        // There is only getDecoratedClass() declared -> dispatch to proxy config.        return AopProxyUtils.ultimateTargetClass(this.advised);      }      else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&          method.getDeclaringClass().isAssignableFrom(Advised.class)) {        // Service invocations on ProxyConfig with the proxy config...        return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);      }       Object retVal;       if (this.advised.exposeProxy) {        // Make invocation available if necessary.        oldProxy = AopContext.setCurrentProxy(proxy);        setProxyContext = true;      }       // May be null. Get as late as possible to minimize the time we "own" the target,      // in case it comes from a pool.      target = targetSource.getTarget();      if (target != null) {        targetClass = target.getClass();      }       // 2.获取当前bean被拦截方法链表      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);       // 3.如果为空,则直接调用target的method      if (chain.isEmpty()) {        Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);        retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);      }            // 4.不为空,则逐一调用chain中的每一个拦截方法的proceed      else {        // We need to create a method invocation...        invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);        // Proceed to the joinpoint through the interceptor chain.        retVal = invocation.proceed();      }       ...      return retVal;    }    ...  }


    4)拦截方法真正被执行调用invocation.proceed()

  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);    }  }

总结4:依次遍历拦截器链的每个元素,然后调用其实现,将真正调用工作委托给各个增强器


 


    


    总结:


    纵观以上过程可知:实际就是为bean创建一个proxy,JDKproxy或者CGLIBproxy,然后在调用bean的方法时,会通过proxy来调用bean方法


    重点过程可分为:


    1)通过AspectJAutoProxyBeanDefinitionParser类将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中


    2)AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization()方法将所有有advice的bean重新包装成proxy


    3)调用bean方法时通过proxy来调用,proxy依次调用增强器的相关方法,来实现方法切入