SpringBean(1)创建

BeanFactory

源码注释

/**
 * The root interface for accessing a Spring bean container.
 * 访问Spring bean容器的根接口。
 * 什么是Spring bean容器?
 * <p>This is the basic client view of a bean container;
 * further interfaces such as {@link ListableBeanFactory} and
 * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
 * are available for specific purposes.
 * 这是bean容器的基础客户端视图,具体用途见ListableBeanFactory、ConfigurableBeanFactory接口
 *
 * <p>This interface is implemented by objects that hold a number of bean definitions,
 * each uniquely identified by a String name. Depending on the bean definition,
 * the factory will return either an independent instance of a contained object
 * (the Prototype design pattern), or a single shared instance (a superior
 * alternative to the Singleton design pattern, in which the instance is a
 * singleton in the scope of the factory). Which type of instance will be returned
 * depends on the bean factory configuration: the API is the same. Since Spring
 * 2.0, further scopes are available depending on the concrete application
 * context (e.g. "request" and "session" scopes in a web environment).
 * 该接口被许多拥有一组bean definitions的对象实现,每个对象能通过一个字符串名称唯一识别。根据bean definition,
 * 该工厂将会返回一个独立实例或共享实例。每种实例类型可以根据bean factory configuration返回,api都是相同的。
 * 从spring2.0开始,实例返回可根据具体应用上下文获取
 * 什么是bean definition?
 * 什么是bean factory configuration,如何获取scope
 *
 * <p>The point of this approach is that the BeanFactory is a central registry
 * of application components, and centralizes configuration of application
 * components (no more do individual objects need to read properties files,
 * for example). See chapters 4 and 11 of "Expert One-on-One J2EE Design and
 * Development" for a discussion of the benefits of this approach.
 * BeanFactory是应用组件的中心注册接口也是应用组件的中心配置接口
 * bean是如何注册?
 *
 * <p>Note that it is generally better to rely on Dependency Injection
 * ("push" configuration) to configure application objects through setters
 * or constructors, rather than use any form of "pull" configuration like a
 * BeanFactory lookup. Spring's Dependency Injection functionality is
 * implemented using this BeanFactory interface and its subinterfaces.
 * 依靠依赖注入通过setter和构造函数来配置应用对象的值,比使用pull方式的依赖查找更好。Spring的依赖注入功能
 * 被BeanFactory接口和子接口实现
 * 依赖注入与依赖查找如何使用依赖注入(DI)对应该的对象赋值
 *
 * <p>Normally a BeanFactory will load bean definitions stored in a configuration
 * source (such as an XML document), and use the {@code org.springframework.beans}
 * package to configure the beans. However, an implementation could simply return
 * Java objects it creates as necessary directly in Java code. There are no
 * constraints on how the definitions could be stored: LDAP, RDBMS, XML,
 * properties file, etc. Implementations are encouraged to support references
 * amongst beans (Dependency Injection).
 * 通常BeanFactory会加载存储在配置资源中的bean定义,并使用org.springframework.beans包配置bean。然而,一个具体
 * 的实现可以简单的返回创建的java对象,没有关于bean定义该如何存储的约束,比如:LDAP,RDBMS,XML,properties文件。
 * 实现鼓励支持bean依赖注入
 * 以上注释表明可自定义bean加载。如给出一种文件,将文件中的描述变为Bean Definition就能注册到容器中
 *
 * <p>In contrast to the methods in {@link ListableBeanFactory}, all of the
 * operations in this interface will also check parent factories if this is a
 * {@link HierarchicalBeanFactory}. If a bean is not found in this factory instance,
 * the immediate parent factory will be asked. Beans in this factory instance
 * are supposed to override beans of the same name in any parent factory.
 * 与ListableBeanFactory相比,HierarchicalBeanFactory接口中的所有操作都会检测父工厂。如果bean在当前工厂实例中
 * 未找到,会立刻向父工厂查找。当前工厂实例中的bean应该覆盖父工厂中同名的bean
 * <p>Bean factory implementations should support the standard bean lifecycle interfaces
 * as far as possible. The full set of initialization methods and their standard order is:
 * Bean工厂实现应该尽可能地满足标准的bean生命周期接口。初始化方法的全部步骤和标准顺序如下:
 * <ol>
 * <li>BeanNameAware's {@code setBeanName}
 * <li>BeanClassLoaderAware's {@code setBeanClassLoader}
 * <li>BeanFactoryAware's {@code setBeanFactory}
 * <li>EnvironmentAware's {@code setEnvironment}
 * <li>EmbeddedValueResolverAware's {@code setEmbeddedValueResolver}
 * <li>ResourceLoaderAware's {@code setResourceLoader}
 * (only applicable when running in an application context)
 * <li>ApplicationEventPublisherAware's {@code setApplicationEventPublisher}
 * (only applicable when running in an application context)
 * <li>MessageSourceAware's {@code setMessageSource}
 * (only applicable when running in an application context)
 * <li>ApplicationContextAware's {@code setApplicationContext}
 * (only applicable when running in an application context)
 * <li>ServletContextAware's {@code setServletContext}
 * (only applicable when running in a web application context)
 * <li>{@code postProcessBeforeInitialization} methods of BeanPostProcessors
 * <li>InitializingBean's {@code afterPropertiesSet}
 * <li>a custom init-method definition
 * <li>{@code postProcessAfterInitialization} methods of BeanPostProcessors
 * </ol>
 *
 * <p>On shutdown of a bean factory, the following lifecycle methods apply:
 * <ol>
 * <li>{@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors
 * <li>DisposableBean's {@code destroy}
 * <li>a custom destroy-method definition
 * </ol>
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 13 April 2001
 * @see BeanNameAware#setBeanName
 * @see BeanClassLoaderAware#setBeanClassLoader
 * @see BeanFactoryAware#setBeanFactory
 * @see org.springframework.context.ResourceLoaderAware#setResourceLoader
 * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher
 * @see org.springframework.context.MessageSourceAware#setMessageSource
 * @see org.springframework.context.ApplicationContextAware#setApplicationContext
 * @see org.springframework.web.context.ServletContextAware#setServletContext
 * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization
 * @see InitializingBean#afterPropertiesSet
 * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
 * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization
 * @see DisposableBean#destroy
 * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName
 */

