Spring Ioc 精简版

抽象

容器的核心抽象类:

  1. BeanFactory:Spring Ioc的核心,即是BeanFactory,它即是我们常说的Spring容器,它负责Bean实例的创建于管理
  2. ApplicationContext:它是BeanFactory子接口,但是添加了更多的企业级功能

功能类:

  1. Environment:容器的环境,默认的实现是StandardEnvironment,Environment中有一个propertySources【MutablePropertySources】字段,该字段存储的是各种属性源(与数据源DataSource类比更容易理解),当StandardEnvironment实例化时,会默认将JVM启动参数和环境变量,分别设置进propertySources【MutablePropertySources】字段中
  2. ResourceLoader:资源加载器,负责将资源加载为Resource(可以类比为Java API中的File对象),PathMatchingResourcePatternResolver是其一个实现,该实现除了可以加载资源外,提供了对路径模式匹配的能,比如我们常用的“classpath:/.xml”写法。

注:MutablePropertySources内维护着一个CopyOnWriteArrayList< PropertySource < ? > >类型的属性源列表

ClassPathXmlApplicationContext 容器启动概述

    public static void main(String[] args) {

        ClassPathXmlApplicationContext classPathXmlApplicationContext = 
        	new ClassPathXmlApplicationContext("config/Bootstrap_${env}.xml");
    }

ClassPathXmlApplicationContext 是基于类路径下的XML配置的容器,容器的启动是通过new ClassPathXmlApplicationContext(…)创建的,下面分步概述主要步骤:
注:所有的步骤都发生在ClassPathXmlApplicationContext 的构造器内

  • 1 super(parent),完成ClassPathXmlApplicationContext的父类中的初始化,其中最重要的是填充resourcePatternResolver【ResourcePatternResolver】字段属性的值为PathMatchingResourcePatternResolver对象,PathMatchingResourcePatternResolver是ResourceLoader的子类,用于将资源加载为Resource对象。
  • 2 解构构造器传入的配置文件地址,本例中即是“config/Bootstrap_${env}.xml”,因为路径允许有 ${} 占位符,故需要对其进行解析,替换掉占位符,并将解析后的配置文件报错到configLocations【String[]】字段属性中。

