zoukankan      html  css  js  c++  java
  • Spring源码学习笔记(五、Spring启动流程解析:准备BeanFactory)

    目录:

    • 解析Bean表达式:BeanExpressionResolve
    • 属性编辑器:PropertyEditor
    • Aware感知
    • 忽略自动转配
    • BeanPostProcessor

    还是和之前一样,我先把主流程的代码贴出来,方便你查阅。

     1 @Override
     2 public void refresh() throws BeansException, IllegalStateException {
     3     // 方法加锁避免多线程同时刷新Spring上下文
     4     synchronized (this.startupShutdownMonitor) {
     5         // 准备上下文刷新
     6         prepareRefresh();
     7 
     8         // 告诉子类刷新内部的beanFactory返回新的BeanFactory
     9         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    10 
    11         // 在当前上下文中准备要beanFactory
    12         prepareBeanFactory(beanFactory);
    13 
    14         try {
    15             // 允许在上下文子类中对beanFactory进行后置处理
    16             postProcessBeanFactory(beanFactory);
    17 
    18             // 在上下文中将BeanFactory处理器注册为Bean
    19             invokeBeanFactoryPostProcessors(beanFactory);
    20 
    21             // 注册Bean处理器用于拦截Bean的创建
    22             registerBeanPostProcessors(beanFactory);
    23 
    24             // 在上下文中初始化国际化信息
    25             initMessageSource();
    26 
    27             // 在上下文中初始化event multicaster(事件多播器)
    28             initApplicationEventMulticaster();
    29 
    30             // 在指定的上下文子类中初始化其他指定的beans
    31             onRefresh();
    32 
    33             // 检查并注册事件监听
    34             registerListeners();
    35 
    36             // 实例化所有剩余的(非延迟初始化)单例
    37             finishBeanFactoryInitialization(beanFactory);
    38 
    39             // 最后一步:发布相应的事件
    40             finishRefresh();
    41         }
    42 
    43         catch (BeansException ex) {
    44             if (logger.isWarnEnabled()) {
    45                 logger.warn("Exception encountered during context initialization - " +
    46                         "cancelling refresh attempt: " + ex);
    47             }
    48 
    49             // 如果出现异常则销毁已创建的单例
    50             destroyBeans();
    51 
    52             // 重置活动标志
    53             cancelRefresh(ex);
    54 
    55             // 将异常传递给调用者
    56             throw ex;
    57         }
    58 
    59         finally {
    60             // Reset common introspection caches in Spring's core, since we
    61             // might not ever need metadata for singleton beans anymore...
    62             resetCommonCaches();
    63         }
    64     }
    65 }

    prepareBeanFactory源码如下:

     1 protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
     2     // 设置上下文类加载器
     3     beanFactory.setBeanClassLoader(getClassLoader());
     4     // 设置BeanExpressionResolver,用于解析属性占位符
     5     beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
     6     // 设置PropertyEditor,用于对象与字符串之间的转换
     7     beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
     8 
     9     // 配置BeanFactory的Context上下文回调
    10     beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    11     // 忽略各种依赖接口
    12     beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    13     beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    14     beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    15     beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    16     beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    17     beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    18 
    19     // BeanFactory接口不在普通工厂中注册为可解析类型
    20     // MessageSource注册(找到并自动装配)为一个Bean
    21     beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    22     beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    23     beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    24     beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    25 
    26     // MessageSource 注册(找到并自动装配)为一个Bean
    27     beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    28 
    29     // 如果发现LoadTimeWeaver,则准备植入
    30     if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    31         beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    32         // 给类型匹配设置一个临时的ClassLoader
    33         beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    34     }
    35 
    36     // Register default environment beans.
    37     if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
    38         beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    39     }
    40     if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
    41         beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    42     }
    43     if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
    44         beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    45     }
    46 }

    首先类加载器没什么可说的,就是获取一些上下文的资源。

    ———————————————————————————————————————————————————————

    BeanExpressionResolve

    我们直接看第5行的setBeanExpressionResolver

    BeanExpressionResolve其实就是用来解析Bean表达式的,默认前后缀分别是“#{”和“}”,当然你也可以自定义前后缀,如下:

     1 // 表达式默认前缀 >>> "#{"
     2 public static final String DEFAULT_EXPRESSION_PREFIX = "#{";
     3 
     4 // 表达式默认后缀 >>> "}"
     5 public static final String DEFAULT_EXPRESSION_SUFFIX = "}";
     6 
     7 public void setExpressionPrefix(String expressionPrefix) {
     8     Assert.hasText(expressionPrefix, "Expression prefix must not be empty");
     9     this.expressionPrefix = expressionPrefix;
    10 }
    11 
    12 public void setExpressionSuffix(String expressionSuffix) {
    13     Assert.hasText(expressionSuffix, "Expression suffix must not be empty");
    14     this.expressionSuffix = expressionSuffix;
    15 }

    ———————————————————————————————————————————————————————

    PropertyEditor

    之后就是添加属性编辑器,PropertyEditor。

    它是用于对象与字符串之间的转换,如“2020-05-04”就可以解析成日期对象。

    Spring提供了很多内置的PropertyEditor,它们都位于org.springframework.beans.propertyeditors包中。默认情况下,大多数由BeanWrapperImpl注册

    • CustomBooleanEditor:布尔属性编辑器。默认情况下由BeanWrapperImpl注册,但是可以通过将其自定义实例注册为自定义编辑器来覆盖。
    • CustomCollectionEditor:集合属性编辑器,可将任何源集合转换为给定的目标集合类型。
    • CustomDateEditor:Date自定义属性编辑器,支持自定义DateFormat。
    • CustomNumberEditor:Number自定义属性编辑器。可用于任何Number子类,如Integer,Long,Float,Double。默认情况下由BeanWrapperI。
    • PropertiesEditor:将字符串转换为Properties对象。默认情况下由BeanWrapperImpl注册。

    当然你也可以自定义,我们来模仿CustomDateEditor,写一个日期的转换。

    通过查看CustomDateEditor源码,发现其继承自PropertiesEditor,所以我们也同样继承PropertiesEditor,具体实现如下:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
     5 
     6     <!-- PropertyEditor方式,和PropertyEditorRegistrar方式二选一 -->
     7     <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
     8         <property name="customEditors">
     9             <map>
    10                 <entry key="java.util.Date" value="com.jdr.spring.propertyeditor.CustomEditor"/>
    11             </map>
    12         </property>
    13     </bean>
    14 
    15     <!-- PropertyEditorRegistrar方式,和PropertyEditor方式方式二选一 -->
    16     <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    17         <property name="propertyEditorRegistrars">
    18             <list>
    19                 <bean class="com.jdr.spring.propertyeditor.CustomEditorRegistrar"/>
    20             </list>
    21         </property>
    22     </bean>
    23 
    24     <bean class="com.jdr.spring.propertyeditor.PropertyEditorBean" name="propertyEditorBean">
    25         <property name="date" value="2020-05-24 22:00:00"/>
    26     </bean>
    27 
    28 </beans>
     1 public class PropertyEditorBean {
     2 
     3     private Date date;
     4 
     5     public Date getDate() {
     6         return date;
     7     }
     8 
     9     public void setDate(Date date) {
    10         this.date = date;
    11     }
    12 }
     1 public class CustomEditor extends PropertyEditorSupport {
     2 
     3     @Override
     4     public String getAsText() {
     5         return super.getAsText();
     6     }
     7 
     8     @Override
     9     public void setAsText(String text) throws IllegalArgumentException {
    10         try {
    11             DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    12             Date parse = format.parse(text);
    13             setValue(parse);
    14         } catch (ParseException e) {
    15             e.printStackTrace();
    16         }
    17     }
    18 }
    1 public class CustomEditorRegistrar implements PropertyEditorRegistrar {
    2 
    3     @Override
    4     public void registerCustomEditors(PropertyEditorRegistry registry) {
    5         registry.registerCustomEditor(Date.class, new CustomEditor());
    6     }
    7 }
    1 public class Test {
    2 
    3     public static void main(String[] args) {
    4         ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("propertyEditor.xml");
    5         PropertyEditorBean propertyEditorBean = (PropertyEditorBean) ctx.getBean("propertyEditorBean");
    6         System.out.println(propertyEditorBean.getDate());
    7     }
    8 }

    ———————————————————————————————————————————————————————

    Aware感知

    我们可以prepareBeanFactory的12-17行看到有很多的Aware,那Aware究竟是做什么的呢。

    如果在某个Bean里面想要使用Spring框架提供的功能,可以通过Aware接口来实现

    通过实现Aware接口,Spring可以在启动时,调用接口定义的方法,并将Spring底层的一些组件注入到自定义的Bean中。

    • ApplicationContextAware:当ApplicationContext创建实现ApplicationContextAware接口的Bean实例时,将为该Bean实例提供对该ApplicationContext的引用。
    • ApplicationEventPublisherAware:为Bean实例提供对ApplicationEventPublisherAware的引用。
    • BeanFactoryAware:为Bean实例提供对BeanFactory的引用。
    • BeanNameAware:获取Bean在BeanFactory中配置的名字。
    • MessageSourceAware:为Bean实例提供对MessageSource的引用。
    • EnvironmentAware:获得Environment支持,这样可以获取环境变量。
    • ResourceLoaderAware:获得资源加载器以获得外部资源文件。
    1 <?xml version="1.0" encoding="UTF-8"?>
    2 <beans xmlns="http://www.springframework.org/schema/beans"
    3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    5 
    6     <bean id="awareBean" class="ai.yunxi.aware.AwareBean"/>
    7 </beans>
     1 public class AwareBean implements ApplicationContextAware, BeanNameAware {
     2     @Override
     3     public void setBeanName(String name) {
     4         System.out.println("=====>" + name);
     5     }
     6 
     7     @Override
     8     public void setApplicationContext(ApplicationContext ctx)
     9             throws BeansException {
    10         System.out.println("=====>" + ctx.getBean("awareBean"));
    11     }
    12 }

    ———————————————————————————————————————————————————————

    忽略自动转配:

    Spring在ConfigurableListableBeanFactory接口中提供了2个可以忽略自动装配的方法:

    1 // 自动装配时忽略指定接口或类的依赖注入
    2 void ignoreDependencyType(Class<?> type);
    3 
    4 // 忽略接口实现类中存在依赖外部的Bean注入
    5 void ignoreDependencyInterface(Class<?> ifc);

    忽略自动装配的做法使得一些基础组件(如:ApplicationContext或BeanFactory)依赖在自动装配时被忽略,而由框架统一设置依赖。如ApplicationContextAware接口的设置会在ApplicationContextAwareProcessor类中完成。

    BeanPostProcessor

    如果想在Spring容器中完成Bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理,就需要定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IoC容器中。

  • 相关阅读:
    项目需求分析
    对软件开发的感想
    趣拼图最后完成及总结
    UML用例图
    数据流图和数据流程图
    第三代迭代目标
    Scrum的三种角色划分及小组成员分工
    WBS Model
    团队开发—百科全书软件项目
    开发流程
  • 原文地址:https://www.cnblogs.com/bzfsdr/p/12953140.html
Copyright © 2011-2022 走看看