Spring技术内幕第2版之IoC和AOP原理

写在前面:
这篇博客是阅读《spring技术内幕》这本书的阅读记录。结合书籍和Spring源码跟踪,分析了IoC和AOP的设计原理。

第2章

2.2 IoC容器系列的设计与实现:BeanFactory和ApplicationContext

2.2.1 Spring的IoC容器系列

作为IoC容器,功能规范的设计表现为借口类BeanFactory,它体现了Spring为提供用户使用的IoC容器所设定的最基本的功能规范。

Spring IoC容器的设计

在这里插入图片描述

  • 从接口BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,是一条主要的BeanFactory设计路径。在这条接口设计路径中,BeanFactory接 口定义了基本的IoC容器的规范。在这个接口定义中,包括了getBean(0这样的IoC容器的基本方法(通过这个方法可以从容器中取得Bean)。而HierarchicalBeanFactory接 口在继承了BeanFactory的基本接口之后,增加了getParentBeanFactory()的接口功能,使BeanFactory具备了双亲IoC容器的管理功能。在接下来的ConfigurableBeanFactory接口中,主要定义了一 -些对BeanFactory的配置功能,比如通过setParentBeanFactory)设置双亲IoC容器,通过addBeanPostProcessor()配置Bean后置处理器,等等。通过这些接口设计的叠加,定义了BeanFactory就是简单IoC容器的基本功能。关于BeanFactory简单IoC容器的设计,我们会在后面的内容中详细介绍。
  • 第二条接口设计主线是,以ApplicationContext应用 上下文接口为核心的接口设计,这里涉及的主要接口设计有,从BeanFactory 到Listable BeanFactory,再到ApplicationContext,再到我们常用的WebApplicationContext或者ConfigurableApplicationContext接口。我们常用的应用上下文基本,上都是ConfigurableApplicationContext或者WebApplicationContext的实现。在这个接口体系中,ListableBeanFactory 和HierarchicalBeanFactory两个接口,连接BeanFactory接口定义和ApplicationConext应用上下文的接口定义。在ListableBeanFactory接口中, 细化了许多BeanFactory的接口功能,比如定义了getBeanDefinitionNames()接口方法;对于HierarchicalBeanFactory接口,我们在前文中已经提到过,对于ApplicationContext接口,它通过继承MessageSource、Resource Loader.ApplicationEventPublisher接口,在BeanFactory简 单IoC容器的基础上添加了许多对高级容器的特性的支持。
  • 这里涉及的是主要接口关系,而具体的IoC容器都是在这个接口体系下实现的,比如DefaultListableBeanFactory, 这个基本IoC容器的实现就是实现了Configurable-BeanFactory, 从而成为一个简单IoC容器的实现。像其他IoC容器,比如.XmlBeanFactory, 都是在DefaultListableBeanFactory的基础上做扩展,同样地,ApplicationContext的实现也是如此。
  • 这个接口系统是以BeanFactory和ApplicationContext为核心的。而BeanFactory又 是IoC容器的最基本接口,在ApplicationContext的设计中,一方面,可以看到它继承了BeanFactory接口体系中的ListableBeanFactory. AutowireCapableBeanFactory.HierarchicalBeanFactory等BeanFactory的接口,具备了BeanFactory IoC容器的基本功能,另-方面,通过继承MessageSource. ResourceLoadr. ApplicationEventPublisher这些接口,BeanFactory 为ApplicationContext赋予了更高级的IoC容器特性。对于ApplicationContext而言,为了在Web环境中使用它,还设计了WebApplicationContext接口,而这个接口通过继承ThemeSource接口来扩充功能。
  1. BeanFactory的应用场景

    BeanFactory借口定义了IoC容器最基本的形式,并且提供了IoC容器所应该遵守的最基本的契约服务,同时,也是使用IoC容器所应遵守的最底层和最基本的编程规范。

    BeanFactory接口设计了getBean方法,这个方法是使用IoC容器API的主要方法,通过这个方法,可以取得IoC容器中管理的Bean,Bean的取得是通过指定名字来索引的。

    有了BeanFactory的定义,用户可以执行以下操作:

    • 通过接口方法containsBean让用户能够判断容器是否含有指定名字的Bean。
    • 通过接口方法isSingleton来查询指定名字的Bean是否是Singleton类型的Bean。对于Singleton属性,用户可以在BeanDefinition中指定。
    • 通过接口方法isPrototype来查询指定名字的Bean是否是prototype类型的。与Singleton属性一样, 这个属性也可以由用户在BeanDefinition中指定。
    • 通过接口方法isTypeMatch来查询指定了名字的Bean的Class类型是否是特定的Class类型。这个Class类型可以由用户来指定。
    • 通过接口方法getType来查询指定名字的Bean的Class类型。
    • 通过接口方法getAliases来查询指定了名字的Bean的所有别名,这些别名都是用户在BeanDefinition中定义的。