Spring Bean容器

Bean容器就是放对象的地方,使用如下Demo验证对象存放位置

@Service
public class UserA {
    public void f1(){
        System.out.println("userA f1...");
    }
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(UserA.class);
        UserA userA = annotationConfigApplicationContext.getBean(UserA.class);
        userA.f1();
    }
}

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.5</version>
</dependency>




对象存放在DefaultSingletonBeanRegistry类的singletonObjects中。所以DefaultSingletonBeanRegistry为真正的Bean容器。类名直译:默认单例Bean登记处。
DefaultSingletonBeanRegistry的源码注释

/**
 * Generic registry for shared bean instances, implementing the
 * {@link org.springframework.beans.factory.config.SingletonBeanRegistry}.
 * Allows for registering singleton instances that should be shared
 * for all callers of the registry, to be obtained via bean name.
 * 共享bean实例的通用登记处,实现了SingletonBeanRegistry,允许注册singleton实例,对于登记处的所有调用者实例是共享的,通过名字获    
 * 取
 * <p>Also supports registration of
 * {@link org.springframework.beans.factory.DisposableBean} instances,
 * (which might or might not correspond to registered singletons),
 * to be destroyed on shutdown of the registry. Dependencies between
 * beans can be registered to enforce an appropriate shutdown order.
 *
 * <p>This class mainly serves as base class for
 * {@link org.springframework.beans.factory.BeanFactory} implementations,
 * factoring out the common management of singleton bean instances. Note that
 * the {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
 * interface extends the {@link SingletonBeanRegistry} interface.
 * 该类主要是为BeanFactory的实现提供基础服务,提炼出单例bean实例的公共部分
 * <p>Note that this class assumes neither a bean definition concept
 * nor a specific creation process for bean instances, in contrast to
 * {@link AbstractBeanFactory} and {@link DefaultListableBeanFactory}
 * (which inherit from it). Can alternatively also be used as a nested
 * helper to delegate to.
 * 与AbstractBeanFactory、DefaultListableBeanFactory相反,该类既没有呈现bean definition的概念,也不承担bean实例的具体创建
 * 流程
 * @author Juergen Hoeller
 * @since 2.0
 * @see #registerSingleton
 * @see #registerDisposableBean
 * @see org.springframework.beans.factory.DisposableBean
 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
 */

从断点处getBean进入:



可以看到Bean是由BeanFactory获取的,正是注释中的第一句:The root interface for accessing a Spring bean container
继续debug可以看到此处的BeanFactory是DefaultListableBeanFactory