注:从步骤 2 之后,便都属于AbstractApplicationContext.refresh()方法的逻辑,AbstractApplicationContext是一个高度的抽象类。refresh()该方法是一个模板方法,是容器刷新的骨架,后续讲 Spring Boot 容器启动时,也会使用它,逻辑大同小异

  • 3 prepareRefresh(),容器刷新前的准备工作,设置一些Ioc容器状态标识,比如closed、active等状态字段

  • 4 obtainFreshBeanFactory(),创建DefaultListableBeanFactory,加载(load)并注册(register)BeanDefinition

    • 4.1 关闭既有的BeanFactory
    • 4.2 new实例化DefaultListableBeanFactory对象,它是默认的BeanFactory实现
    • 4.3 设置DefaultListableBeanFactory,包括是否允许环形引用
    • 4.4 loadBeanDefinitions(),加载并注册BeanDefinition
      • ① 通过XmlBeanDefinitionReader(BeanDefinitionRegistry registry)构造器实例化XmlBeanDefinitionReader,实例化时在构造器参数中传入的入参是DefaultListableBeanFactory,因为DefaultListableBeanFactory实现了BeanDefinitionRegistry 接口,XmlBeanDefinitionReader将其保存到registry【BeanDefinitionRegistry】字段中,以备注册BeanDefinition时候使用
      • ② 配置XmlBeanDefinitionReaderResourceLoaderEnvironment
      • ③ for循环,将前面解析的configLocation传给XmlBeanDefinitionReader进行解析
      • XmlBeanDefinitionReader通过自定义配置的ResourceLoader【PathMatchingResourcePatternResolver】将配置文件读取转换为Resource对象
      • XmlBeanDefinitionReader通过默认配置的DocumentLoader【DefaultDocumentLoader】,将Resource解析为Document
      • ⑥ 实例化BeanDefinitionDocumentReader,并调用它的registerBeanDefinitions(Document,XmlReaderContext)方法,进行BeanDefinition的解析和注册。其中XmlReaderContext 入参是一个小型的上下文对象,他持有对XmlBeanDefinitionReade的引用,以及对NamespaceHandlerResolver的引用。在解析Document中的自定义元素【Element】时,NamespaceHandlerResolver会派到用场;而XmlBeanDefinitionReade则持有对BeanDefinitionRegistry的引用,注册BeanDefinition时候会派到用场!!
        • Ⅰ如果Document中的子元素【Element】属于默认的命名空间(即是Beans命名空间中的元素)中的< bean >元素,那么将该元素【Element】委托给BeanDefinitionParserDelegate的parseBeanDefinitionElement(Element)方法来解析,解析完成后,将解析得到的BeanDefinition,注册到BeanDefinitionRegistry上
        • Ⅱ 如果Document中的子元素【Element】属于默认的命名空间(即是Beans命名空间中的元素)中的< import resource=“***” >元素,那么重复上面的所有步骤:解析resource配置文件路径、XmlBeanDefinitionReader读取配置文件并解析为Resource和Document、BeanDefinitionDocumentReader解析Document并注册BeanDefinition
        • Ⅲ 如果Document中的子元素【Element】属于默认的命名空间(即是Beans命名空间中的元素)中的< alias >元素,提取属性值,并通过BeanDefinitionRegistry的registerAlias(beanName, alias)注册上去。
        • Ⅳ 如果Document中的子元素【Element】或者根节点不属于默认的命名空间,那么将该元素【Element】委托给BeanDefinitionParserDelegate的parseCustomElement(Element)方法来全权解析。
    • 4.5 将实例化的DefaultListableBeanFactory设置进ApplicationContextbeanFactory字段中,虽然ApplicationContext继承了BeanFactory,但却是用组合的方式来实现的
  • 5 prepareBeanFactory(ConfigurableListableBeanFactory):我们说过ApplicationContext添加了许多企业级功能,前面已经完成了BeanFactory的创建和配置文件的解析与注册,而ApplicationContext的拓展也由此展开。

    • 5.1 添加EL表达式解析器
    • 5.2 添加属性编辑器
    • 5.3 设置几个忽略自动装配的接口,通俗的讲,就是如果如果某个类实现了这些接口,我们可以忽略对其感兴趣感知的资源的装配,比如我们实现了ApplicationContextAware,就是为了可以感知到ApplicationContext对象,在自动装配时候不需要对其装配,Spring通过BeanPostProcessor机制来解决了这几个特殊接口的装配,见5.4步骤。
      • EnvironmentAware
      • EmbeddedValueResolverAware
      • ResourceLoaderAware
      • ApplicationEventPublisherAware
      • MessageSourceAware
      • ApplicationContextAware
    • 5.4 beanFactory.addBeanPostProcessor() 注册ApplicationContextAwareProcessor和ApplicationListenerDetector
      • ApplicationContextAwareProcessor:实现了BeanPostProcessor,在init-method之后,会调用它的postProcessBeforeInitialization(…)方法,该方法负责为Bean填充一些特殊资源,因此实现了上述Aware接口的Bean在被初始化后,即可以获得自己感兴趣的资源。
      • ApplicationListenerDetector:该BeanPostProcessor会监测实现了ApplicationListener的Bean,它会通过applicationContext.addApplicationListener方法将这些Bean加入到ApplicationContext的多播器【ApplicationEventMulticaster】的订阅者队列上,由此,实现ApplicationListener的Bean就可以接收到消息了~~
  • 6 postProcessBeanFactory(ConfigurableListableBeanFactory),此时所有beanDefjnition都已加载,但还没有实例化任何bean。该方法默认无代码,具体的逻辑交给特定的ApplicationContext实现类去完成。

  • 7 invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory),调用上下文中注册为的BeanFactoryPostProcessor。因为此时还没有开始实例化任何Bean,Spring允许BeanFactoryPostProcessor在容器实例化任何Bean之前读取配置元数据,并且可以修改它。这也是为什么Spring可以根据< context:property-placeholder location=“xxxx.properties”/>中的配置信息,替换掉BeanDefinition中的 ${xxx} 模样的值。

  • 8 registerBeanPostProcessors(ConfigurableListableBeanFactory),注册BeanPostProcessor,这里只是注册

  • 9 初始化国际化,即i18n

  • 10 初始化Application中的事件多播器,即applicationEventMulticaster【ApplicationEventMulticaster】

  • 11 onRefresh(),该方法默认无代码,但是在启动Web类型的容器时,很重要,Servlet的启动就是在该方法中实现的

  • 12 finishBeanFactoryInitialization(ConfigurableListableBeanFactory),完成这个上下文的BeanFactory的初始化,其中包括对ConversionService的设置、BeanDefinition冻结以及实例化所有非延迟的单例Bean,因为Bean实例化涉及的太多,会单独总结!

    • ConversionService:它是类型转换的服务接口,我们可以实现ConversionService,并将实现类注册为Bean,Spring会在此时对其实例化,并通过beanFactory.setConversionService(ConversionService)注册上去
    • BeanDefinition冻结:通过设置状态标志位,暗示BeanDefnition被冻结,不会再被修改了。
    • 实例化所有非延迟的单例Bean:ApplicationContext实现的默认行为就是在启动时将所有单例且非延迟的Bean进行实例化
  • 12 finishRefresh(),通过调用LifecycleProcessor的onRefresh()和发布ContextRefreshedEvent事件,完成对ApplicationContext的刷新工作。

    • 12.1 initLifecycleProcessor():初始化LifecycleProcessor,它就是生命周期处理器。Spring提供了一个Lifecycle接口,所有实现它的类,都需要实现start()和stop()方法,Spring会保证在启动时候调用其start()方法来开始生命周期,在Spring关闭时调用stop()方法,来结束生命周期。
    • 12.2 getLifecycleProcessor().onRefresh():通知LifecycleProcessor,启动所有实现了Lifecycle接口的Bean!!
    • 12.3 publishEvent(new ContextRefreshedEvent(this)),发布一个事件,感兴趣的ApplicationListner可以异步消费处理。

