zoukankan      html  css  js  c++  java
  • spring装配Bean过程

    主要流程:

    1、读取配置文件
    2、实例化bean和填充bean属性
    这个粗略的流程感觉更像是一个需求,有了这个需求,那么spring内部是怎么处理的呢?
    我们知道spring的两个核心接口BeanFactory和ApplicationContext。BeanFactory主要定义容器的核心方法,ApplicationContext加以扩展,主要使用的还是ApplicationContext。在ApplicationContext的子类中,AbstractApplicationContext中的refresh()方法定义了容器加载配置文件及装配Bean的过程。
    AbstractApplicationContext#refresh()代码如下:
     1        // Prepare this context for refreshing.
     2             //1 准备刷新工作
     3             prepareRefresh();
     4             // Tell the subclass to refresh the internal bean factory.
     5             // 2 实例化BeanFactory,将配置文件的信息装入到容器的Bean定义的注册表(BeanDefinitionRegistry中),此时Bean还未初始化
     6             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
     7             // Prepare the bean factory for use in this context.
     8             // 3 准备BeanFactory 主要是加载一些类
     9             prepareBeanFactory(beanFactory);
    10             try {
    11                 // Allows post-processing of the bean factory in context subclasses.
    12                 // 4 留作子类实现
    13                 postProcessBeanFactory(beanFactory);
    14                 // Invoke factory processors registered as beans in the context.
    15                 // 5 调用工厂后处理器
    16                 invokeBeanFactoryPostProcessors(beanFactory);
    17                 // Register bean processors that intercept bean creation.
    18                 //6 注册bean后处理器
    19                 registerBeanPostProcessors(beanFactory);
    20                 // Initialize message source for this context.
    21                 //7 初始化消息源
    22                 initMessageSource();
    23                 // Initialize event multicaster for this context.
    24                 //8 初始化事件广播器
    25                 initApplicationEventMulticaster();
    26                 // Initialize other special beans in specific context subclasses.
    27                 //9 钩子方法
    28                 onRefresh();
    29                 // Check for listener beans and register them.
    30                 // 10 注册监听器
    31                 registerListeners();
    32                 // Instantiate all remaining (non-lazy-init) singletons.
    33                 //11 完成bean实例化(除lazy-init),并放入缓存中
    34                 finishBeanFactoryInitialization(beanFactory);
    35                 // Last step: publish corresponding event.
    36                 // 12 广播刷新事件
    37                 finishRefresh();
    38             }
    其实直接粘贴代码意义不大。重点还是要看refresh()函数的重点步骤。

    一、prepareRefresh():准备工作。

    在spring代码中,很容易看到这样的逻辑:就是在一个流程动作上下包裹着前后的动作。也就是说:在刷新动作时,会包裹刷新前动作和刷新后动作。这些动作可以有操作,也可以没有。又如:在读取xml文件加载bean时的一个操作(处理xml前,处理xml后):
    1 preProcessXml(root);
    2 parseBeanDefinitions(root, this.delegate);
    3 postProcessXml(root);
    这里主要记录了一下容器开始时间,初始化了属性资源。

    二、ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    作用:实例化BeanFactory,将配置文件的信息装入到容器的Bean定义的注册表(BeanDefinitionRegistry中),此时Bean还未初始化
    其中,AbastractApplicationContext#obtainFreshBeanFactory()如下:
    1 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    2         refreshBeanFactory();
    3         ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    4         if (logger.isDebugEnabled()) {
    5             logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    6         }
    7         return beanFactory;
    8 }
    这里的getBeanFactory()方法用来获取实例。重点是refreshBeanFactory()方法。
    AbstractRefreshableApplicationContext#refreshBeanFactory()
     1 protected final void refreshBeanFactory() throws BeansException {
     2         if (hasBeanFactory()) {
     3             destroyBeans();
     4             closeBeanFactory();
     5         }
     6         try {
     7             DefaultListableBeanFactory beanFactory = createBeanFactory();
     8             beanFactory.setSerializationId(getId());
     9             customizeBeanFactory(beanFactory);
    10             loadBeanDefinitions(beanFactory);
    11             synchronized (this.beanFactoryMonitor) {
    12                 this.beanFactory = beanFactory;
    13             }
    14         }
    15         catch (IOException ex) {
    16             throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    17         }
    18     }
    如果存在BeanFacotry(),则销毁。这里要保证在BeanDefinitionRegistry中存储的是最新的xml对应的Bean映射。然后就到了loadBeanDefinitions(beanFactory)加载BeanDefinitions.中间跳过一些步骤,然后就到了XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource encodedResource)
    显然是要从相应的资源(这里是XML中)读取信息。截取一段信息:
     1 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
     2         if (currentResources == null) {
     3             currentResources = new HashSet<EncodedResource>(4);
     4             this.resourcesCurrentlyBeingLoaded.set(currentResources);
     5         }
     6         if (!currentResources.add(encodedResource)) {
     7             throw new BeanDefinitionStoreException(
     8                     "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
     9         }
    10         try {
    11             InputStream inputStream = encodedResource.getResource().getInputStream();
    12             try {
    13                 InputSource inputSource = new InputSource(inputStream);
    14                 if (encodedResource.getEncoding() != null) {
    15                     inputSource.setEncoding(encodedResource.getEncoding());
    16                 }
    17                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    18             }
    19             finally {
    20                 inputStream.close();
    21             }
    22         }
    这里提到了doLoadBeanDefinitions(inputSource, encodedResource.getResource());方法。点进去之后跟想的一样,加载xml文件
    1 try {
    2             Document doc = doLoadDocument(inputSource, resource);
    3             return registerBeanDefinitions(doc, resource);
    4 }
    接着跳过几步就到了把xml中的DOM节点信息转化为Bean对应属性的方法
     1 protected void doRegisterBeanDefinitions(Element root) {
     2         BeanDefinitionParserDelegate parent = this.delegate;
     3         this.delegate = createDelegate(getReaderContext(), root, parent);
     4         if (this.delegate.isDefaultNamespace(root)) {
     5             String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
     6             if (StringUtils.hasText(profileSpec)) {
     7                 String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
     8                         profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
     9                 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
    10                     return;
    11                 }
    12             }
    13         }
    14         preProcessXml(root);
    15         parseBeanDefinitions(root, this.delegate);
    16         postProcessXml(root);
    17         this.delegate = parent;
    18 }
    这里到了parseBeanDefinitions(root, this.delegate)方法。也就是把root信息转化为BeanDefinitions。点进去发现spring处理两类节点:
    • 默认元素
    • 用户自定义元素
     1 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
     2         if (delegate.isDefaultNamespace(root)) {
     3             NodeList nl = root.getChildNodes();
     4             for (int i = 0; i < nl.getLength(); i++) {
     5                 Node node = nl.item(i);
     6                 if (node instanceof Element) {
     7                     Element ele = (Element) node;
     8                     if (delegate.isDefaultNamespace(ele)) {
     9                         parseDefaultElement(ele, delegate);
    10                     }
    11                     else {
    12                         delegate.parseCustomElement(ele);
    13                     }
    14                 }
    15             }
    16         }
    17         else {
    18             delegate.parseCustomElement(root);
    19         }
    20 }
    这里就是遍历Element了。然后对每个节点判断,如果是默认命名空间(http://www.springframework.org/schema/beans)下的元素,则使用parseDefaultElement(ele, delegate)方法,否则使用delegate.parseCustomElement(ele);方法。查看默认元素的方法:parseDefaultElement(ele, delegate);
     1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
     2         if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
     3             importBeanDefinitionResource(ele);
     4         }
     5         else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
     6             processAliasRegistration(ele);
     7         }
     8         else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
     9             processBeanDefinition(ele, delegate);
    10         }
    11         else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
    12             // recurse
    13             doRegisterBeanDefinitions(ele);
    14         }
    15 }
    这里有四个节点:
    • public static final String IMPORT_ELEMENT = "import";
    • public static final String ALIAS_ELEMENT = "alias";
    • public static final String BEAN_ELEMENT = "bean";
    • public static final String NESTED_BEANS_ELEMENT = "beans";
    这里再看一下DefaultBeanDefinitionDocumentReader#processBeanDefinition(ele, delegate)方法
    1 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    里面有一个行代码跳转到registerBeanDefinition(bdHolder, getReaderContext().getRegistry())
     1 public static void registerBeanDefinition(
     2             BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
     3             throws BeanDefinitionStoreException {
     4         // Register bean definition under primary name.
     5         String beanName = definitionHolder.getBeanName();
     6         registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
     7         // Register aliases for bean name, if any.
     8         String[] aliases = definitionHolder.getAliases();
     9         if (aliases != null) {
    10             for (String aliase : aliases) {
    11                 registry.registerAlias(beanName, aliase);
    12             }
    13         }
    14 }
    这里有一个地方需要注意:前面提到过的BeanDefinitionRegistry,也就是把配置信息装入到BeanDefinitionRegistry中。进入 registerBeanDefinition方法中如下:
    1 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    2             throws BeanDefinitionStoreException {
    3         Assert.hasText(beanName, "'beanName' must not be empty");
    4         Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    5         this.beanDefinitionMap.put(beanName, beanDefinition);
    6 }
    这里我们可以看到,beanName和beanDefinition装入到了一个Map中。这个map的定义如下:
    1 /** Map of bean definition objects, keyed by bean name */
    2     private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
    其中,key位bean的名字,value为BeanDefinition。
    这个时候的Bean还是没有属性的。

    三、prepareBeanFactory(beanFactory):加载一些类资源

    四、postProcessBeanFactory(beanFactory):留作子类实现,这里为空

    五、invokeBeanFactoryPostProcessors(beanFactory):调用BeanFactoryPostProcessor接口处理beanFactory.

    这个方法的参数是beanFactory,它包含了在xml定义的bean信息。调用这个方法,可以在读取xmlbean信息之后以及实例化bean之前修改bean定义的属性。
    spring中,有内置的一些BeanFactoryPostProcessor实现类,常用的有:
    • org.springframework.beans.factory.config.PropertyPlaceholderConfigurer:属性占位符(${user})
    • org.springframework.beans.factory.config.PropertyOverrideConfigurer
    • org.springframework.beans.factory.config.CustomEditorConfigurer:用来注册自定义的属性编辑器
    通过这几个实现类可以了解到这个接口会起到的一些作用:对占位符进行处理等
    其中:BeanFactoryPostProcessor接口定义如下:
    1 public interface BeanFactoryPostProcessor {
    2     /**
    3     *在其标准初始化后修改应用程序上下文的内部bean工厂。 所有bean定义都将被加载,但是没有bean将被实例化。 
    4     *这允许覆盖或添加属性,即使是急切地初始化bean。
    5     */
    6     void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
    7 }
    这一步的重点是理解BeanFactoryPostProcessor接口的作用。实现这个接口,可以实现修改bean属性的功能。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。可以同时配置多个BeanFactoryPostProcessor,并通过设置'order'属性来控制各个BeanFactoryPostProcessor的执行次序。
    这里摘抄一个网上的例子(原地址:http://blog.csdn.net/caihaijiang/article/details/35552859)实现一个自定义的BeanFactoryPostProcessor实现类来实现动态修改Bean属性功能。
    1.在配置文件中定义一个名为MyBean的bean,这里只提供了两个简单的属性desc和mark。以及定义一个实现BeanFactoryPostProcessor接口的类MyBeanFactoryPostProcessorImpl
    1 <bean id="myBean" class="uodut.spring3x.MyBean">
    2         <property name="desc" value="这里通过自定义MyBeanFactoryPostProcessorImpl来动态改变mark的值"></property>
    3         <property name="mark" value="initMark"></property>
    4     </bean>
    5 <bean id="myBeanFactoryPostProcessor" class="uodut.spring3x.MyBeanFactoryPostProcessorImpl"/>
    2.MyBeanFactoryPostProcessorImpl类实现如下:
     1 public class MyBeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor {
     2     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
     3         BeanDefinition bd = beanFactory.getBeanDefinition("myBean");
     4         System.out.println("属性值:" + bd.getPropertyValues().toString());
     5         MutablePropertyValues propertyValues = bd.getPropertyValues();
     6         if (propertyValues.contains("mark")) {
     7             propertyValues.addPropertyValue("mark", "initMark->afterMark");
     8         }
     9     }
    10 }
    3.定义一个测试方法来测试
    1 public void test1(){
    2         ApplicationContext context = new ClassPathXmlApplicationContext("BeanTest.xml");
    3         MyBean bean = (MyBean) context.getBean("myBean");
    4         System.out.println("描述:" + bean.getDesc());
    5         System.out.println("备注:" + bean.getMark());
    6 }
    运行后输出结果如下:(可以看到mark属性的值从initMark变成了initMark->afterMark)
    1 属性值:PropertyValues: length=2; bean property 'desc'; bean property 'mark'
    2 描述:这里通过自定义MyBeanFactoryPostProcessorImpl来动态改变mark的值
    3 备注:initMark->afterMark

    六、registerBeanPostProcessors(beanFactory);注册BeanPostProcessors

    BeanPostProcessor,可以在spring容器实例化bean之后,在执行bean的初始化方法前后,添加一些自己的处理逻辑。这里说的初始化方法,指的是下面两种:
    1)bean实现了InitializingBean接口,对应的方法为afterPropertiesSet
    2)在bean定义的时候,通过init-method设置的方法
    注意:BeanPostProcessor是在spring容器加载了bean的定义文件并且实例化bean之后执行的。BeanPostProcessor的执行顺序是在BeanFactoryPostProcessor之后。
    这里在上面的例子基础上修改:
    1 public class MyBean implements InitializingBean{
    2     public void afterPropertiesSet() throws Exception {
    3         System.out.println("调用afterPropertiesSet方法");
    4         this.desc = "在初始化方法中修改之后的描述信息";
    5     }
    6 }
    声明MyBeanPostProcessorImpl实现BeanPostProcessor
     1 public class MyBeanPostProcessorImpl implements BeanPostProcessor {
     2     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
     3         System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法之前的数据: " + bean.toString());
     4         return bean;
     5     }
     6     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
     7         System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法之后的数据:" + bean.toString());
     8         return bean;
     9     }
    10 }
    运行测试用例:
    1 属性值:PropertyValues: length=2; bean property 'desc'; bean property 'mark'
    2 调用setDesc方法
    3 调用setMark方法
    4 BeanPostProcessor,对象myBean调用初始化方法之前的数据: [描述:原始描述信息, 备注:initMark->afterMark]
    5 调用afterPropertiesSet方法
    6 调用initMethod方法
    7 BeanPostProcessor,对象myBean调用初始化方法之后的数据:[描述:在初始化方法中修改之后的描述信息, 备注:initMark->afterMark]
    8 描述:在初始化方法中修改之后的描述信息
    9 备注:initMark->afterMark
    可以看到在初始化方法前后会调用BeanPostProcessor接口的两个方法。

    七、initMessageSource();初始化消息源

    八、initApplicationEventMulticaster();初始化应用上下文事件广播

    九、onRefresh();初始化其他特殊的bean,这是一个钩子方法。

    十、registerListeners();注册监听器

    十一、finishBeanFactoryInitialization(beanFactory);初始化所有单实例的Bean,使用lazy-init的bean除外。初始化Bean后,将它们放入Spring容器的缓存中。

    十二、finishRefresh();创建上下文刷新事件,事件广播器负责将这些事件广播到每个注册的事件监听器中。

    spring容器从加载配置文件到创建出一个完整的Bean的流程

    1、ResourceLoader从存储介质中加载Spring配置信息,并使用Resource表示该配置文件的资源
    2、BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中的每个<bean>解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中。
    3、容器扫描BeanDefinitionRegistry中的BeanDefinition,使用java反射机制识别出实现BeanFactoryPostProcessor接口的Bean,然后调用BeanFactoryPostProcessor的实现类对BeanDefinitionRegistry中的BeanDefinition进行加工处理。主要作以下两个工作:
    1)对使用到占位符的<bean>元素标签进行解析,得到最终的配置值。这意味着对半成品式的BeanDefinition对象进行加工处理并得到成品的BeanDefinition对象。(这里用到了前文提到的PropertyPlaceholderConfigurer
    2)对BeanDefinitionRegistry中的BeanDefinition进行扫描,通过反射机制找出所有属性编辑器的Bean(实现java.beans.PropertyEditor接口的Bean),并自动将它们注册到spring容器的属性编辑器注册表中(PropertyEditorRegistry)
    4、Spring容器从BeanDefinitionRegistry中取出加工后的BeanDefinition,并调用InstantiationStrategy进行Bean实例化的工作。
    5、在实例化Bean时,Spring容器使用BeanWrapper对Bean进行封装,它结合该Bean的BeanDefinition以及容器中属性编辑器,完成Bean属性的设置工作。
    6、BeanPostProcess对完成属性设置的Bean进行后续加工,装配出一个准备就绪的Bean。
     
    参考资料:
    • 《spring 3.x企业应用开发实战》
    • http://blog.csdn.net/caihaijiang/article/details/35552859 
  • 相关阅读:
    php7垃圾回收机制
    PHP-FPM
    go main.main之前的准备
    ElementUI MessageBox 弹框
    ElementUI table 样式修改
    ElementUI Dropdown 下拉菜单
    vue+django实现下载文件
    ElementUI 点击展开/隐藏
    vue+django实现websocket连接
    ElementUI 上传文件以及限制
  • 原文地址:https://www.cnblogs.com/uodut/p/7218516.html
Copyright © 2011-2022 走看看