在这里插入图片描述

  1. BeanFactory容器的设计原理

    在BeanFactory的基础上,Spring提供了符合这个IoC容器接口的一系列容器的实现。以XmlBeanFactory的实现为例:

在这里插入图片描述

XmlBeanFactory源码:

public class XmlBeanFactory extends DefaultListableBeanFactory {

	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}
	
	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}

}

在这里插入图片描述

通过编程式使用IoC容器,我们可以通过factory对象来使用DefaultListableBeanFactory这个IoC容器。在使用IoC容器时,需要如下几个步骤:

1) 创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。

2)创建一个BeanFactory,这里使用DefaultListableBeanFactory.

3)创建-一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,通过一一个 回调配置给BeanFactory.

4)从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注册Bean定义之后,需要的IoC容器就建立起来了。这个时候就可以直接使用IoC容器了。

  1. ApplicationContext的应用场景

    Application是一个高级形态意义上的IoC容器,在BeanFactory的基础上添加的附加功能,这些功能为ApplicationContext提供了以下BeanFactory不具备的特征:

    • 支持不同的信息源。我们看到ApplicationContext扩展了MessageSource接口,这些信息源的扩展功能可以支持国际化的实现,为开发多语言版本的应用提供服务。
    • 访问资源。这- -特性体现在对ResourceLoader和Resource的支持上,这样我们可以从不同地方得到Bean定义资源。这种抽象使用户程序可以灵活地定义Bean定义信息,尤其是从不同的I/O途径得到Bean定义信息。这在接口关系上看不出来,不过一般来说,具体ApplicationContext都是继承了DefaultResourceLoader的子类。因为DefaultResourceLoader是AbstractApplicationContext的基类,关于Resource在IoC容器中的使用,后面会有详细的讲解。
    • 支持应用事件。继承了接口ApplicationEventPublisher,从而在上下文中引入了事件机制。这些事件和Bean的生命周期的结合为Bean的管理提供了便利。
    • 在ApplicationContext中提供的附加服务。这些服务使得基本IoC容器的功能更丰富。因为具备了这些丰富的附加功能,使得ApplicationContext与简单的BeanFactory相比,对它的使用是一种面向框架的使用风格,所以一般建议在开发应用时使用ApplicationContext作为IoC容器的基本形式。
  2. ApplicationContext容器的设计原理

    以FileSystemXmlApplicationContext的实现为例来说明ApplicationContext容器的设计原理。作为一个具体的应用上下文,只需要实现和它自身设计相关的两个功能。

    一个功能是,如果应用直接使用FileSystemXmlApplicationContext,对于实例化这个应用上下文的支持,同时启动IoC容器的refresh()过程:

    public FileSystemXmlApplicationContext(
          String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
          throws BeansException {
    
       super(parent);
       setConfigLocations(configLocations);
       if (refresh) {
          refresh();
       }
    }
    

    refresh()在IoC容器启动的具体实现,在后面进行分析。

    另一个功能是与FileSystemXmlApplicationContext设计具体相关的功能,这部分与怎样从文件系统中加载XML的Bean定义资源有关。通过这个过程,可以为在文件系统中读取以XML形式存在的BeanDefinition做准备,因为不同的应用上下文实现对应着不同的读取BeanDefinition的方式,在FileSystemXmlApplicationContext的实现代码如下:

    protected Resource getResourceByPath(String path) {
       if (path.startsWith("/")) {
          path = path.substring(1);
       }
       return new FileSystemResource(path);
    }
    

2.3 IoC容器的初始化过程

IoC容器的初始化是由refresh()方法启动的,这个方法标志着IoC容器的正式启动。具体来说,这个启动包括BeanDefinition的Resource定位、载入和注册三个基本过程。

2.3.1 BeanDefinition的Resource定位
2.3.2 BeanDefinition的载入和解析

跟踪源码得到Resource的定位和载入:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3.3 BeanDefinition在IoC容器的注册

在这里插入图片描述

2.4 IoC容器的依赖注入

getBean是依赖注入的起点,之后会调用createBean,在createBean过程中,Bean对象会依据BeanDefinition定义的要求生成。在AbstractAutowireCapableBeanFactory中实现了这个createBean。

在createBean()中调用了doCreateBean()创建Bean的调用。doCreateBean()中有个重要的方法: createBeanInstance和populatedBean。在createBeanInstance中生成了Bean所包含的Java对象,这个对象的生成方式都是由相关的BeanDefinition来指定的。最常见的实例化过程是instantiateBean,使用CGLIB对Bean进行实例化。SimpleInstantiationStrategy类是Spring用来生成Bean对象的默认类,提供两种实例化Java对象的方法。一种是BeanUtils,一种是CGLIB。

在实例化Bean对象生成的基础上,再介绍一下Spring是怎样的对这些对象进行处理的,也就是Bean对象生成以后,怎样把这些Bean对象的依赖关系设置好,完成这个依赖注入过程。这个过程设计对各种Bean对象的属性的处理过程(即依赖关系处理的过程),这个依赖关系处理的依据就是已经解析得到的BeanDefinition。populatedBean方法就是处理这个过程的,这个方法在AbstractAutowireCapableBeanFactory中实现。
在这里插入图片描述

2.5 容器的其他相关特性的设计

2.5.1 ApplicationContext和BEan的初始化及销毁
2.5.2 lazy-init属性和预实例化

对lazy-init属性是容器refresh的一部分。在finishBeanFactoryInitialization的方法中,封装了对lazy-init属性的处理,实际的处理是在DefaultLIstableBeanFactory这个基本容器的preInstantiateSingletons方法中完成的。这个方法对单例Bean完成预实例化。

2.5.3 FactoryBean的实现

跟随getBean的源码发现,FactoryBean的实现先是获得FactoryBean,然后如果要求获得的beanName不是以$开头的话走到最后会调用getObject方法!

2.5.4 BeanPostProcessor的实现
5.5.5 autowiring(自动依赖装配的实现)

BeanPostProcessor是使用IoC容器时经常会遇到的-一个特性,这个Bean的后置处理器是一个监听器,它可以监听容器触发的事件。将它向IoC容器注册后,容器中管理的Bean具备了接收IoC容器事件回调的能力。BeanPostProcessor的使用非常 简单,只需要通过设计一个具体的后置处理器来实现。同时,这个具体的后置处理器需要实现接口类BeanPostProcessor,然后设置到XML的Bean配置文件中。这个BeanPostProcessor是一个接口类,它有两个接口方法,一个是
postProcessBeforeInitialization, 在Bean的初始化前提供回调入口;一个是postProcessAfterInitialization,在Bean的初始化后提供回调入口,这两个回调的触发都是和容器管理Bean的生命周期相关的。下图展示了以getBean 方法为起始的调用关系
在这里插入图片描述

InitializeBean调用中完成了postProcessBeforeInitialization和postProcessBAfterInitialization方法的调用

在这里插入图片描述

具体的初始化过程也是IoC容器完成依赖注人的一-个重要部分。在initializeBean方法中, 需要使用Bean的名字,完成依赖注人以后的Bean对象,以及这个Bean对应的BeanDefinition。在这些输入的帮助下,完成Bean的初始化工作,这些工作包括为类型是BeanNameAware的Bean设置Bean的名字,类型是BeanClassLoaderAware的Bean设置类装载器,类型是BeanFactoryAware的Bean设置自身所在的IoC容器以供回调使用,当然,还有对postProcess-BeforeInitialization/postProcess AfterInitialization的回调和初始化属性init- method的处理等。经过这一-系列的初始化处理之后,得到的结果就是可以正常使用的由IoC容器托管的Bean了。实现代码:
在这里插入图片描述

2.5.5 autowiring(自动依赖装配)的实现

populateBean中对autowiring属性的处理

if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
      mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
   MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
   if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
      autowireByName(beanName, mbd, bw, newPvs);
   }
   if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
      autowireByType(beanName, mbd, bw, newPvs);
   }
   pvs = newPvs;
}

