Spring的Bean生命周期之BeanDefinition详解
作者:爱吃牛肉的大老虎
1 BeanDefinition
在spring bean创建过程 依赖 BeanDefinition 中的信息处理bean的生产。
BeanDefinition 是 Spring Framework 中定义 Bean 的配置元信息接口
在处理配置文件生成BeanDefinition主要经过:Spring Bean 读取解析配置信息、spring bean 注册阶段、Spring BeanDefinition 合并阶段
1.1 Spring Bean 读取解析配置信息
spring bean的配置信息分为:
- 面向资源
- XML配置的处理主要使用 :XmlBeanDefinitionReader
- Properties 资源配置:PropertiesBeanDefinitionReader
- 面向注解
- 面向注解 BeanDefinition 解析: AnnotatedBeanDefinitionReader
1.1.1 XML 配置的处理主要使用的事例
这里主要基于xml中的构造方法处理:
public class UserHolder { private User user; public UserHolder() { } public UserHolder(User user) { this.user = user; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String toString() { return "UserHolder{" + "user=" + user + '}'; } }
/** * 基于 XML 资源的依赖 Constructor 注入示例 */ public class XmlDependencyConstructorInjectionDemo { public static void main(String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); String xmlResourcePath = "classpath:/META-INF/dependency-constructor-injection.xml"; // 加载 XML 资源,解析并且生成 BeanDefinition beanDefinitionReader.loadBeanDefinitions(xmlResourcePath); // 依赖查找并且创建 Bean UserHolder userHolder = beanFactory.getBean(UserHolder.class); System.out.println(userHolder); } }
xml的配置文件
<?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="org.geekbang.thinking.in.spring.ioc.overview.domain.User"> <property name="id" value="1"/> <property name="name" value="测试"/> <property name="city" value="test"/> <property name="workCities" value="BEIJING,HANGZHOU"/> <property name="lifeCities"> <list> <value>BEIJING</value> <value>SHANGHAI</value> </list> </property> <property name="configFileLocation" value="classpath:/META-INF/user-config.properties"/> </bean> <bean id="superUser" class="org.geekbang.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user" primary="true"> <property name="address" value="杭州"/> </bean> <bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder"> <constructor-arg name="user" ref="superUser" /> </bean> </beans>
1.1.2 注解 BeanDefinition 解析示例
使用AnnotatedBeanDefinitionReader 来处理注解 的类生成BeanDefinition 并注入到容器中
public class AnnotatedBeanDefinitionParsingDemo { public static void main(String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 基于 Java 注解的 AnnotatedBeanDefinitionReader 的实现 AnnotatedBeanDefinitionReader beanDefinitionReader = new AnnotatedBeanDefinitionReader(beanFactory); int beanDefinitionCountBefore = beanFactory.getBeanDefinitionCount(); // 注册当前类(非 @Component class) beanDefinitionReader.register(AnnotatedBeanDefinitionParsingDemo.class); int beanDefinitionCountAfter = beanFactory.getBeanDefinitionCount(); int beanDefinitionCount = beanDefinitionCountAfter - beanDefinitionCountBefore; System.out.println("已增加加载 BeanDefinition 数量:" + beanDefinitionCount); // 普通的 Class 作为 Component 注册到 Spring IoC 容器后,通常 Bean 名称为 annotatedBeanDefinitionParsingDemo // Bean 名称生成来自于 BeanNameGenerator,注解实现 AnnotationBeanNameGenerator AnnotatedBeanDefinitionParsingDemo demo = beanFactory.getBean("annotatedBeanDefinitionParsingDemo", AnnotatedBeanDefinitionParsingDemo.class); System.out.println(demo); } }
1.2 spring bean 注册阶段
在步骤一中生成的 BeanDefinition 需要注入到容器中 BeanDefinition 注册接口:BeanDefinitionRegistry 注册BeanDefinition其实就是把 BeanDefinition信息放入map中
注册到一个map中
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
1.3 Spring BeanDefinition 合并阶段
在合并阶段主要解决Bean继承时子类合并父类公共属性问题:
可以把 父BeanDefinition的信息合并到子BeanDefinition中;合并中的BeanDefinition主要依靠下面三个子类实现的
- ChildBeanDefinition:要指定父类。实例化的时候一定要是 父BeanDefinition,永远只能作为一个子BeanDefinition。
- RootBeanDefinition: spring 2.5的首选 一般表示。 作为父beanDefinition出现也可以作为普遍的bd 但是不能设置父beanDefinition即 不能作为子bd。
- GenericBeanDefinition:spring 2.5 以后出现的 常用的 可以 替换 ChildBeanDefinition 但是不能替换 RootBeanDefinition。可以完成RootBeanDefinition和ChildBeanDefinition 两种的功能。
合并过程一般是把 GenericBeanDefinition的处理合并成 RootBeanDefinition。
1.3.1 BeanDefinition 合并示例
public class MergedBeanDefinitionDemo { public static void main(String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 基于 XML 资源 BeanDefinitionReader 实现 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); String location = "META-INF/dependency-lookup-context.xml"; // 基于 ClassPath 加载 XML 资源 Resource resource = new ClassPathResource(location); // 指定字符编码 UTF-8 EncodedResource encodedResource = new EncodedResource(resource, "UTF-8"); int beanNumbers = beanDefinitionReader.loadBeanDefinitions(encodedResource); System.out.println("已加载 BeanDefinition 数量:" + beanNumbers); // 通过 Bean Id 和类型进行依赖查找 User user = beanFactory.getBean("user", User.class); System.out.println(user); User superUser = beanFactory.getBean("superUser", User.class); System.out.println(superUser); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- <context:annotation-config/>--> <!-- <context:component-scan base-package="org.acme" />--> <!-- Root BeanDefinition 不需要合并,不存在 parent --> <!-- 普通 beanDefinition GenericBeanDefinition --> <!-- 经过合并后 GenericBeanDefinition 变成 RootBeanDefinition --> <bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User"> <property name="id" value="1"/> <property name="name" value="小马哥"/> <property name="city" value="HANGZHOU"/> <property name="workCities" value="BEIJING,HANGZHOU"/> <property name="lifeCities"> <list> <value>BEIJING</value> <value>SHANGHAI</value> </list> </property> <property name="configFileLocation" value="classpath:/META-INF/user-config.properties"/> </bean> <!-- 普通 beanDefinition GenericBeanDefinition --> <!-- 合并后 GenericBeanDefinition 变成 RootBeanDefinition,并且覆盖 parent 相关配置--> <!-- primary = true , 增加了一个 address 属性 --> <bean id="superUser" class="org.geekbang.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user" primary="true"> <property name="address" value="杭州"/> </bean> <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName" value="user"/> </bean> </beans>
执行结果。
我们可以看到 SuperUser中包括的 其父类 User的相关属性
已加载 BeanDefinition 数量:3
User{id=1, name='小马哥', city=HANGZHOU, workCities=[BEIJING, HANGZHOU], lifeCities=[BEIJING, SHANGHAI], configFileLocation=class path resource [META-INF/user-config.properties], company=null, context=null, contextAsText='null', beanName='user'}
SuperUser{address='杭州'} User{id=1, name='小马哥', city=HANGZHOU, workCities=[BEIJING, HANGZHOU], lifeCities=[BEIJING, SHANGHAI], configFileLocation=class path resource [META-INF/user-config.properties], company=null, context=null, contextAsText='null', beanName='superUser'}
1.3.2 源码分析
合并 BeanDefinition 操作在 AbstractBeanFactory中发doGetBean()方法中的 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
protected RootBeanDefinition getMergedBeanDefinition( String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException { synchronized (this.mergedBeanDefinitions) { RootBeanDefinition mbd = null; // Check with full lock now in order to enforce the same merged instance. if (containingBd == null) { mbd = this.mergedBeanDefinitions.get(beanName); } if (mbd == null) { if (bd.getParentName() == null) { // Use copy of given root bean definition. if (bd instanceof RootBeanDefinition) { mbd = ((RootBeanDefinition) bd).cloneBeanDefinition(); } else { mbd = new RootBeanDefinition(bd); } } else { // Child bean definition: needs to be merged with parent. BeanDefinition pbd; try { String parentBeanName = transformedBeanName(bd.getParentName()); if (!beanName.equals(parentBeanName)) { //TODO 这里这次在去合并父类的 父类 这里使用递归的处理 pbd = getMergedBeanDefinition(parentBeanName); } else { BeanFactory parent = getParentBeanFactory(); if (parent instanceof ConfigurableBeanFactory) { pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { throw new NoSuchBeanDefinitionException(parentBeanName, "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without an AbstractBeanFactory parent"); } } } catch (NoSuchBeanDefinitionException ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex); } // Deep copy with overridden values. //最终的返回对象 合并后的对象 mbd = new RootBeanDefinition(pbd); mbd.overrideFrom(bd); } // Set default singleton scope, if not configured before. if (!StringUtils.hasLength(mbd.getScope())) { mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON); } // A bean contained in a non-singleton bean cannot be a singleton itself. // Let's correct this on the fly here, since this might be the result of // parent-child merging for the outer bean, in which case the original inner bean // definition will not have inherited the merged outer bean's singleton status. if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) { mbd.setScope(containingBd.getScope()); } // Cache the merged bean definition for the time being // (it might still get re-merged later on in order to pick up metadata changes) if (containingBd == null && isCacheBeanMetadata()) { this.mergedBeanDefinitions.put(beanName, mbd); } } return mbd; } }
这一节我们简单的介绍了 spring BeanDefinition的处理过程。
到此这篇关于Spring的Bean生命周期之BeanDefinition详解的文章就介绍到这了,更多相关Bean生命周期之BeanDefinition内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!