Spring 4.3 源码分析之 IOC 组件概述

1. IOC 概述

IOC: Inversion of Control(控制反转), 这里其实指的是: 将程序中需要使用的 POJOs, 丢入到容器中, 解析成统一的 BeanDefinition(主要基于XML的 GenericBeanDefinition/RootBeanDefinition 与 通过 注解生成的 ScannedGenericBeanDefinition), 而 Bean 依赖的信息都在XML中描述(或通过注解描述), 容器则负责 Bean 的统一生成(本来Bean的创建/销毁是由我们开发人员控制的), 因此造成了 Bean 的创建/销毁由Bean容器控制的反转现象!(通过下图理解一下)


container-magic.png

图中角色:

1. 如上图 Spring IOC 容器使用了 Configuration Metadata, Configuration Metadata 告知 Spring容器如何去实例化, 配置和装配对应的对象
2. Configuration Metadata(及 BeanDefinition)现在主要由解析XML.Properties 或通过扫描指定目录下带有特定注解的 Bean 生成
2. IOC 主要组件 BeanDefinition 的属性

先看一下 BeanDefinition 主要属性

// Bean 的Class 对象
private volatile Object beanClass;

// bean 的作用范围, 对应 bean 属性 scope (常见的就是单例/原型)
private String scope = SCOPE_DEFAULT;

// 是否是抽象, 来自 bean 属性的 abstract(抽象的类是不能直接生成对象)
private boolean abstractFlag = false;

// 是否延迟加载, 对应 bean 属性 lazy-init (值是否在使用 Bean的时候才正真的创建 Bean)
private boolean lazyInit = false;

// 自动注入模式, 对应 bean 属性 autowire (这个属性主要指 XML 描述的 beanDefinition, 在生成bean的对吼阶段, 获取容器中的对象, 自动装配在 BeanDefinition 对应的 Field 上)
private int autowireMode = AUTOWIRE_NO;

// 依赖检查, Spring 3.0 后 弃用这个属性
private int dependencyCheck = DEPENDENCY_CHECK_NONE;

// 用来表示一个 bean 的实例化依靠另一个 bean 先实例化(PS: 这个很少使用了)
private String[] dependsOn;

/**
 *  autowire-candidate 属性设置为 false, 
 *  这样容器在查找自动装配对象时
 *  将不考虑该 bean, 即它不会被考虑作为其他 bean 
 *  自动装配的候选者, 但是该 bean 
 *  本身还是可以使用自动装配来注入其他的 bean
 */
private boolean autowireCandidate = true;

// 自动装配时当出现多个 bean 候选者时, 将作为首候选者 (PS: 使用比较少)
private boolean primary = false;

// 用于记录 Qualifier, 对应子元素 qualifier(当使用 @Autowired 时, 有多个候选Bean 时, 就通过这个Qualifier 来进行区分)
private final Map<String, AutowireCandidateQualifier> qualifiers =
        new LinkedHashMap<String, AutowireCandidateQualifier>(0);

// 允许访问非公开的构造器和方法, 程序设置  (PS: 用得比较少)
private boolean nonPublicAccessAllowed = true;

/**
 * 是否以一种宽松的模式解析构造函数, 默认 true
 * 如果是 false, 则在如下情况
 * interface ITest()
 * class ITestImpl implement ITest();
 * class Main {
 *     Main(ITest i) {}
 *     Main(ITestImpl i) {}
 * }
 * 抛出异常, 因为 Spring 无法准确确定哪个构造函数
 * 程序设置
 *
 * lenient 宽大, 仁慈
 */
private boolean lenientConstructorResolution = true;

/**
 * 对应 bean 属性 factory-bean 用法 (PS: 这里涉及 FactoryBean 这个概念, 这个类主要是解决: 创建一个类, 但创建这个类的过程比较长/条件比较多, 这时候就使用同一的抽象工厂模式(FactoryBean对象) 来创建对象)
 * <bean id="instanceFactoryBean" class="example.chapter3.InstanceFactoryBean />
 * <bean id="currentTime" factory-bean="instanceFactoryBean" factory-method="createTime" />
 */
private String factoryBeanName;

// 对应 bean 属性 factory-method
private String factoryMethodName;

// 记录构造函数注入属性, 对应 bean 属性 constructor-arg
private ConstructorArgumentValues constructorArgumentValues;