以autowireByName为例:
在这里插入图片描述

2.5.6 Bean的依赖检查
2.5.7 Bean对IoC容器的感知

在某些情况下,需要在Bean中直接对IoC容器进行操作,Spring通过特定aware接口来完成的。aware接口有以下这些:

  • BeanNameAware,可以在Bean中得到它在IoC容器中的Bean实例名称。
  • BeanFactoryAware,可以在Bean中得到Bean所在的IoC容器,从而直接在Bean中使用IoC容器的服务。
  • ApplicationContextAware,可以在Bean中得到Bean所在的应用上下文,从而直接在Bean中使用应用上下文的服务。
  • MessageSourceAware,在Bean中可以得到消息源。
  • ApplicationEventPublisherAware,在Bean中可以得到应用上”下文的事件发布器,从而可以在Bean中发布应用上下文的事件。
  • ResourceLoaderAware,在Bean中可以得到ResourceLoader,从而在Bean中使用ResourceL oader加载外部对应的Resource资源。

2.6 总结

关于容器的基本工作原理,可以大致整理出一下几个方面:

  • BeanDefinition的定位。对IoC容器来说,它为管理POJO之间的依赖关系提供了帮助,但也要依据Spring的定义规则提供Bean定义信息。我们可以使用各种形式的Bean定义信息,其中比较熟悉和常用的是使用XML的文件格式。在Bean定义方面,Spring为 用户提供了很大的灵活性。在初始化IoC容器的过程中,首先需要定位到这些有效的Bean定义信息,这里Spring使用Resource接口来统一这些Bean定 义信息,而这个定位由ResourceLoader来完成。如果使用上下文,ApplicationContext本身就为客户提供了定位的功能。因为上下文本身就是DefaultResourceLoader的子类。如果使用基本的BeanFactory作为IoC容器,客户需要做的额外工作就是为BeanFactory指定相应的Resource来完成Bean信息的定位。
  • 容器的初始化。在使用上下文时,需要-一个对它进行 初始化的过程,完成初始化以后,这个IoC容器才是可用的。这个过程的入口是在refresh中实现的,这个refresh相当于容器的初始化函数。在初始化过程中,比较重要的部分是对BeanDefinition信息的载人和注册工作。相当于在IoC容器中需要建立一个BeanDefinition定义的数据映像,Spring为了达到载入的灵活性,把载入的功能从IoC容器中分离出来,由BeanDefinitionReader来完成Bean定义信息的读取、解析和IoC容器内部BeanDefinition的建立。在DefaultListableBeanFactory中, 这些BeanDefinition被维护在一个Hashmap中,以后的IoC容器对Bean的管理和操作就是通过这些BeanDefinition来完成的。