注:BeanDefinitionRegistry继承自AliasRegistry,因此他除了可以注册BeanDefinition之外,还可以注册Alias与BeanName之间的映射关系。

< Bean >元素的解析

默认元素< bean >的解析分三步:

  1. 元素解析,即由Element解析为BeanDefinition,该步骤代码量很大,但是工作很单一,提取元素中的信息,并设置进BeanDefinition中,最终完成对Element的解析
  2. 元素装饰,主要是针对于< bean > 中出现的自定义内嵌标签或属性,对BeanDefinition进行装饰~
  3. BeanDefinition注册,将最终的BeanDefinition注册到BeanDefinitionRegistry上
自定义元素的解析
  1. 根据Element,解析出其命名空间地址
  2. 通过NamespaceHandlerResolver的resolve(namespaceUri)方法,根据命名空间地址,解析出该命名空间地址所对应的NamespaceHandler
  3. 通过NamespaceHandler,对Element进行解析

NamespaceHandlerResolver是如何根据命名空间地址,解析出其对应的NamespaceHander的呢?

我们来看Aop命名空间对应的AopNamespaceHandler是如何注册的:

public abstract class NamespaceHandlerSupport implements NamespaceHandler {
	// 保存 Label -> BeanDefinitionParser的映射,用于自定义元素的解析
	private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
	// 报存 Label -> BeanDefinitionDecorator的映射
	// 	decorators用于自定义元素的装饰,比如scoped-proxy
	// 	attributeDecorators用于自定义属性的装饰
	private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();
	private final Map<String, BeanDefinitionDecorator> attributeDecorators = new HashMap<>();
}
  • 0 创建一个xsd文件,其中有该命名空中元素的约束信息,详Google,
  • 1 创建AopNamespaceHandler类,并继承自NamespaceHandlerSupport
  • 2 覆写【override】NamespaceHandler接口中的void init()方法,我们可以在该方法中,完成对各种BeanDefinitionParser、BeanDefinitionDecorator的注册。
    • BeanDefinitionParser:完成对自定义元素的解析工作,基本的工作是负责将自定义的Element转化为任意数量的BeanDefinition,但是很多框架还会在其中安插更复杂的功能
    • BeanDefinitionDecorator:主要是用于处理内嵌于 < bean > 标签中的自定义的标签或者属性
  • 3 METE-INF文件夹下创建名为spring.handlers文件,文件按行分隔