// 普通属性集合 (在XML 中定义Bean的信息时, 通常propertyValues里面有很多依赖信息)
private MutablePropertyValues propertyValues;

// 方法重写的持有者, 记录 lookup-method, replaced-method 元素(PS: 与此对应有注解Loopup, 但运用的比较少了)
private MethodOverrides methodOverrides = new MethodOverrides();

// 初始化方法, 对应 bean 属性 init-method (PS: 通过 实现InitializingBean 接口, 可以达到同样效果)
private String initMethodName;

// 销毁方法, 对应  bean 属性 destory-method (PS: 与之对应的是 DisposableBean, 一帮都是在这类方法中完成资源释放之类的操作)
private String destroyMethodName;

// 是否执行 init-method, 程序设置  (默认是 true)
private boolean enforceInitMethod = true;

// 是否执行 destory-method, 程序设置 (默认是 true)
private boolean enforceDestroyMethod = true;

// 是否是用户定义的而不是应用程序本身定义的, 创建 AOP 部分组件时为 true(见 ConfigBeanDefinitionbeanParser.parseAdvice 方法)
private boolean synthetic = false;

/**
 * 定义这个 bean 的应用, APPLICATION: 用户, INFRASTRUCTURE(infrastructure 基础设施): 内部使用, 与用户无关, SUPPORT: 某些复杂配置的一部分程序设置, 一般都是 BeanDefinition.ROLE_APPLICATION;
 */
private int role = BeanDefinition.ROLE_APPLICATION;

// bean 的描述信息
private String description;

// 这个 bean 定义的资源 (其实就是 A.class 这个文件)
private Resource resource;

PS: 属性好多, 一直其实上面属性中相对重要的有一下几个
2.1 class(该实例化的 Bean), name(bean的命名)
2.2 scope(定义Bean的生命周期)
2.3 constructor arguments(Bean构造函数依赖注入的属性)
2.4 properties(Bean依赖注入的属性, 这里的 properties 主要还是指XML或properties里面描述的信息)
2.5 autowiring mode(是否自动装配, 若是的话, 则容器会在整个容器中寻找所有符合条件的 Field)
2.6 lazy-initialization mode (是否 lazy 生成Bean, 指是否在正真需要使用 Bean时才会创建bean, 但现在运用中一般都是使用 ApplicationContext 的子类, 这个类会在 refresh() 方法里面中调用 finishBeanFactoryInitialization(), 然后再调用 preInstantiateSingletons 来实例化容器中的所有类), 2.7 initialization method (Spring 容器中 bean 的初始化方法, 这个完全可以通过实现接口 InitializingBean 来实现)
2.8 destruction method (Spring 容器中 bean 的销毁方法, 这个也完全可以通过实现接口 DisposableBean 来实现)

PS: 这个有个漏掉的点: 在Spring 创建 Bean的过程中, 容器里面的 BeanFactoryPostProcessor 可以对Bean的元数据进行一定的修改
3. IOC 主要组件 BeanDefinition 的分类

BeanDefinition 其实是根据 BeanDefinition 的收集策略来进行分类的, 主要分成下面:

1. GenericBeanDefinition:            一个标准的 BeanDefinition, 通过XML 读取Bean的描述信息组装的BeanDefinition一般都是GenericBeanDefinition, 后续的 beanDefinition 都是在这个类的基础上进行扩展
2. ScanedGenericBeanDefinition:      这个类扩展于 GenericBeanDefinition, 底层是通过 ASM 的ClassReader 来获取对应注解 Annotation  相关的元数据
3. AnnotatedGenericBeanDefinition:   这个 BeanDefinition 与上面的ScanedGenericBeanDefinition 很相似, 都具有 bean上注解的描述信息, AnnotatedGenericBeanDefinition 这个类主要是在使用 AnnotationConfigApplicationContext创建容器时, 通过 AnnotationBeanDefinitionReader 来获取对应 BeanDefinition 时创建的
4. IOC 主要组件 BeanDefinitionReader 概述

先看一下接口BeanDefinitionReader 的主要方法


// beanDefinition 的读取者
// 这里有个注意点, BeanDefinition 的读取器不一定非要实现BeanDefinitionReader接口, 其实这里指的就是 AnnotatedBeanDefinitionReader 与 ClassPathBeanDefinitionScanner
public interface BeanDefinitionReader {