在容器初始化完成以后,IoC容器的使用就准备好了,但这时只是在IoC容器内部建立了BeanDefinition,具体的依赖关系还没有注入。在客户第- -次向IoC容 器请求Bean时,IoC容器对相关的Bean依赖关系进行注入。如果需要提前注入,客户可以通过lazy-init属性进行预实例化,这个预实例化是上下文初始化的–部分,起到提前完成依赖注人的控制作用。在依赖注入完成以后,IoC容器就会保持这些具备依赖关系的Bean供客户直接使用。这时可以通过getBean来取得Bean,这些Bean不是简单的Java对象,而是已经包含了对象之间依赖关系的Bean,尽管这些依赖注人的过程对用户来说是不可见的。

第3章

3.1 Spring AOP 概述

先简单的回顾一些相关的AOP概念,然后逐步展开第AOP实现原理的分析,通过对实现原理的分析来了解Spring AOP模块,在这些实现原理的分析中,包括代理对象的生成、AOP拦截器的实现等。在分析中,以ProxyFactoryBean和ProxyFactory为例进行说明。

3.1.2 Advice同时

从接口BeforeAdvice开始,在BeforeAdvice的继承关系中,定义了为待增强的目标方法设置的前置增强接口MethodBeforeAdvice,使用这个前置接口需要实现一个回调函数:

