1. spring 容器
1. 内部工作机制
Spring 的AbstractApplicationContext是ApplicationContext的抽象实现类,该抽象类的refresh()方法定义了Spring容器在加载配置文件后的各项处理过程,这些处理过程清晰地刻画了Spring容器启动时的各项操作
下面是refresh()中的逻辑执行:
各步详解:
下图描述Spring容器从加载配置文件到创建出一个完整Bean的作业流程及参与的角色 :
各组件详解:
注:
1.spring 框架的脉络:
- 接口层描述了容器的重要组件及组件间的协作关系
- 继承体系逐步实现组件的各项功能
2.Spring组件按其所承担的角色可以划分为两类 :
- 物料组件:Resource,BeanDefinition,PropertyEditor及最终的Bean等,像流水线上被加工的物料
- 设备组件:ResourceLoader,BeanDefinitionReader,BeanFactoryPostProcessor,InstantiationStrategy及BeanWrapper等,像流水线上的加工设备
2. BeanDefinition
- BeanDefinition是配置文件元素标签在容器内的表示。
<bean>
拥有class,scope,lazy-init等配置属性,BeanDefinition对应的有beanClass,scope,lazyInit等属性
注: 在配置文件中:
- 父
<bean>
对应 RootBeanDefinition - 子
<bean>
对应 ChildBeanDefinition - AbstractBeanDefinition 对二者共同的类信息进行抽象
其中:
- Spring通过
BeanDefinition
将配置文件中的<bean>
配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。 - BeanDefinitionRegistry就像Spring配置信息的内存数据库,后续操作直接从BeanDefinitionRegistry中读取配置信息。
- 一般情况下,BeanDefinition只在容器启动时加载并解析,除非容器刷新或重启,这些信息不会发生变化。
创建最终的BeanDefinition主要包括两个步骤 :
- ①. 利用BeanDefinitionReader读取承载配置信息的Resource,通过XML解析器解析配置信息的DOM(Document Object Model)对象,简单地为每个生成对应的BeanDefinition。在配置文件中,可能通过占位符变量引用外部属性文件的属性,这些占位符变量在这一步还未被解析出来
- ②. 利用容器中注册的BeanFactoryPostProcessor对半成品的BeanDefinition进行加工处理,将以占位符表示的配置解析为最终的实际值
3. InstantiationStrategy
- InstantiationStrategy负责根据BeanDefinition对象创建一个Bean实例。
- Spring将实例化Bean的工作通过一个策略接口进行描述,是为了可以方便地采用不同的实例化策略,以满足不同需求。
SimpleInstantiationStrategy
是最常用的实例化策略,该策略利用Bean实现类的默认构造函数,带参构造函数或工厂方法创建Bean的实例CglibSubclassingInstantiationStrategy
扩展了SimpleInstantiationStrategy,为需要进行方法注入的Bean提供了支持。利用CGLib类库为Bean动态生成子类,在子类中生成方法注入的逻辑,然后使用这个动态生成的子类创建Bean的实例InstantiationStrategy
仅负责实例化Bean的操作,相当于new的功能,不会参数Bean属性的设置。所以由InstantiationStrategy返回的Bean实例,实际上是半成品,属性填充的工作留待BeanWrapper来完成
4. BeanWrapper
- Spring委托BeanWrapper完成Bean属性的填充工作
- 在Bean实例被InstantiationStrategy创建出来后容器主控程序将Bean通过BeanWrapper包装起来(通过调用BeanWrapper的setWrappedInstance(Object obj)方法完成)
PropertyAccessor
接口定义了各种访问Bean 属性的方法PropertyEditorRegistry
是属性编辑器的注册表
BeanWrapper实现类BeanWrapperImpl有三重身份:
- ①. Bean包装器
- ②. 属性访问器
- ③. 属性编辑器注册表
一个BeanWrapperImpl实例内部封装了两类组件:
- 被封装的待处理的Bean
- 一套用于设置Bean属性的属性编辑器
填充属性:
- 目标Bean实例和属性编辑器
- 获取Bean对应的BeanDefinition,它从BeanDefinitionRegistry中直接获取。Spring主控程序从BeanDefinition中获取Bean属性的配置信息PropertyValue,并使用属性编辑器对PropertyValue进行转换以得到Bean的属性。
- BeanWrapperImpl在内部使用Spring 的BeanUtils工具类对Bean进行反射操作,设置属性
2. 使用外部属性文件
Spring提供了PropertyPlaceholderConfigurer,它能使Bean在配置时应用外部属性文件。它实现了BeanFactoryPostProcessor接口,也是一个Bean工厂后处理器
1. PropertyPlaceholderConfigurer属性文件
- 使用PropertyPlaceholderConfigurer属性文件 :
通过PropertyPlaceholderConfigurer引入jdbc.properties属性文件,调整数据源Bean的配置:
或:
- PropertyPlaceholderConfigurer的其他属性 : locations,fileEncoding,order,placeholderPrefix和placeholderSuffix
- 使用引用属性文件:
<context:property-placeholder location="classpath:com/smart/placeholder/jdbc.properties"/>
在基于注解及基于Java类的配置中引用属性:
- 在基于XML的配置文件中,通过”&{propNaem}”形式引用属性值
- 在基于注解配置的Bean可以通过@Value注解为Bean的成员变量或方法入参自动注入容器已有的属性
注:
- @Value注解可以为Bean注入一个字面值,亦可以通过@Value(“&{propName}”)的形式根据属性名注入属性值。
- 由于标注@Configureration的类本身就相当于标注了@Component,所以标注了@Configuration的类中引用属性的方式和基于注解配置的引用方式是完全一样的
3. 国际化信息
1. Locale
Locale是表示语言和国家/地区信息的本地化类,是创建国际化应用的基础
2. 本地化工具类
java.util包中提供了几个支持本地化的格式化操作工具类,如NumberFormat,DateFormat,MessageFormat
如:
注:
- pattern1是简单形式的格式化信息串,通过{n}占位符指定动态参数的替换位置索引,{0}表示第一个参数,{1}表示第二个参数,以此类推
- pattern2,除了参数位置索引索引,还指定了参数的类型和样式。{1,time,short}表示从第二个入参中获取时分秒部分的值,显示为短样式时间;{1,date,long}表示从第二个入参中获取日期部分的值,显示为长样式时间。
- 在②定义了用于替换格式化占位符的动态参数,用到了自动装包的语法。
- 在③通过messageFormat的format()方法格式化信息串,使用了系统默认的本地化对象,由于是中文平台,因此默认为Locale.CHINA。在④显示指定了MessageFormat的本地化对象
3. MessageSource
Spring定义了访问国际化信息的MessageSource接口,并定义了几个实现类:
1.MessageSource类结构 :
HierarchicalMessageSource
接口添加了两个方法,建立父子层级的MessageSource结构,该接口的setParentMessageSource(MessageSource parent)方法用于设置父MessageSource,而
getParentMessageSource()方法用于返回父MessageSource。HierarchicalMessageSource
接口最终的两个实现类是ResourceBundleMessageSource和ReloadableResourceBundleMessageSource。 它们基于ResourceBundle基础类实现,允许仅通过资源吗加载国际化信息。ReloadableResourceBundleMessageSource
提供了定时刷新功能,允许在不重启系统的情况更新资源的信息。StaticMessageResource
主要用于程序测试,允许通过编程的方式提供国际化信息。而DelegatingMessageSource是为了方便操作父MessageSource而提供的代理类
2.ResourceBundleMessageSource: 该类允许用户通过beanName指定一个资源名(包括类路径的全限定资源名),或通过beanNames指定一组资源名,完成和6-17相同的任务
3.ReloadableResourceBundleMessageSource: 和ResourceBundleMessageSource的唯一区别在于它可以定时刷新资源文件,以便在应用程序不重启的情况下感知资源文件的变化
4. 容器事件
ApplicationContext能发布事件并且允许注册相应的事件监听器,拥有一套完善的事件发布和监听机制。Java通过EventObject类和EventListener接口描述事件和监听器,某个组件或框架如需事件发布和监听机制,需要通过扩展它们进行定义。
在事件体系中,除了事件和监听器外,还有3个重要概念:
- ①事件源:事件的产生者,任何一个EventObject都必须拥有一个事件源
- ②事件监听器注册表:组件或框架必须提供事件监听器注册表来保存事件监听器。当组件和框架中的事件源产生事件时,就会通知这些位于事件监听器注册表中的监听器。
- ③事件广播器:它是事件和事件监听器沟通的桥梁,负责把事件通知给事件监听器
1. Spring事件类结构
事件类:
注: Application的唯一构造函数是ApplicationEvent(Object source)
,通过source指定事件源,有两个子类:ApplicationContextEvent
:容器事件,拥有4个子类,分别表示容器启动,刷新,停止及关闭的事件RequestHandleEvent
:这是一个与Web应用相关的事件,当一个HTTP请求被处理后,产生该事件,只有在web.xml中定义了DispatcherServlet时才会产生该事件。拥有两个子类,分别代表Servlet和Portlet的请求事件
事件监听器接口 :
ApplicationListener
接口只定义了一个方法:onApplicationEvent(E event),该方法接收ApplicationEvent事件对象,在该方法中编写事件的响应处理逻辑。SmartApplicationListener
定义了两个方法:
boolean supportsEventType(Class<? extends ApplicationEvent> eventType)
:指定监听器支持哪种类型的容器事件,即它只会对该类型的事件做出响应boolean supportsSourceType(Class<?> sourceType)
:指定监听器仅对何种事件源对象做出响应
事件广播器:
2. Spring事件体系的具体实现
- Spring在ApplicationContext接口的抽象实现类AbstractApplicationContext中完成了事件体系的搭建。
- AbstractApplicationContext拥有一个applicationEventMulticaster(应用上下文事件广播器)成员变量,它提供了容器监听器的注册表。
AbstractApplicationContext在refresh()这个容器启动启动方法中通过以下3个步骤搭建了事件的基础设施:
注:
- 在⑤,Spring初始化事件的广播器,可以在配置文件中为容器定义一个自定义的事件广播器,只要实现ApplicationEventMulticaster即可,Spring会通过反射机制将其注册容器的事件广播器。如果没有找到配置的外部事件广播器,则Spring自动使用SimpleApplicationEventMulticaster作为事件广播器
- 在⑦,Spring根据反射机制,从BeanDefinitionRegistry中找出所有实现ApplicationListener的Bean,将它们注册为容器的事件监听器,即将其添加到事件广播器所提供的事件监听器注册表中
- 在⑨,容器启动完成,调用事件发布接口向容器中所有的监听器发布事件