    // 这里的 BeanDefinitionRegistry 其实就是 beanFactory 的注册接口, BeanDefinition 通过这个接口注入到 BeanFactory 看一下 BeanDefinitionRegistry 的实现类就知道了
    BeanDefinitionRegistry getRegistry();

    // ResourceLoader 代表的是 BeanDefinitionReader 需要读取的是 描述BeanDefinition的资源文件加载器, 以前 BeanFactory 与 ResourceLoader 是分离开的两个组件, 后来出现了 ApplicationContext, 将 beanFactory 与 ResourceLoader 结合到一个类中
    ResourceLoader getResourceLoader();

    // 加载类的 ClassLoader
    ClassLoader getBeanClassLoader();

    // bean 的命名生成器
    BeanNameGenerator getBeanNameGenerator();

    // 加载指定目录Resource下 BeanDefinition 的方法
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

    // 加载多个指定目录Resource下 BeanDefinition 的方法
    int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

    // 加载指定目录Resource下 BeanDefinition 的方法
    int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

    // 加载多个指定目录Resource下 BeanDefinition 的方法
    int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}

上面方法定义了获取 BeanDefinition的主要生成策略, 但其中有个重要的点, 就是 BeanDefinition 的读取器不一定非要实现 BeanDefinitionReader 接口, 比如 AnnotatedBeanDefinitionReader(直接注入带注解的bean) 与 ClassPathBeanDefinitionScanner(扫描指定目录下面带注解的Bean)

5. IOC 主要组件 BeanDefinitionReader 分类

BeanDefinitionReader 首先是通过一个抽象类 AbstractBeanDefinitionReader 来定义了加载 beanDefinition 的模板方法(模板模式), 接下来根据在不同文件(XML/Properties)中定义Bean描述信息来实现不同类别的 BeanDefinitionReader(策略模式)

1. AbstractBeanDefinitionReader:         实现 beanDefinitionReader 接口的抽象类, 主要定义了加载类的主逻辑, 后续的类依据不同存储文件不同格式, 有不同的具体实现类 
2. PropertiesBeanDefinitionReader:       读取 properties 文件的 beanDefinitionReader, 这个类相对简单, 但是没有下面的 XMLBeanDefinitionReader 扩展性那么好
3. GroovyBeanDefinitionReader:           这个类是基于 XMLBeanDefinitionReader 实现的 BeanDefinitionReader
4. XmlBeanDefinitionReader:              最常见的 BeanDefinitionReader, 也是相对扩展性最好的 Reader(PS: 它的扩展性主要体现在自定义命名空间, 比如 <mvc...>, <aop...>, <tx...>, 或者dubbo扩展的 <dubbo....>)
5. AnnotatedBeanDefinitionReader:        虽然没有实现 BeanDefinitionReader 接口, 但是它其实还是个 BeanDefinitionReader, 主要是当使用基于注解驱动的 AnnotationConfigApplicationContext 时使用的
6. ClassPathBeanBeanDefinitionScanner:   这个类也没有实现BeanDefinitionReader , 但是它非常强大, 知道指定一个扫描的目录, 就会将符合条件的Bean生成 ScanedGenericBeanDefinition 对象, 注入到容器中(PS: 当Spring的XML文件中定义了 <context:component-scan> 也会创建这个类, 并且通过这个类扫描指定目录项目的类, 并registry 到 BeanFactory 中)

上面额几种 beanDefinitionReader中, XmlBeanDefinitionReader, AnnotatedBeanDefinitionReader, ClassPathBeanBeanDefinitionScanner 是比较重要的, 也是最常见的, 后续篇章会专门分析

6. IOC 主要组件 Resource 概述

Resource: Spring 里面对资源进行封装的统一抽象接口, 里面定义了一些通用的方法, 详情如下:

// Spring 中资源的统一抽象接口
public interface Resource extends InputStreamSource {

    // 返回的资源是否存在
    boolean exists();

    // 返回的资源是否可读
    boolean isReadable();

    // 返回这个资源是否有已打开流的处理, 若果是 true, 则此 InputStream 就不能被多次读取, 而且只能被读取和关闭以免资源泄露
    boolean isOpen();

    // 转换 Resource 到 URL
    URL getURL() throws IOException;

    // 转换 Resource 到File
    File getFile() throws IOException;

    // 返回资源内容的长度
    long contentLength() throws IOException;