## 命名空间地址=自定义NamespaceHandler的全限定类名
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
  • 4 METE-INF文件夹下创建名为spring.schemas文件,文件按行分隔
## xsd公网地址=xsd文件目录路径
https\://www.springframework.org/schema/aop/spring-aop.xsd=org/springframework/aop/config/spring-aop.xsd

Spring的NamesapceHandlerResolver其实也是扫描所有META-INF下的spring.handlers文件,并将其中的配置提取出来,入缓存。这样当给定命名空间地址时,别可以快速的返回其对应的NamespaceHandler了~~

并且,解析Element的工作也并不是由NamespaceHandler来做的,当我们调用parse(Element element, ParserContext parserContext)方法进行元素解析时,它底层通过NamespaceHandlerSupport 中的缓存Map,根据Element的Tag,找到适合的BeanDefinitionParser来对其进行解析。

Spring Boot 容器启动概述

  • 1 createApplicationContext():创建SpplicationmContext

    • 根据WebApplicationType这个Enum的值,选取对应类型的ApplicationContext实现
    • case:SERVLET : 实例化AnnotationConfigServletWebServerApplicationContext类型的上下文对象
    • case REACTIVE:实例化AnnotationConfigReactiveWebServerApplicationContext类型的上下文对象
    • default:实例化AnnotationConfigApplicationContext类型的上下文对象
  • 2 prepareContext(…):加载Bean

    • 2.1 getAllSources(),返回所有的配置源,配置源包括三种:
      • Class类型,多为启动类的Class独享,比如SpringBoot的启动类叫SpringEnjoyApplication,那么就是该类的Class实例
      • Resource类型,为XML配置文件的Resource表现
      • Package类型,多为需要扫描的包名
      • CharSequence类型:多为XML配置文件的路径地址,解析方式与ClassPathXmlApplicationContext中配置文件的解析类似
    • 2.2 load(ConfigurableApplicationContext context,Object[] sources):加载所有的BeanDefinition,该方法很重要
      • ⑴ 创建一个BeanDefinitionLoader,并将上面获得的Source数组传递进来,该类负责从底层数据源(包括XML和JavaConfig)中加载bean定义。该类充当XmlBeanDefinitionReader、AnnotatedBeanDefinitionReader还有ClassPathBeanDefinitionScanner的Facade类
      • ⑵ BeanDefinitionLoader.load(),执行BeanDefinitoin加载,根据传入的source,选择不同的BeanDefinitionReader进行BeanDefinition的加载
  • 3 refreshContext(context):

    • refresh():与ClassPathXmlApplicationContext调用refresh()方法一毛一样,但是因为上下文对象类型的不同,部分方法的实现可能不一致
  • 4 afterRefresh(context, applicationArguments):默认无实现

Starter自动化配置原理
@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE})
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Documented
@java.lang.annotation.Inherited
@org.springframework.boot.SpringBootConfiguration
@org.springframework.boot.autoconfigure.EnableAutoConfiguration
@org.springframework.context.annotation.ComponentScan(excludeFilters = {@org.springframework.context.annotation.ComponentScan.Filter(type = org.springframework.context.annotation.FilterType.CUSTOM, classes = {org.springframework.boot.context.TypeExcludeFilter.class}), @org.springframework.context.annotation.ComponentScan.Filter(type = org.springframework.context.annotation.FilterType.CUSTOM, classes = {org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter.class})})
public @interface SpringBootApplication {
    @org.springframework.core.annotation.AliasFor(annotation = org.springframework.boot.autoconfigure.EnableAutoConfiguration.class)
    java.lang.Class<?>[] exclude() default {};

    @org.springframework.core.annotation.AliasFor(annotation = org.springframework.boot.autoconfigure.EnableAutoConfiguration.class)
    java.lang.String[] excludeName() default {};