void before(Method method, Object[] args, @Nullable Object target) throws Throwable;

作为回调函数,before方法的实现在Advice中被配置到目标方法后,会在调用目标方法时被回调。具体的调用参数有: Method对象,这个参数是目标方法的反射对象; Object[]对象数组,这个对象数组中包含目标方法的输入参数。以CountingBeforeAdvice为 例来说明BeforeAdvice的具体使用,CountingBeforeAdvice是接口MethodBeforeAdvice的具体实现.

3.1.3 pointcut切点

在这里插入图片描述

public interface Pointcut {
   ClassFilter getClassFilter();
   MethodMatcher getMethodMatcher();
   Pointcut TRUE = TruePointcut.INSTANCE;
}

在Pointcut的基本接口定义中可以看到,需要返回一个MethodMatcher。对于Point的匹配判断功能,具体是由这个返回的MethodMatcher来完成的,也就是说,由这个MethodMatcher来判断是否需要对当前方法调用进行增强,或者是否需要对当前调用方法应用配置好的Advice通知。在Pointcut的类继承关系中,以正则表达式切点JdkRegexpMethodPointcut的实现原理为例,来具体了解切点Pointcut的工作原理。JdkRegexpMethodPointcut类 完成通过正则表达式对方法名进行匹配的功能。在JdkRegexpMethodPointcut的 基类StaticMethod-MatcherPointcut的实现中可以看到,设置MethodMatcher为StaticMethodMatcher,同时JdkRegexpMethodPointcut也是这个MethodMatcher的子类,它的类层次关系如图3-7所示。

3.1.4 Advisor通知器
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
   private Pointcut pointcut = Pointcut.TRUE;
   public DefaultPointcutAdvisor() {
   }
   public DefaultPointcutAdvisor(Advice advice) {
      this(Pointcut.TRUE, advice);
   }
   public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
      this.pointcut = pointcut;
      setAdvice(advice);
   }
   public void setPointcut(@Nullable Pointcut pointcut) {
      this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
   }
   public Pointcut getPointcut() {
      return this.pointcut;
   }
   public String toString() {
      return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
   }

}

3.2 Spring AOP的设计与实现

3.2.1 JVM的动态代理特性

在Spring AOP实现中,使用的核心技术是动态代理,而这种动态代理实际上是JDK的一个特性。
在这里插入图片描述
在这里插入图片描述

3.2.2 Spring AOP的设计分析
3.2.3 Spring AOP的应用场景