DefaultListableBeanFactory的UML图可简化一下:

DefaultListableBeanFactory源码注释

/**
 * Spring's default implementation of the {@link ConfigurableListableBeanFactory}
 * and {@link BeanDefinitionRegistry} interfaces: a full-fledged bean factory
 * based on bean definition metadata, extensible through post-processors.
 * Spring中ConfigurableListableBeanFactory与BeanDefinitionRegistry(bean定义登记处)的默认实现。该类是基于bean定义元数据
 * bean增强器扩展的全方位实现
 * <p>Typical usage is registering all bean definitions first (possibly read
 * from a bean definition file), before accessing beans. Bean lookup by name
 * is therefore an inexpensive operation in a local bean definition table,
 * operating on pre-resolved bean definition metadata objects.
 * 在获取bean之前,先用于注册bean定义。在本地的bean定义表中通过名称查找bean是一个性能好高的操作
 * <p>Note that readers for specific bean definition formats are typically
 * implemented separately rather than as bean factory subclasses: see for example
 * {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
 *
 * <p>For an alternative implementation of the
 * {@link org.springframework.beans.factory.ListableBeanFactory} interface,
 * have a look at {@link StaticListableBeanFactory}, which manages existing
 * bean instances rather than creating new ones based on bean definitions.
 */

实现BeanFactory,继承DefaultSingletonBeanRegistry,使行为与容器分离。这里的妙处在于BeanFactory都不知道要操作什么容器,只提供了访问容器的行为,DefaultSingletonBeanRegistry实现简单对象存储,复杂的逻辑由具体的子类实现,比如Bean的创建过程,过程中的Aware通知,Bean的增强处理,Bean查找时的候选过滤,查找时校验等(上图让我想到《Head First 设计模式》入门篇中飞行和呱呱叫行为的抽象),DefaultListableBeanFactory是能真正获取到Bean的地方,然而代码中获取Bean的地方是由AnnotationConfigApplicationContext开始的,这里就引出了ApplicationContext

应用程序上下文

Demo中AnnotationConfigApplicationContext的UML图



ApplicationContext源码注释

/**
 * Central interface to provide configuration for an application.
 * This is read-only while the application is running, but may be
 * reloaded if the implementation supports this.
 *
 * <p>An ApplicationContext provides:
 * <ul>
 * <li>Bean factory methods for accessing application components.
 * Inherited from {@link org.springframework.beans.factory.ListableBeanFactory}.
 * <li>The ability to load file resources in a generic fashion.
 * Inherited from the {@link org.springframework.core.io.ResourceLoader} interface.
 * <li>The ability to publish events to registered listeners.
 * Inherited from the {@link ApplicationEventPublisher} interface.
 * <li>The ability to resolve messages, supporting internationalization.
 * Inherited from the {@link MessageSource} interface.
 * <li>Inheritance from a parent context. Definitions in a descendant context
 * will always take priority. This means, for example, that a single parent
 * context can be used by an entire web application, while each servlet has
 * its own child context that is independent of that of any other servlet.
 * </ul>
 *
 * <p>In addition to standard {@link org.springframework.beans.factory.BeanFactory}
 * lifecycle capabilities, ApplicationContext implementations detect and invoke
 * {@link ApplicationContextAware} beans as well as {@link ResourceLoaderAware},
 * {@link ApplicationEventPublisherAware} and {@link MessageSourceAware} beans.
 */

框架简化UML



可以看到一个代理模式,上下文具有了访问Bean容器的所有能力,在AbstractApplicationContext可以看到



上下文具有访问BeanDefinition容器的所有能力,在GenericApplicationContext可以看到

BeanDefinition容器

在上面的UML图中,AnnotationConfigApplicationContext、DefaultListableBeanFactory都实现了BeanDefinitionRegistry接口:Bean定义登记处
BeanDefinitionRegistry源码注释

/**
 * Interface for registries that hold bean definitions, for example RootBeanDefinition
 * and ChildBeanDefinition instances. Typically implemented by BeanFactories that
 * internally work with the AbstractBeanDefinition hierarchy.
 * 登记处接口,该登记处保存着bean定义,如RootBeanDefinition、ChildBeanDefinition实例。被Bean工厂的子类实现
 * <p>This is the only interface in Spring's bean factory packages that encapsulates
 * <i>registration</i> of bean definitions. The standard BeanFactory interfaces
 * only cover access to a <i>fully configured factory instance</i>.
 * 
 * <p>Spring's bean definition readers expect to work on an implementation of this
 * interface. Known implementors within the Spring core are DefaultListableBeanFactory
 * and GenericApplicationContext.
 */

