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容器中。