一方面,应用可以直接使用AOP的功能,设计应用的横切关注点,把跨越应用程序多个模块的功能抽象出来,并通过简单的AOP的使用,灵活地编制到模块中,比如可以通过AOP实现应用程序中的日志功能。另一方面,在Spring内部,.一些支持模块也是通过Spring AOP来实现的,比如后面将要详细介绍的事务处理。从这两个角度就已经可以看到Spring AOP的核心地位了。

3.3 简历AopProxy代理对象

3.3.1 设计原理

对于Spring应用,是通过配置和调用Spring的ProxyFactoryBean来完成代理对象生成任务的。
在这里插入图片描述

3.3.2 配置ProxyFactoryBean

基于XML配置:

  1. 定义使用的通知器Advisor,这个通知器应该作为一个Bean来定义。很重要的一点是,这个通知器的实现定义了需要对目标对象进行增强的切面行为,也就是Advice通知。
  2. 定义ProxyFactoryBean,把它作为另一个Bean来定义,它是封装AOP功能的主要类。在配置ProxyFactoryBean时,需要设定与AOP实现相关的重要属性,比如proxyInterface、interceptorNames和target等。从属性名称可以看出,interceptorNames属性的值 往往设置需要定义的通知器,因为这些通知器在ProxyFactoryBean的AOP配置下,是通过使用代理对象的拦截器机制起作用的。所以,这里依然沿用了拦截器这个名字,也算是旧瓶装新酒吧。
  3. 定义target属性,作为target属性注入的Bean,是需要用AOP通知器中的切面应用来增强的对象,也就是前面提到的base对象。
    在这里插入图片描述
3.3.3 ProxyFactoryBean生成AopProxy代理对象

具体的AopProxy生成过程:
在这里插入图片描述

ProxyFactorybean中getObject()方法:

public Object getObject() throws BeansException {
   initializeAdvisorChain();
   if (isSingleton()) {
      return getSingletonInstance();
   }
   else {
      if (this.targetName == null) {
         logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
               "Enable prototype proxies by setting the 'targetName' property.");
      }
      return newPrototypeInstance();
   }
}

其中为Proxy代理对象配置Advisor链是在initializeAdvisorChain()方法完成的:

private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
   if (this.advisorChainInitialized) {
      return;
   }
   if (!ObjectUtils.isEmpty(this.interceptorNames)) {
      if (this.beanFactory == null) {
         throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
               "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
      }
      if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
            this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
         throw new AopConfigException("Target required after globals");
      }
       // 这里是添加Advisor链的调用,是通过interceptorNames属性进行配置的
      for (String name : this.interceptorNames) {
         if (logger.isTraceEnabled()) {
            logger.trace("Configuring advisor or advice '" + name + "'");
         }

         if (name.endsWith(GLOBAL_SUFFIX)) {
            if (!(this.beanFactory instanceof ListableBeanFactory)) {
               throw new AopConfigException(
                     "Can only use global advisors or interceptors with a ListableBeanFactory");
            }
            addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
                  name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
         }
          // 如果程序在这里被调用,那么需要加入命名的拦截器advice,并且需要检查这个Bean是singleton还是prototype类型
         else {
            Object advice;
            if (this.singleton || this.beanFactory.isSingleton(name)) {
               advice = this.beanFactory.getBean(name);
            }
            else {
               advice = new PrototypePlaceholderAdvisor(name);
            }
            addAdvisorOnChainCreation(advice, name);
         }
      }
   }
   this.advisorChainInitialized = true;
}

这个初始化过程有一个标志位advisorChainInitialized,这个标志用来表示通知器链是否已经初始化。如果已经初始化,那么这里就不会再初始化,而是直接返回。也就是说,这个初始化的工作发生在应用第一次通过ProxyFactoryBean去获取代理对象的时候。在完成这个初始化之后,接着会读取配置中出现的所有通知器,这个取得通知器的过程也比较简单,把通知器的名字交给容器的getBean方法就可以了,这是通过对IoC容器实现的一一个回调来完成的。然后把从IoC容器中取得的通知器加入拦截器链中,这个动作是由addAdvisorOnChainCreation方法来实现的。