    // 资源上次修改时间
    long lastModified() throws IOException;

    // 创建资源相对于这个资源
    Resource createRelative(String relativePath) throws IOException;

    // 对的这个资源的名字
    String getFilename();

    // 返回资源的描述, 用于错误处理中打印的信息
    String getDescription();
}

上面是资源抽象的统一接口, 而针对资源获取的不同方式, 对应的有相应的子类(PS: AbstractResource 是其对应的抽闲子类, 其间定义了一些抽象的方法, 所有的子类都是从这个类派生出来)

7. IOC 主要组件 Resource 类别

在 Spring 中针对获取资源的不同渠道, 创建了相应的 Resource(主要是依据 路径的格式不同, 来创建不同类别的 Resource), 其中有我们进行开发中常用的一些类, 如下:

1. FileSystemResource: 比如使用容器 FileSystemXmlApplicationContext时, 配置的 XML 资源都会被封装成FileSystemResource 
2. ServletContextResource: 若是用 XmlWebApplicationContext 系列的 ApplicationContext 容器时, 一般都是生成这个(PS: 这时是以 "/" 开头)
3. ClassPathResource: 当使用 ClassPathXmlApplicationContext 时, 最终将资源包装成 ClassPathResource(则对应的资源将通过 ClassLoader 进行加载)(PS: 这时是以 "/" 或"classpath:"开头, 若不是的话, 则封装成 URLResource)

PS: 在上面创建 Resource时, 若遇到 Classpath*: 开头时, 则将通过 PathMatchingResourcePatternResolver 来进行获取对应资源, 这时又会判断是否路径中含有 "*/?" 这种字符串, 则获取能正则匹配成功的所有文件; 
    补充: classpath*: 代表的是获取当前ClassLoader对应的 URLClassPath下面的所有包中匹配的资源(包括父 ClassLoader, 详情见 PathMatchingResourcePatternResolver.getResources(String locationPattern) ); 而 classpath 则是通过Class.getResourceAsStream(String path) 来获取一个数据流, 对滴就一个文件(详情见 ClassPathResource.getInputStream())
8. IOC 主要组件 ResourceLoader 概述

ResourcceLoader 中定义了获取Spring中统一资源的方法, 它将根据路径的前缀来生成对应的 Resource, 其中最通用的就是下面这几种:

1. DefaultResourceLoader: 这个类是 AbstractApplicationContext 的超类, 其间定义了获取 Resourcce的默认方法 getResource(String location); 若路径以"/"开头, 则交由子类创建对应的 Resource(根据不同的子类类别创建不同的 Resource, 如 FileSystemXmlApplicationContext的FileSystemResource, GenericWebApplicationContext 的 ServletContextResource, ClassPathXmlWebApplicationContext的ClassPathResource), 若路径以 "classpath:" 开头, 则创建 ClassPathResource, 若不是以上两种情况, 则直接创建 UrlResource
2. PathMatchingResourcePatternResolver: 这个类是在创建 AbstractApplicationContext 时默认创建的 ResourceResolver, 当遇到的路径是以 "classpath*: " 开头是就通过这个类来获取 ClassLoader的classpath下面的所有 jar 中的符合条件的XML资源(PS: PathMatchingResourcePatternResolver是一个非常重要的类, 而且它获取资源的方式非常直接借鉴)
9. IOC 主要组件 BeanDefinitionDocumentReader

这个类存在的意义主要是封装成 XML文件为 Document, 并且根据每个节点的命名空间获取对应的 Namespacehandler 来进行处理解析对应的节点; 其默认实现就是 DefaultBeanDefinitionDocumentReader; 对于普通的命名空间 "http://www.springframework.org/schema/beans", 则直接用 BeanDefinitionParserDelegate的parseBeanDefinitionElement来进行解析; 若遇到其他命名空间的, 则通过NamespaceHandlerResolver 来获取对应的 NamespaceHandler 来进行解析
(PS: NamespaceHandler 也是通过META-INF/Spring.handlers 下面定义的命名空间对应的Namespacehandler, 并通过反射来进行创建, 具体看 DefaultNamespacehandlerResolver 的gethandlerMappings )

10. IOC 主要组件 NamespaceHandler

Spring解析XML 时, 主要依据命名空间来选择对应的 NamespaceHandler 来进行解析