    @org.springframework.core.annotation.AliasFor(annotation = org.springframework.context.annotation.ComponentScan.class, attribute = "basePackages")
    java.lang.String[] scanBasePackages() default {};

    @org.springframework.core.annotation.AliasFor(annotation = org.springframework.context.annotation.ComponentScan.class, attribute = "basePackageClasses")
    java.lang.Class<?>[] scanBasePackageClasses() default {};
}

自动化配置的根源,即是@EnableAutoConfiguration注解

当Spring中开启了注解以及component-scan,那么会注册一个很重要的BeanFactoryPostProcessor:

  • ConfigurationClassPostProcessor

当BeanFactory加载并注册完BeanDefinition后,便会调用所有的BeanFactoryPostprocessor,BeanFactoryPostprocessor共有两个比较重要的接口:

  • BeanFactoryPostProcessor,声明了void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法
  • BeanDefinitionRegistryPostProcessor,它是BeanFactoryPostProcessor的子接口,额外声明了postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法

ConfigurationClassPostProcessor是一个BeanDefinitionRegistryPostProcessor,Spring会先执行postProcessBeanDefinitionRegistry(…),然后执行postProcessBeanFactory(…)方法。

主要看postProcessBeanDefinitionRegistry(…),该方法根据注册表中的由@Configuration注解标准的BeanDefinition,进而派生出更多的BeanDefinition。

Bean加载