生成单例对象是在getSingletonInstance()的代码中完成,这个方法是ProxyFactoryBean生成AopProxy代理对象的调用入口。代理对象会封装对target目标对象的调用,也就是说针对target对象的方法调用行为会被这里生成的代理对象所拦截。具体的生成过程是,首先读取ProxyFactoryBean中的配置,为生成代理对象做好必要的准备,比如设置代理的方法调用接口等。Spring通过 AopProxy类来具体生成代理对象。对于getSingletonInstance()方法中代理对象的生成过程,如下代码所示:

private synchronized Object getSingletonInstance() {
   if (this.singletonInstance == null) {
      this.targetSource = freshTargetSource();
      if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
         // 根据AOP框架来判断需要代理的入口
         Class<?> targetClass = getTargetClass();
         if (targetClass == null) {
            throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
         }
         // 这里设置代理对象的接口
         setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
      }
      // Initialize the shared singleton instance.
      super.setFrozen(this.freezeProxy);
      // 这里的方法会使用ProxyFactory来生成需要的Proxy
      this.singletonInstance = getProxy(createAopProxy());
   }
   return this.singletonInstance;
}

// 通过createProxy返回的AopProxy来得到代理对象
protected Object getProxy(AopProxy aopProxy) {
		return aopProxy.getProxy(this.proxyClassLoader);
}

具体的代理对象生成,是在ProxyCreatorSupport中借助AopProxyFactory完成的。生成代理对象的入口实现:

protected final synchronized AopProxy createAopProxy() {
   if (!this.active) {
      activate();
   }
   // 通过AopProxyFactory取得AopProxy,这个AopProxyFactory是在初始化函数中定义的,使用的是DefaultAopProxyFactory
   return getAopProxyFactory().createAopProxy(this);
}

在Spring中,使用JDK和CGLIB来生成AopProxy代理对象的工作,是由JdkDynamicAopProxy和CglibProxyFactory来完成的。详细的代理对象的生成过程会在下
面的小节进行详细的分析。

3.3.4 JDK生成AopProxy代理对象

在这里插入图片描述

public Object getProxy(@Nullable 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);
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
3.3.5 CGLIB生成AopProxy代理对象
public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isDebugEnabled()) {
      logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
   }
   // 从advised中取得IoC容器中配置的target对象
   try {
      Class<?> rootClass = this.advised.getTargetClass();
      Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

      Class<?> proxySuperClass = rootClass;
      if (ClassUtils.isCglibProxyClass(rootClass)) {
         proxySuperClass = rootClass.getSuperclass();
         Class<?>[] additionalInterfaces = rootClass.getInterfaces();
         for (Class<?> additionalInterface : additionalInterfaces) {
            this.advised.addInterface(additionalInterface);
         }
      }

      // 
      validateClassIfNecessary(proxySuperClass, classLoader);

      //  验证代理对象的接口设置
      // 创建并配置CGLIB的Enhancer, 这个Enhancer是CGLIB的主要操作类
      Enhancer enhancer = createEnhancer();
      if (classLoader != null) {
         enhancer.setClassLoader(classLoader);
         if (classLoader instanceof SmartClassLoader &&
               ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
            enhancer.setUseCache(false);
         }
      }
       
      // 设置Enhancer对象,包括设置代理接口,回调方法
      // 来自advised的IoC配置,比如使用AOP的DynamicAdvisedInterceptor拦截器
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }
      // fixedInterceptorMap only populated at this point, after getCallbacks call above
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);

      // 生成代理对象
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   catch (CodeGenerationException | IllegalArgumentException ex) {
      throw new AopConfigException("Could not generate CGLIB subclass of class [" +
            this.advised.getTargetClass() + "]: " +
            "Common causes of this problem include using a final class or a non-visible class",
            ex);
   }
   catch (Throwable ex) {
      // TargetSource.getTarget() failed
      throw new AopConfigException("Unexpected AOP exception", ex);
   }
}