1. http://www.springframework.org/schema/beans 对应 默认的解析类 BeanDefinitionParserDelegate
2. 当遇到其他命名空间时, 则通过 DefaultNamespaceHandlerResolver.resolve(namespaceUri) 来获取对应的 NamespaceHandler 来进行解析(比如遇到 <mvc...>, <aop...>, <tx...>, 或者 dubbo 中的<dubbo>)

DefaultNamespaceHandlerResolver 通过获取所有 "META-INF/spring.handlers" 里面定义的 Namespacehandler, 并找到namespace与之对应的进行解析

11. IOC 主要组件 BeanFactoryPostProcessor

BeanFactoryPostProcessor: 对这个名词进行分割; BeanFactoryPostProcessor = BeanFactory(bean工厂) + Post(后) + Processor(处理器); 这样就好理解了!

public interface BeanFactoryPostProcessor {
        // 这个方法在 BeanFactory 加载好所有的 Bean 信息到容器后, 实例化任何Bean实例之前(Bean在容器中有实例化(Instantiation)与初始化(Initialization)的差别, 这点很重要)
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

对于这个类, 最常见的就是用于修改 Bean 的属性, 比如一开始在对应属性上面设置 "${}" 设置这样的占位符, 然后通过对应类进行赋值


BeanFactoryPostProcessor.jpg

如图:

BeanFactoryPostProcessor:      定义了方法执行的入口(在加载所有Bean信息到容器之后)
PropertiesLoaderSupport:       Properties加载/解析的方式方法
PropertyResourceConfigurer:    继承PropertiesLoaderSupport, 实现BeanFactoryPostProcessor接口, 其中定义执行方法的主逻辑, 并留下模板方法 processorProperties(), 留给子类去实现
PropertyOverrideConfigurer:    这个类主要在 properties 文件里面写入 beanName.fieldname=fieldvalue 这样的键值对来进行解析数据
PropertySourcesPlaceholderConfigurer/PropertyPlaceholderConfigurer: 这两个类都是继承PropertyResourceConfigurer, 但与PropertyOverrideConfigurer不同的是, 这两个类都是通过"${}" 这种占位符号来确定哪个数据需要替换成 Properties 里面的数据, 其中还涉及到了非常有效的工具类 PlaceholderResolver(这个类是来获取对应数据中所有的 ${} 中的数据, 其中涉及到递归获取, 这种获取的方式其实很值得借鉴), 当然还有我们熟悉的设计模式 --> 观察者模式, 通过 BeanDefinition 的观察者 BeanDefinitionVisitor 来进行 visitor 每一个 BeanDefinition中的属性(见BeanDefinitionVisitor)
12. IOC 主要组件 BeanPostProcessor

BeanPostProcessor 接口是Spring IOC 中扩展机制的提现, 自己可以在BeanPostProcessor的回调方法中实现 "实例化逻辑, 依赖分析"等;
它具有以下特定:

1. 可以在容器中注入多个 BeanPostProcessor, 并且通过 Ordered接口来控制 BeanPostProcessor实现的次序;
2. BeanPostProcessor 的 postProcessbeforeInitialization方法作用在 bean 的初始化方法(PS: 这里的初始化方法主要是通过xml中配置的, 或者实现 InitializingBean接口而具有的 afterPropertiesSet)前面调用, 作用在 Bean 实例化, 并且设置好 XML 中配置的依赖熟悉之后调用(PS: 其中包括InstantiationAwareBeanPostProcessr的postProcessPropertyValues 之后 ---> postProcessPropertyValues 主要是将 bean中被 @Autowired,@Resource, @PersistenceContext, @Required 注解修饰属性注入对应的值);而在Spring容器自己提供的几个 BeanPostProcesser的实现类中, 可以看到在 ApplicationContextAwareProcessor 中完成了一下 Aware 接口的唤醒
3. BeanPostProcessor 的 postProcessAfterInitialization方法主要作用在 Bean的初始化方法之后执行(PS: 这时的bean已经实例化,并且进行类依赖注入+Aware接口的唤醒), 对于这个接口, 最常见的就是通过这个接口实现对 Bean 的 AOP(PS: 其中涉及到一个 IOC 容器中配置多个 AbstractAutoProxyCreator 的情况, 而其内部通过advisedBeans来判断 Bean 是否已经被 动态代理了); 在自动AOP中, 主要有基于命名空间的 AspectJAwareAdvisorAutoProxyCreator 与 基于 @AspectJ注解风格的 AnnotationAwareAspectJAutoProxyCreator (PS: 针对这两个类, 在介绍AOP中会详细解析)
4. InstantiationAwareBeanPostProcessor 的 postProcessBeforeInstantiation: Spring中 bean 的实例化前置处理器, 当系统中配置 customTargetSourceCreators 时, 将通过特定的 TargetSource 来对 Bean 进行动态代理, 若类已经被动态代理了, 则相应的也会执行 BeanPostProcessor.postProcessAfterInitialization 方法(PS: postProcessAfterInitialization 里面可能有些其他的逻辑需要执行)
5. InstantiationAwareBeanPostProcessor 的 postProcessAfterInstantion: bean 实例的后置方法, 其主要是控制是否需要将 xml 中配置的 Spring 属性进行注入 + InstantiationAwareBeanPostProcessor.postProcessPropertyValue(PS: postProcessPropertyValue这个方法主要是注入被注解修饰的属性)是否执行, 一般情况就是返回 true
6. InstantiationAwareBeanPostProcessor 的 postProcessProperty: 这是一个非常重要的方法, 主要完成Bean中被注解注释的属性的注入(比如: AutowiredAnnotationBeanPostProcessor 处理 @Autowired, @Value; RequiredAnnotationBeanPostProcessor处理 @Resource; PersistenceAnnotationBeanPostProcessor处理@PersistenceContext等注解)(PS: SpringMVC 的@RequestMapping 与 事务的 @Transactional 又是不同的处理逻辑)
7. SmartInstantiationAwareBeanPostProcessor 的 predictBeanType: 预测Class的类型, 当系统中发现无法确定Bean类型, 并且存在SmartInstantiationAwareBeanPostProcessor, 就会通过这个方法来获取对应的class类型(PS: 这个方法主要的实现在 AbstractAutoProxyCreator里面)
8. SmartInstantiationAwareBeanPostProcessor 的 determineCandidateConstructors: 预测类的构造函数, 之所以有这个方法, 主要存在 @Lookup 注解(PS: 这个注解很少使用), 这个determineCandidateConstructors方法在实例化 Bean的过程中会被调用具体看 AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors 方法
9. SmartInstantiationAwareBeanPostProcesso r的 getEarlyBeanReference: 预测Bean实例, 在 AbstractAutoProxyCreator中这个方法与postProcessAfterInitialization中只有一个会执行 Bean 的代理过程(PS: 这是为什么呢, 看看 earlyProxyReferences就知道了, 两个方法在执行 wrapIfNecessary 都会通过 earlyProxyReferences 判断是否已经创建好 AOP 对象); 对于这个getEarlyBeanReference 方法是在 Bean 被提早暴露时才会调用
举例: 
      两个类:  class A { B b;}; class B { A a; }; 都注入到 Spring 容器, 
      1. XmlBeanDefinitionReader 将描述 A,B两个类的xml 文件读入, 并解析成 BeanDefinition, 然后注入到 DefaultListableBeanFactory中;
      2. DefaultListableBeanFactory 通过 getBean 方法开始实例化这两个类, 在创建 A 的时, 发现A是单例, 其容器允许循环引用, 并且 A 不在 map -> singletonsCurrentlyInCreation(表示类是否在创建) 中, 则将 A 包装成 ObjectFactory 类, 放入单例工厂中, 已备被其他创建的类引用, 而 ObjectFactory 中是通过 AbstractAutowireCapableBeanFactory.getEarlyBeanReference --> SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference --> AbstractAutoProxyCreator.getEarlyBeanReference 来暴露创建的动态代理对象
      3. 而在实例化 A 后, 发现 A 依赖于 B(这里的依赖信息可能存在于xml 或通过 @Autowired/@Resource 注解), 则开始创建类 B
      4. 同样在创建类 B 时, 发现 B 依赖于 A, 所以直接通过 earlySingletonObjects 来获取上面提早暴露的 ObjectFactory, 接着会 AbstractAutowireCapableBeanFactory.getEarlyBeanReference --> SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference --> AbstractAutoProxyCreator.getEarlyBeanReference 来得到最终暴露出来的 A 对象

BeanPostProcessor 的分类, 各个子类的作用, 包括 BeanFactory 中如何解决循环引用(只有scope是单例, 并且依赖不是存在在构造函数中才能解决循环引用) 进行了介绍, 后续会对个别 BeanPostProcessor 详细的解析

13. IOC 主要组件 FactoryBean

FactoryBean: Spring容器中 某类特定Bean的工厂类(这里其实就涉及到了抽象工厂模式), FactoryBean 充斥着整个IOC容器, 一开始接触 FactoryBean的同学可能不深刻理解这个类的好处, 其实看一下 ProxyFactoryBean(生成动态代理的 FactoryBean), ScheduledExecutorFactoryBean(生成定时线程执行器的 FactoryBean), TransactionProxyFactoryBean(基于目标 Target 生成通过切面控制事务的动态代理类), JobDetailFactoryBean(生成基于 org.quartz.JobDetail 的实例), 就能知道 FactoryBean 主要其实是对于一些创建过程复杂的类进行通过操作的类,

14. IOC 主要组件 BeanFactory

BeanFactory: 是Spring IOC 容器的顶层接口, 其中定义了获取Bean的一般接口, 如下:

// Spring 的 Bean 容器最顶层的接口, 定义了 Ioc 容器的基本规范
public interface BeanFactory {

