简单介绍一下spring bean的生命周期,bean的生命周期简述
目录
豆定义读者体系BeanDefinitionReader接口定义元信息配置与解析方式读取器元信息解析源码分析AnnotatedBeanDefinitionReader元信息解析源码分析总结写在前面
注:本文章使用的跳羚版本为发布,其弹簧版本为发布
虽然豆的创建可以采用BeanDefinitionAPI也可以直接采用注解方式,但从学习角度出发这里主要以应用程序界面形式创建豆子。下面以一段豆创建的示例来引出讨论的议题。
公众的类XmlBeanMetaDataConfigDemo { public static void main(String[]args){ DefaultListableBeanFactory bean factory=new DefaultListableBeanFactory();XmlBeanDefinitionReader bean定义reader=new XmlBeanDefinitionReader(bean factory);int bean definitions=bean定义读取器。loadbean定义( META-INF/spring。XML’);System.out.println(加载的的载入和解析个数为: bean定义);//用户就是普通的波乔类这里不再给出用户定义用户user=bean factory。获取bean(用户,用户。类);System.out.println(用户);}}?可扩展标记语言版本=1.0 编码=UTF八号?豆子xmlns= http://www。spring框架。org/schema/beans xmlns : xsi= http://www。w3。org/2001/XML schema-instance xsi :架构位置= http://www。spring框架。org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd bean id= user class= com。我就是我。dto。用户属性名=名称值=我就是我/属性名=年龄值= 上面代码是使用BeanDefinitionReader去加载可扩展置标语言文件中的豆定义
BeanDefinitionReader体系
先来看一下BeanDefinitionReader的接口定义以及继承关系
通过上面的类图我们发现BeanDefinitionReader有三个实现类,可以看出针对豆定义的不同载体均提供了解析手段,有可扩展置标语言形式的、有性能形式的等等。
BeanDefinitionReader接口定义
公共接口BeanDefinitionReader { //返回注册了当前的载入和解析的beanfactorybeanditionregistry获取注册表();@ NullableResourceLoader getResourceLoader();@ nullable class loader getBeanClassLoader();//BeanName生成器,默认是DefaultBeanNameGeneratorBeanNameGenerator getBeanNameGenerator();//从指定资源中加载BeanDefinition,并返回加载
到的BeanDefinition的个数int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;//从指定资源路径中中加载BeanDefinition,并返回加载到的BeanDefinition的个数int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;}由于Bean元信息的配置与解析是息息相关的,下面的一些例子也是将它们揉在一起讨论的。
元信息配置与解析方式
1、API方式
这种方式直接采用BeanDefinition
API 来构成Bean元信息,并将其注入到IoC容器中,这里主要使用到BeanDefinitionBuilder
或GenericBeanDefinition
两种方式来创建Bean元信息
关于BeanDefinition
这个议题的讨论会放在其他篇章中,这里不再赘述了。
public class ApiBeanMetaDataConfigDemo { public static void main(String[] args) { //创建注解相关的上下文 AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(); //创建GenericBeanDefinition GenericBeanDefinition beanDefinition=new GenericBeanDefinition(); //设置BeanClass beanDefinition.setBeanClass(User.class); //设置属性 MutablePropertyValues propertyValues=new MutablePropertyValues(); propertyValues.addPropertyValue("name","我就是我"); propertyValues.addPropertyValue("age",18); beanDefinition.setPropertyValues(propertyValues);//注册BeanDefinition context.registerBeanDefinition("user",beanDefinition);//刷新IoC容器 context.refresh();//获取Bean User user = context.getBean("user", User.class); System.out.println(user);//关闭上下文 context.close(); }}
2、面向XML配置
从XML配置资源处 加载BeanDefinition
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.wojiushiwo.dto.User"> <property name="name" value="wojiushiwo"/> <property name="age" value="18"/> </bean></beans>
public class XmlBeanMetaDataConfigDemo { public static void main(String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //加载指定文件的BeanDefinition,并获取加载地BeanDefinition个数 int beanDefinitions = beanDefinitionReader.loadBeanDefinitions("META-INF/spring.xml"); System.out.println("加载的BeanDefinition个数为:" + beanDefinitions); User user = beanFactory.getBean("user", User.class); System.out.println(user); }}
3、面向Properties配置
这种配置方式不太常用,配置资源时需要遵守规则,配置规则可参考PropertiesBeanDefinitionReader
注释文档,其规则如下
Properties 属性名使用场景(class) Bean类全称限定名(abstract)是否为抽象的 BeanDefinition(parent)指定 parent BeanDefinition 名称(lazy-init)是否为延迟初始化(ref)引用其他 Bean 的名称(scope)设置 Bean 的 scope 属性${n}n 表示第 n+1 个构造器参数
## 指定BeanClassuser.(class)=com.wojiushiwo.dto.User## 属性user.name=我就是我user.age=19
public class PropertiesBeanMetaDataConfigDemo { public static void main(String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); PropertiesBeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(beanFactory); ClassPathResource classPathResource=new ClassPathResource("META-INF/user.properties"); EncodedResource resource=new EncodedResource(classPathResource,"UTF-8"); int beanDefinitions = beanDefinitionReader.loadBeanDefinitions(resource); System.out.println("加载的BeanDefinition个数为:" + beanDefinitions); User user = beanFactory.getBean("user", User.class); System.out.println(user); }}
上面 Properties文件默认读写编码为ISO-8859-1 因此这种直接加载方式会出现中文乱码,可通过加载在加载资源时指定编码方式来解决
4、面向注解
比如@Autowired
、@Bean
、@Component
、@Configuration
等,这些在当下都比较常用不再赘述
XmlBeanDefinitionReader元信息解析 源码分析
下面就XmlBeanDefinitionReader
调用链中比较重要的地方进行分析
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try { //解析Xml文件生成Document,这里不再展开Document doc = doLoadDocument(inputSource, resource);// 解析Document 注册BeanDefinitionint count = registerBeanDefinitions(doc, resource);//省略日志打印return count;}catch (BeanDefinitionStoreException ex) {//... 异常及日志}}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //获取DefaultBeanDefinitionReaderBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();//获取IoC容器中 已经存在的BeanDefinition的个数int countBefore = getRegistry().getBeanDefinitionCount();//这里实际上执行解析Document文档树 注册BeanDefinitiondocumentReader.registerBeanDefinitions(doc, createReaderContext(resource));//返回此次加载的BeanDefinition个数return getRegistry().getBeanDefinitionCount() - countBefore;}
protected void doRegisterBeanDefinitions(Element root) {BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);// namespace=http://www.springframework.org/schema/beans 表示为默认命名空间 则使用默认的解析方式去解析元素,否则将采用NamespaceHandler去解析if (this.delegate.isDefaultNamespace(root)) { //获取profile属性,profile与Spring配置环境有关系String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);//如果配置了profileif (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); //如果当前Environment环境与profile不匹配 则流程结束if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {//省略日志return;}}} //解析xml前置操作preProcessXml(root);//解析xmlparseBeanDefinitions(root, this.delegate);//解析xml后置操作postProcessXml(root);this.delegate = parent;}
关于上面源码分析中profile以及NameSpace的理解 请看这里的XML
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" profile="dev"> <!-- 一些配置--></beans>
可以看出 xmlns所指代的就是namespace,而profile也可以配置在beans标签上,其中
http://www.springframework.org/schema/beans
表示默认命名空间。因为Spring允许自定义标签,所以通过是否为默认命名空间作为判断依据来选择使用不同的解析方式去解析标签
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {//如果是默认命名空间if (delegate.isDefaultNamespace(root)) {NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element) {Element ele = (Element) node;if (delegate.isDefaultNamespace(ele)) {parseDefaultElement(ele, delegate);}else { // 使用NamespaceHandler去解析标签,比如ContextNamespaceHandler去解析<context:xx>的标签等delegate.parseCustomElement(ele);}}}}else { // 使用NamespaceHandler去解析标签,比如ContextNamespaceHandler去解析<context:xx>的标签等delegate.parseCustomElement(root);}}
上面的代码逻辑,根据是否为默认命名空间从而选择不同的解析方式,自定义标签或非默认命名空间指令 需要继承NamespaceHandler
去实现自己的标签解析方式
非默认命名空间指令举例
<context:component-scan base-package="com.wojiushiwo"/><aop:xx></aop><context:component-scan base-package="com.wojiushiwo"/><aop:xx></aop>
这里直接看针对默认命名空间的解析代码
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //如果是import指令if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}//如果是alias指令else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {processAliasRegistration(ele);}//如果是bean指令else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {processBeanDefinition(ele, delegate);}else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);}}
针对bean指令
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {//使用BeanRegistry对BeanDefinition进行注册BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name " +bdHolder.getBeanName() + "", ele, ex);}// Send registration event.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}
我们再来梳理下主要流程:
1、解析Xml文件 生成Document
2、针对Document命名空间进行标签解析
默认命名空间 标签解析非默认命名空间或自定义标签 自行实现NamespaceHandler
去解析(Spring 内置了一些NamespaceHandler解析自定义标签)3、使用BeanRegistry对BeanDefinition进行注册
AnnotatedBeanDefinitionReader元信息解析 源码分析
AnnotatedBeanDefinitionReader
从名称上看它似乎与BeanDefinitionReader
有千丝万缕的关系,实质上二者没有关系。AnnotatedBeanDefinitionReader
主要是 对注解Bean进行解析的。
先举例说明下
@Configurationpublic class BeanInitializationDemo { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 注册 Configuration Class(配置类) applicationContext.register(BeanInitializationDemo.class); // 启动 Spring 应用上下文 applicationContext.refresh(); applicationContext.close(); }}
借助上面的例子我们看一下其调用流程
以上面的例子来看,AnnotationBeanDefinitionReader
的创建是在AnnotationConfigApplicationContext
构造函数中进行的。
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {private final AnnotatedBeanDefinitionReader reader;private final ClassPathBeanDefinitionScanner scanner;public AnnotationConfigApplicationContext() { //创建AnnotatedBeanDefinitionReaderthis.reader = new AnnotatedBeanDefinitionReader(this);this.scanner = new ClassPathBeanDefinitionScanner(this);}}
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// 获取BeanNameString beanName = definitionHolder.getBeanName();//注册bd到IoC容器registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// 如果bean存在别名,则将beanName与alias的关系也存起来String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {registry.registerAlias(beanName, alias);}}}
透过上面XmlBeanDefinitionReader
和AnnotationBeanDefinitionReader
对BeanDefinition的解析来看,最终BeanDefinition的注册都指向了BeanDefinitionReaderUtils.registerBeanDefinition
。我们先来大概看一下代码实现
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// 获取BeanNameString beanName = definitionHolder.getBeanName();//注册bd到IoC容器registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// 如果bean存在别名,则将beanName与alias的关系也存起来String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {registry.registerAlias(beanName, alias);}}}
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注盛行IT的更多内容!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。