3.4 Spring AOP拦截器调用的实现

3.4.1 设计原理
3.4.2 JdkDynamicAopProxy的invoke拦截

在JdkDynamicAopProxy中生成Proxy对象时,对AopProxy代理对象的生成调用:

Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
3.4.4 目标对象方法的调用

如果没有设置拦截器,那么会对目标对象的方法直接进行调用。对于JdkDynamicAopProxy代理对象,这个对目标对象的方法调用是通过AopUtils使用反射机制在AopUtils .invokeJoinpointUsingReflection的方法中实现的,如下代码所示。 在这个调用中,首先得到调用方法的反射对象,然后使用invoke启动对方法反射对象的调用:

public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
      throws Throwable {
   // 使用方法调用target对象的方法
   try {
      ReflectionUtils.makeAccessible(method);
      return method.invoke(target, args);
   }
   catch (InvocationTargetException ex) {
      // Invoked method threw a checked exception.
      // We must rethrow it. The client won't see the interceptor.
      throw ex.getTargetException();
   }
   catch (IllegalArgumentException ex) {
      throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
            method + "] on target [" + target + "]", ex);
   }
   catch (IllegalAccessException ex) {
      throw new AopInvocationException("Could not access method [" + method + "]", ex);
   }
}
3.4.5 AOP拦截器链的调用

在运行拦截器的拦截方法之前,需要对代理方法完成一个匹配判断,通过这个匹配判断来决定拦截器是否满足切面增强的要求。大家一定还记得前面提到的,在Pointcut切点中需要进行matches的匹配过程,即matches调用对方法进行匹配判断,来决定是否需要实行通知增强。以下看到的调用就是进行matches的地方,具体的处理过程在ReflectiveMethodInvocation的proceed方法中,如下代码所示。在proceed方法中,先进行判断,如果现在已经运行到拦截器链的末尾,那么就会直接调用目标对象的实现方法;否则,沿着拦截器链继续进行,得到下一个拦截器,通过这个拦截器进行matches判断,判断是否是适用于横切增强的场合,如果是,从拦截器中得到通知器,并启动通知器的invoke方法进行切面增强。在这个过程结束以后,会迭代调用proceed方法,直到拦截器链中的拦截器都完成以上的拦截过程为止。

public Object proceed() throws Throwable {
    
   //从索引为-1的拦截器开始调用,并按序递增如果拦截器链中的拦截器迭代调用完毕,这里开始调用target的函数,这个函数是通过反射机制完成的,具体实现在AopUtils . invokeJoinpointUs ingReflection方法中
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }
   // 这里沿着定义好的interceptor0r InterceptionAdvice链进行处理
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // 这里对拦截器进行动态匹配的判断,还记得前面分析的Pointcut吗?这里是触发进行匹配的地方,如果和定义的Pointcut匹配,那么这个advice将会得到执行
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         // 如果不匹配,那么proceed会被递归调用,直到所有的拦截器都被运行过为止
         return proceed();
      }
   }
   else {
      // //如果是一个interceptor,直接调用这个interceptor对应的方法
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}
3.4.6 配置通知类
3.4.7 Advice通知的实现
3.4.8 ProxyFactory实现AOP

3.5 Spring AOP的高级特性

m =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// 如果不匹配,那么proceed会被递归调用,直到所有的拦截器都被运行过为止
return proceed();
}
}
else {
// //如果是一个interceptor,直接调用这个interceptor对应的方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}


#### 3.4.6 配置通知类

#### 3.4.7 Advice通知的实现

#### 3.4.8 ProxyFactory实现AOP

### 3.5 Spring AOP的高级特性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值