    // 通过在BeanName名字前面加 & 来获取生成对象的 factory
    String FACTORY_BEAN_PREFIX = "&";

    // 通过 Bean 的名字, 获取对应的实例
    Object getBean(String name) throws BeansException;

    // 获取指定名称的 Bean, 其中 requiredType 指的是 Bean的类型
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    // 获取指定名称的 Bean
    <T> T getBean(Class<T> requiredType) throws BeansException;

    // 获取指定名称的 Bean, 其中 args 指的是 构造函数/工厂方法的参数
    Object getBean(String name, Object... args) throws BeansException;

    // 获取指定类型的 Bean, 其中 args 指的是 构造函数/工厂方法的参数
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    // 判断容器是否含有 Bean
    boolean containsBean(String name);

    // 这个名称对应的 Bean 是否是 singleton 类型
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    // 这个名称对应的 Bean 是否是 prototype 类型
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    // 判断指定名称对应的 Bean 是否与 typeToMatch 相匹配(这个方法使用得非常多)
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    // 判断指定名称对应的 Bean 是否与 typeToMatch 相匹配(这个方法使用得非常多)
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    // 获取指定 Name 的 bean 类型(PS: 一个特例, 若遇到 FactoryBean, 则会通过 FactoryBean.getObjectType 来获取最终对象的类型)
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    // 获得 Bean 的别名
    String[] getAliases(String name);
}

上面接口主要定义了获取 Bean 的接口, 至于 BeanFactory 的继承(HierachicalBeanFactory), 自动装配(AutowireCapableBeanFactory), 批量获取 bean(ListableBeanFactory) 这些特性都是由子接口进行定义, 如下图:


BeanFactory.jpg

乍一看, 图中类/接口好复杂, 没事, 我们一层一层梳理:

接口:
   第一层: BeanFactory:                         这个接口里面定义获取 Bean 的基础方法
   第二层: HierarchicalBeanFactory:             这个接口定义了获取 parentBeanFactory 的方法(PS: 这里其实也存在着委派机制, 当子BeanFactory 获取不到bean时, 会通过父BeanFactory来进行获取, 但是反过来, 父BeanFactory若获取不到Bean, 则不会再到子BeanFactory中获取, 这种父子BeanFactory形式通常在于: 在 Tomcat的web.xml里面配置 ContextLoaderListener.contextInitialized --> ContextLoader.initWebApplicationContext --> 通过反射直接创建 XmlWebApplicationContext  <-- 这个是 parentBeanFactory, 与之对应的 子BeanFactory 是 由 HttpServletBean.init() --> FrameworkServlet.initServletBean --> FrameworkServlet.initWebApplicationContext --> FrameworkServlet.createWebApplicationContext(这时会将上面的父beanFactory设置到子BeanFactory中)) 
           ListableBeanFactory:                (基于类型)获取容器中所有 BeanNames, 获取被注解注释的 BeanName 等方法
           AutowireCapableBeanFactory:         这里面定义了, 创建Bean, 自动装配 Bean, 初始化 Bean, 为特定 Bean 解决对应依赖属性的方法
   第三层: ConfigurableBeanFactory:             增加配置Bean功能的BeanFactory, 这个接口, 继承 SingletonBeanFactory, 具备了注册单例bean的接口, 除此之外 其中还定义了配置 ConversionServere(Spring里面的类型转换器, Spring 默认的是 DefaultConversionService, 主要是完成数据类型的转换器的注册); 配置 BeanPostProcessor(Bean后置处理器)的方法, 销毁 Bean的方法
           ConfigurableListableBeanFactory:    这个接口里面定义了 preInstantiateSingletons (预实例化Bean的方法), 获取指定名称的 BeanDefinition 的方法等
       (PS: 这个第三层接口主要是针对第二层接口的功能延伸, 其中 ConfigurableBeanFactory 具备了 注册单例Bean的功能)
实现类:
           AbstractBeanFactory:                这个类继承了 FactoryBeanRegistrySupport 具备了注册单例+支持FactoryBean的能力, 本身也实现了创建 Bean 的主逻辑(getBean 方法), 并留下了 createBean, getBeanDefinition, containsBeanDefinition 等模板方法
           AbstractAutowireCapableBeanFactory: 这个类继承了 AbstractBeanFactory 并实现了模板方法 createBean, 并实现了接口 AutowireCapableBeanFactory, 完成了对 BeanPostProcessor 的处理
           DefaultListableBeanFactory:         这个类是 Spring IOC 里面功能最全的 Bean 容器, 我们常用的 ApplicationContext 都是基本都是在内部有个 DefaultListableBeanFactory(基于 ApplicationContext 继承 BeanFactory, 所以你也可以将这种程序设计方式视为 代理方式)
PS:        一开始看 Spring IOC 时被 BeanFactory 的继承接口给吓住了(哇 这么多的接口继承关系, 层级又是这么多), 结果才发现, 这么多接口中只有第一层与第二层接口之间有明显的扩展, 而下面的接口, 都是逐渐逐渐扩充对应的方法

上面是 BeanFactory 的接口设计及实现, 接口层面主要是3级, 都是对容器功能一点一点的扩充, 而其中 AbstractBeanFactory 设计了创建类的主逻辑(getBean方法), 并且预留了创建类的模板方法(createBean), 而最终的实现类就是 DefaultListableBeanFactory, 工作中常用的:

1. XmlBeanFactory(基于 XmlBeanDefinitionReader读取xml配置的Bean依赖关系)
2. XmlWebApplicationContext(用于web容器中的BeanFactory)
3. ClassPathXmlApplicationContext(通过在 classpath 下面查找指定文件的 BeanFactory)
4. FileSystemXmlApplicationContext(直接通过文件相对路基获取xml配置文件的 BeanFactory)
5. AnnotationConfigWebApplicationContext (通过扫描指定目录下面的 class 文件, 并将被特定注解修饰的额类加载到容器中的 BeanFactory)

都是或多或少的用到 DefaultListableBeanFactory(要是继承, 要么就是将DefaultListableBeanFactory设置为其内部的一个变量)

15. IOC 总结

上面简单的介绍了一下 Spring IOC 里面的主要组件, 其中涉及到的组件还是比较多的, 一开始进行看源码时可能被浩瀚的代码给淹没, 从而恐惧, 接着就放弃了(相信很多人都会经历过这几个过程), 当然我也经历过, 当然直到现在自己对 Spring 里面有些细节的东西还不是非常了解, 但其实没关系, 个人觉得只要把握主要的设计思想以及常见的架构设计就可以了!(毕竟全部弄精还是非常耗时的一个工程)

16. 参考资料

Spring技术内幕分析
Spring IOC 原理
Spring 5 源码分析
开涛的 Spring杂谈
伤神的 Spring源码分析
SpringMVC源码分析系列
Spring Core
Spring MVC源码剖析
Spring IOC 源码分析
Spring源码情操陶冶
Spring 揭秘 (PS: 这本书绝对给力)
Spring 技术内幕
Spring 源码深度分析
看透 Spring MVC 源代码分析与实践
Spring 高级程序设计 (PS:这本书已经绝版, 所以当时是自己下载 pdf, 然后联系淘宝卖家忙帮复印, 其实就是英文版本的 "Pro Spring 3")
深入分析 Java Web(PS: 许令波写的, 虽然是本老书, 但是里面分析得确实很深入)
expert one-on-one J2EE Development without EJB (PS: Spring 作者自己写的书, 当时也是下载 PDF, 联系淘宝卖家复印购买到的)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容