Bean的加载主要是通过BeanFactory的getBean(Name)来展开的,核心的步骤总结如下:

  • 1 转换Name为BeanName
    • 如果name以&开头,那么递归移除&
    • 如果name是一个别名【Alias】,则将其转换为映射的BeanName
  • 2 getSingleton(BeanName,allowEarlyReference),查看缓存中是否已有该BeanName对应的实例,该方法在处理环形引用时特别有用
    • 2.1 判断singletonObjects【ConcurrentHashMap】缓存中是否有给定的BeanName对应的Bean实例,该缓存只存储单例对象,如果存在Bean实例,直接返回
    • 2.2 判断singletonsCurrentlyInCreation【Set< BeanName>】缓存中有给定的BeanName,该缓存中只存储当前正处于创建中的Bean的BeanName,如果不存在,直接返回Null
    • 2.3 判断earlySingletonObjects【HashMap】缓存中是否有给定的BeanName对应的Bean实例存在,如果存在Bean实例,直接返回
    • 2.4 判断allowEarlyReference标志位,以确定是否允许环形引用,如果不允许,则直接返回Null
    • 2.4 判断singletonFactories【Map>】缓存中是否存在给定的BeanName对应的ObjectFactory,如果不存在,则知己返回Null,如果存在,则调用ObjectFactory#getObject()方法,获得单例Bean实例,然后从singletonFactories中移除该BeanName对应的这条Entry,并将BeanName与Bean实例的映射设置进earlySingletonObjects缓存中,以示该Bean实例是提前实例化的单例对象。
  • 3 getMergedLocalBeanDefinition(BeanName):当程序走到这里,那么说明给定的BeanName并没有被实例化,那么久先获取BeanName锁对应的BeanDefinition,因为Spring支持一个特别的属性,就是parent属性,预示该BeanDefinition有双亲BeanDefinition,因此Spring在这时候会先找出其双亲BeanDefinition,然后用子BeanDefinition进行覆盖Merge,最终生成一个Merged了的BeanDefinition,用于作为实例化Bean的蓝图
  • 4 isSingleton ??
    • 单例 Singleton
      • 4.1 beforeSingletonCreation(beanName):创建Bean实例之前,先将BeanName插入singletonsCurrentlyInCreation【Set< BeanName>】缓存中,表示该Bean处于创建中了

      • 4.2 createBean(BeanName,BeanDefinition,Args):Bean实例化以及依赖注入

        • ⑴ resolveBeforeInstantiation(BeanName,BeanDefinition):该方法给BeanPostProcessor一个机会,如果他们可以返回一个代理对象,那么就不需要Spring来完成实例化工作了,下面的实例化的步骤都不需要做了。而如果所有的BeanPostProcssor均不能代理该BeanDefinition,那么就继续执行Spring的实例化工作。通俗讲,这个方法更像是一个短路函数,类似于&& 、||。一般这个方法都不会返回代理对象,即使是AOP代理的,这个函数主要是针对特殊配置的Bean
        • ⑵ doCreateBean(BeanName,BeanDefinition,Args):真正的Bean实例化
          • ① createBeanInstance(BeanName,BeanDefinition,Args) 根据提供的参数和Class,查找合适的构造器,反射创建对象,注意,如果BeanDefinition中有Method Inject相关的配置(存储在BeanDefinition的methodOverride字段中),那么使用CGLib来生成其子类,并实例化;没有的话,则正常反射创建对象,不会代理。
          • ② applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName):在属性装配(autowirsed)之前,回调类型为MergedBeanDefinitionPostProcessor的后置处理器的postProcessMergedBeanDefinition()方法,该方法可以操作或修改MergedBeanDefinition,但Spring所提供的几个实现,大多是在这里预先搜寻待注入的属性元数据,并缓存之,比如:CommonAnnotationBeanPostProcessor会找出所有@Resource注解的属性,并封装为一个InjectionMetadata对象,方便自动装配属性时使用,AutowiredAnnotationBeanPostProcessor则会找出所有@Autowired/@Value/@Inject/@Lookup的属性元数据并缓存之,方便自动装配时使用
          • ③ addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)),向singletonFactories【Map>】插入一个ObjecFactory,以实现环形引用
          • ④ populateBean(BeanName,BeanDefinition,Bean) 对Bean进行属性注入
            • Ⅰ InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation(…):在装配属性执行,循环执行所有类型为InstantiationAwareBeanPostProcessor的后置处理器的postProcessAfterInstantiation(Bean,BeanName)方法,该方法的返回值是Boolean,只要有任何一个后置处理器返回False,那么就短路停止,表示该Bean不需要自动装配,否则,就对其进行属性的自动装配
            • Ⅱ InstantiationAwareBeanPostProcessor#postProcessProperties(…):循环执行所有类型为InstantiationAwareBeanPostProcessor的后置处理器的postProcessProperties(PropertyValues,Bean,BeanName)方法,以对Bean中的属性进行自动装配,比如:CommonAnnotationBeanPostProcessor会帮我们装配@Resource注解的属性,而AutowiredAnnotationBeanPostProcessor则会装配@Autowired/@Value/@Inject/@Lookup的属性。
            • Ⅲ applyPropertyValues(…):最后,如果还有未装配的属性,则最后执行一波装配,此处装配的大多都是通过< property name="" value="">形式赋值的属性。
          • ⑥ initializeBean(…) 初始化Bean,直白一点讲,就是调用Bean的init()方法,该方法比较重要哟,因为Aop的代理也是在这里做的
            • ⅠapplyBeanPostProcessorsBeforeInitialization:在初始化之前,调用BeanPostProcess的postProcessBeforeInitialization()方法,默认情况下什么也不做
            • Ⅱ invokeInitMethods:调用Bean的init方法
            • Ⅲ applyBeanPostProcessorsAfterInitialization:在初始化之后,调用BeanPostProcessor的postProcessAfterInitialization()方法,最容易面试问到的AOP就是在这里做的,对Bean进行代理,并返回代理后的Bean。
      • 4.3 afterSingletonCreation(BeanName):创建Bean实例之后,再将BeanName移出singletonsCurrentlyInCreation【Set< BeanName>】缓存中,以示该Bean创建完成

      • 4.4 addSingleton(BeanName,Bean):它将实例化后的单例对象注册到了singletonObjects【ConcurrentHashMap】、registeredSingletons【LinkedHashSet<>】这两个缓存中,并从singletonFactories、earlySingletonObjects中移除相应的记录,至此完成了Bean的加载~~

    • 原型:Prototype,与Singleton大同小异
      • 4.1 beforePrototypeCreation(BeanName):创建Bean实例之前,先在prototypesCurrentlyInCreation缓存中记录,以示正在创建
      • 4.2 createBean(BeanName,BeanDefinition,Args) 同Singlrton的创建
      • 4.3 afterPrototypeCreation(BeanName):创建Bean实例之后,再在prototypesCurrentlyInCreation缓存中清理掉记录,以示创建完成。Spring并不追踪Prototype类型的Bean实例,故也不记录,任其自生自灭
  • 5 getObjectForBeanInstance(Bean,Name,BeanName,BeanDefinition):到此,我们已经取得了BeanName对应的Bean实例,但是getBean(Name)是想获得Name对应Bean实例,因此这个方法就是来做着工作。对于FactoryBean,要么是获取工厂Bean实例本身,要么是工厂Bean所创建的对象。对于非FactoryBean,则直接返回,不做任何处理。