BeanDefinition

源码注释

/**
 * A BeanDefinition describes a bean instance, which has property values,
 * constructor argument values, and further information supplied by
 * concrete implementations.
 * BeanDefinition描述bean实例,BeanDefinition有属性值、构造器参数值、更多的信息由具体的实现提供
 * <p>This is just a minimal interface: The main intention is to allow a
 * {@link BeanFactoryPostProcessor} to introspect and modify property values
 * and other bean metadata.
 *
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @since 19.03.2004
 * @see ConfigurableListableBeanFactory#getBeanDefinition
 * @see org.springframework.beans.factory.support.RootBeanDefinition
 * @see org.springframework.beans.factory.support.ChildBeanDefinition
 */

Demo验证BeanDefinition到底是什么

@Service
public class UserA {
    public void f1(){
        System.out.println("userA f1...");
    }
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(UserA.class);
        BeanDefinition beanDefinition = annotationConfigApplicationContext.getBeanFactory().getBeanDefinition("userA");
        System.out.println(beanDefinition);
    }
}

断点展示beanDefinition内容



可以看到BeanDefinition存放了类的注册,全路径,scope范围等元信息

Bean创建过程

注册BeanDefinition到容器,登记bean名称


AbstractApplicationContext::refresh

先看看原文核心流程反应

// Prepare this context for refreshing.准备上下文用于刷新
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.告诉子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.准备bean工厂在上下文中使用
prepareBeanFactory(beanFactory);
// Allows post-processing of the bean factory in context subclasses.允许上下文子类中的bean工厂增强处理
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.调用工厂增强器在上下文中作为bean进行注册
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.注册bean增强器,该增强器能拦截bean创建
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.为上下文初始化事件播放器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.在特定上下文子类中,初始化其他bean
onRefresh();
// Check for listener beans and register them.检查监听bean并注册
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.初始化所有剩下的(非懒加载)单例
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.发布相应事件
finishRefresh();

从上文的注释中可以看到几个核心词汇:BeanFactoryPostProcessor--bean工厂增强器、BeanPostProcessor--bean增强器、ApplicationEventMulticaster--事件播放器、ApplicationListener-应用监听器。我们先看bean的初始化流程,后面再学习这些核心词汇涉及的内容。在构造函数设置断点
Bean创建序列图


从图中可以看到重点逻辑在AbstractAutowireCapableBeanFactory::doCreateBean


在这个过程中涉及到3个重要的单词:instantiate(实例化)-->populate(填值)-->initialize(初始化)

instantiate实例化

调用构造函数(默认构造函数或代参构造函数)分配内存并对普通成员变量(不需要依赖注入的变量)赋值


populate填值

成员变量中的@value @Autowired @Resource依赖查询赋值


initialize初始化

实现类的对应接口通知

  1. Aware(BeanNameAware、BeanClassLoaderAware、BeanFactoryAware)接口
  2. BeanPostProcessor::postProcessBeforeInitialization
  3. InitializingBean::afterPropertiesSet、initMethod
  4. BeanPostProcessor::postProcessAfterInitialization

    BeanPostProcessor::postProcessBeforeInitialization
    跟进源码可以看到会对当前的所有PostProcesser遍历一边,调用Befroe方法

    ApplicationContextAwareProcessor调用如下的Aware通知,常用的setApplicationContext获取上下午就是这儿通知的

    CommonAnnotationBeanPostProcessor调用被@PostConstruct标注的函数
    InitializingBean::afterPropertiesSet、initMethod

    调用InitializingBean::afterPropertiesSet,调用@Bean注解上initMethod对应的方法
    由上可知常见方法的调用顺序:
    ApplicationContextAware::setApplicationContext-->@PostConstruct-->InitializingBean::afterPropertiesSet-->@Bean(initMethod)

总结

  • 看源码一定要重视注释
  • 纵向了解顶层接口或类的设计概念,顶层概念的框架图。再通过Bean创建过程,横向串联各个接口的业务关联。这也是面向抽象与面向过程的区别
  • 可以自定义BeanPostProcessor在初始化之前和之后对Bean增强
  • 阅读源码,不只是知道流程,还能够知道如何扩展
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容