方法注入【Method Inject】

在大多数应用程序场景中,容器中的大多数Bean都是单例的。当一个单例Bean需要与另一个单例Bean协作,或者一个非单例Bean需要与另一个非单例Bean协作时,通常通过将一个bean定义为另一个Bean的属性来处理这种依赖性。但是当Bean的生命周期不同时,就会出现问题,比如:单Bbean A需要使用非单例(原型)Bean B。容器只创建一次单例Bean,因此只得到一次设置属性的机会。容器不能每次需要Bean B的新实例时都向bean A提供一个。

方案1: ApplicationContextAware

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

方案2:Lookup Method Injection

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }
    // 方法的签名规则:
    //  [abstract]  theMethodName(no-arguments);
    @Lookup("command") // 注解形式,value的值,是Command的BeanName
    protected abstract Command createCommand();

}
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
    
bean>

方案3:Replace Method Injection

public class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    public Command createCommand(){
    	//
    };

}

实现替换器

public class CommandReplacer implements MethodReplacer implements ApplicationContextAware{
    private ApplicationContext applicationContext;
   
    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result

        return this.applicationContext.getBean("command", Command.class);;
    }

}

xml配置

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    
    <replaced-method name="createCommand" replacer="commandReplacer">
    replaced-method>
bean>

<bean id="commandReplacer" class="a.b.c.CommandReplacer"/>

方案4 :scoped-proxy

Spring IoC容器不仅管理对象(bean)的实例化,还支持协作者(或依赖项)的连接。如果您想将(例如)一个HTTP Request-Scope的Bean注入到另一个更长的作用域的Bean中,您可以选择注入一个AOP代理来代替Request-Scope的bean。也就是说,您需要注入一个代理对象,它与作用域对象公开相同的公共接口,但也可以从相关作用域(如HTTP请求)检索实际目标对象,并将方法调用委托给实际对象。


<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    
    <bean id="userPreferences" class="com.something.UserPreferences" scope="session">
        
        <aop:scoped-proxy/> 
    bean>

    
    <bean id="userService" class="com.something.SimpleUserService">
        
        <property name="userPreferences" ref="userPreferences"/>
    bean>
beans>

方案5:ObjectFactory< T > Or Provider < T >

public class SimpleMovieLister {
 	// 将对实际对象的依赖,转换为对Provider的依赖,ObjectFactory的使用与之类似
    private Provider<MovieFinder> movieFinder;

    @Inject
    public void setMovieFinder(Provider<MovieFinder> movieFinder) {
        this.movieFinder = movieFinder;
    }

    public void listMovies() {
        this.movieFinder.get().findMovies(...);
        // ...
    }
}

工厂

静态工厂

在定义使用静态工厂方法创建的Bean时,使用class属性指定包含静态工厂方法的类,使用factory-method属性指定工厂方法本身的名称。您应该能够调用这个方法(带有可选参数,如后面所述)并返回一个活动对象,该对象被视为与通过构造函数创建对象是一样的。这种bean定义的一个用途是在遗留代码中调用静态工厂。

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}
<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
实例工厂

与通过静态工厂方法实例化类似,使用实例工厂方法实例化将从容器中调用现有bean的非静态方法来创建新bean。要使用这种机制,将class属性保留为空,并在factory-bean属性中指定当前(或父或父)容器中bean的名称,该容器包含要调用来创建对象的实例方法。使用factory-method属性设置工厂方法本身的名称。下面的例子展示了如何配置这样一个bean:

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    
bean>


<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

一个工厂类也可以包含多个工厂方法哟哟

你可能感兴趣的:(Spring)