版本说明
本文的Spring版本为5.2.12.RELEASE
什么是BeanDefinition
当我们在spring中表示一个Bean时,我们通常会使用XML配置文件或者注解等方式来表示。通过配置文件或者注解中的信息,容器就能按照我们的定义来创建Bean,并为之提供不同的特性。这些XML或者注解中的信息最后都将被解析成对应的BeanDefinition,容器在创建这些Bean时,它的依据就是BeanDefinition中的信息。如果使用现实生活中的例子来说明,BeanDefinition就好像产品的设计图,而容器就是工厂,工厂(spring)按照产品的设计图(BeanDefinition)来生产产品(Bean实例)。例如下面这段XML文件中的内容,我们定义一个Book实例如下:
<bean id="book" class="com.buydeem.model.Book">
<property name="bookName" value="钢铁是怎样炼成的"/>
<property name="author" value="尼古拉·奥斯特洛夫斯基"/>
</bean>
Spring在处理上面的内容时会将使用BeanDefinition来保存其中的信息。当然Spring中还有其他实现,例如:注解、properties文件等等。
public class BeanDefinitionDemo {
public static void main(String[] args){
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("spring-bean-definition.xml");
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("book");
System.out.println(beanDefinition);
}
}
上面就是实例就是读取spring-bean-definition.xml文件中的内容将其转化成BeanDefinition。
BeanDefinition包含哪些信息
在Spring的官方文档中Bean Overview中有有详细说明。主要包括以下内容:
Property | Explained in… |
---|---|
Class | Instantiating Beans |
Name | Naming Beans |
Scope | Bean Scopes |
Constructor arguments | Dependency Injection |
Properties | Dependency Injection |
Autowiring mode | Autowiring Collaborators |
Lazy initialization mode | Lazy-initialized Beans |
Initialization method | Initialization Callbacks |
Destruction method | Destruction Callbacks |
从上面可以看出,我们平常在使用XML或者注解时定义的内容都包括在上面的表格中。
如何创建BeanDefinition
不管是读取XML文件还是注解等方式构建BeanDefinition,最后底层都是基于API方式来创建BeanDefinition实例。下面使用读取XML和Builder模式构建BeanDefinition:
public class BeanDefinitionDemo {
public static void main(String[] args){
//xml构建
xmlBeanDefinition();
//builder构建
builderBeanDefinition();
}
public static void xmlBeanDefinition(){
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("spring-bean-definition.xml");
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("book");
System.out.println(beanDefinition);
Book book = beanFactory.getBean(Book.class);
System.out.println(book);
}
public static void builderBeanDefinition(){
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Book.class)
.addPropertyValue("bookName","钢铁是怎样炼成的")
.addPropertyValue("author","尼古拉·奥斯特洛夫斯基")
.getBeanDefinition();
System.out.println(beanDefinition);
beanFactory.registerBeanDefinition("user",beanDefinition);
System.out.println(beanFactory.getBean("user"));
}
}
最后的结果如下:
Generic bean: class [com.buydeem.model.Book]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [spring-bean-definition.xml]
Book{bookName='钢铁是怎样炼成的', author='尼古拉·奥斯特洛夫斯基'}
Generic bean: class [com.buydeem.model.Book]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
Book{bookName='钢铁是怎样炼成的', author='尼古拉·奥斯特洛夫斯基'}
构建的BeanDefinition的方式很多,不仅仅值限于上面的方式。
如何注册BeanDefinition
创建Bean需要BeanDefinition,解析完配置或者注解生成的BeanDefinition最后会被注册。BeanDefinitionRegistry就是最底层的关于BeanDefinition注册相关的接口。该接口主要提供了对BeanDefinition管理相关的方法,简单的说就是定义了增删查等方法。常见的IOC容器实现类几乎都直接或者间接的实现了该接口。这也说明IOC容器具有管理BeanDefinition的功能。
public interface BeanDefinitionRegistry extends AliasRegistry {
//注册BeanDefinition
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
//删除BeanDefinition
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
//获取BeanDefinition
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
//是否包含BeanDefinition
boolean containsBeanDefinition(String beanName);
//获取BeanDefinition的名字数组
String[] getBeanDefinitionNames();
//获取已经注册的BeanDefinition数量
int getBeanDefinitionCount();
//确定给定的bean名称是否被使用
boolean isBeanNameInUse(String beanName);
}
常见的实现
RootBeanDefinition和ChildBeanDefinition
这两个实现通常可以搭配使用,从而在一定程度上减少重复配置。例如现在我们有一个Mike类,该类的定义如下:
public class Mike {
private String color;
private String source;
private String country;
}
现在我们需要创建多个Mike类型的实例,而这些实例中大多数属性都是一致的,只有少部分属性是特殊的。如下面spring-root-child-bean-definition.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="root" abstract="true">
<property name="color" value="白色"/>
<property name="source" value="牛"/>
</bean>
<!--子模板继承父模板-->
<bean id="yili" class="com.buydeem.model.Mike" parent="root">
<property name="country" value="中国"/>
</bean>
<!--子模板继承父模板-->
<bean id="quecao" class="com.buydeem.model.Mike" parent="root">
<property name="country" value="美国"/>
</bean>
</beans>
在上面的XML配置文件中,我们定义了两个实例Bean,分别为yili和quecao。我们定义中它的颜色和来源都是一样的,但是品牌的国籍不同,一个来自中国一个来自美国。使用测试代码如下:
public static void xmlRootAndChildBeanDefinition(){
//创建IOC容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//读取配置文件
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("spring-root-child-bean-definition.xml");
//获取Bean
Mike yiliMike = (Mike) beanFactory.getBean("yili");
Mike quecaoMike = (Mike) beanFactory.getBean("quecao");
System.out.println(yiliMike);
System.out.println(quecaoMike);
}
上面的方式是使用XML配置的,我们同样是可以使用API来完成上面XML的配置,代码如下:
public static void apiRootAndChildBeanDefinition(){
//创建容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//创建Root模板BeanDefinition
MutablePropertyValues rootProperties = new MutablePropertyValues()
.add("color","白色")
.add("source","牛");
RootBeanDefinition root = new RootBeanDefinition();
root.setPropertyValues(rootProperties);
root.setAbstract(true);
beanFactory.registerBeanDefinition("root",root);
//创建yili
MutablePropertyValues yiliProperties = new MutablePropertyValues()
.add("country","中国");
ChildBeanDefinition yiliBeanDefinition = new ChildBeanDefinition("root", Mike.class,null,yiliProperties);
beanFactory.registerBeanDefinition("yili",yiliBeanDefinition);
//创建quecao
MutablePropertyValues quecaoProperties = new MutablePropertyValues()
.add("country","美国");
ChildBeanDefinition quecaoBeanDefinition = new ChildBeanDefinition("root", Mike.class,null,quecaoProperties);
beanFactory.registerBeanDefinition("quecao",quecaoBeanDefinition);
//获取Bean
Mike yiliMike = (Mike) beanFactory.getBean("yili");
Mike quecaoMike = (Mike) beanFactory.getBean("quecao");
System.out.println(yiliMike);
System.out.println(quecaoMike);
}
GenericBeanDefinition
该类是Spring2.5之后推出的一个实现类,他既可以当做普通BeanDefinition来使用,同样也能当做RootBeanDefinition来使用。
ConfigurationClassBeanDefinition
该类是ConfigurationClassBeanDefinitionReader中的一个内部类,我们在spring中使用@Bean注解来定义Bean时生成的BeanDefinition就是这种类型,代码如下:
public static void configurationClassBeanDefinition(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//BeanDefinitionDemo类为配置类
context.register(BeanDefinitionDemo.class);
context.refresh();
Mike yili = (Mike) context.getBean("yili");
System.out.println(yili);
BeanDefinition definition = context.getBeanDefinition("yili");
System.out.println(definition.getClass());
}
@Bean
public Mike yili(){
Mike mike = new Mike();
mike.setColor("白色");
mike.setSource("牛");
mike.setCountry("中国");
return mike;
}
AnnotatedGenericBeanDefinition
从名称可以看出处理注解相关的BeanDefinition,例如我们下面的MyComponent类中,我们可以设置它的名称,scope,是不是primary等等信息。
public static void annotatedGenericBeanDefinition(){
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory);
reader.register(MyComponent.class);
MyComponent myComponent1 = beanFactory.getBean(MyComponent.class);
MyComponent myComponent2 = beanFactory.getBean(MyComponent.class);
System.out.println(myComponent1 == myComponent2);
BeanDefinition definition = beanFactory.getBeanDefinition("myComponent_1");
System.out.println(definition.getClass());
System.out.println(definition.getScope());
}
其中MyComponent类的定义如下:
@Component(value = "myComponent_1")
@Primary
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MyComponent{
}
最后的打印结果如下所示:
false
class org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
prototype
ScannedGenericBeanDefinition
通过名称就能知道该BeanDefinition与扫描相关。我们在spring 中可以通过指定包名,然后通过扫描的方式将@Component,@Service,@Repository等相关注解标记的类注册成Spring Bean。直接看下面代码:
BookDao定义如下:
package com.buydeem.component;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao {
}
BookService定义如下:
package com.buydeem.component;
import org.springframework.stereotype.Service;
@Service
public class BookService {
}
示例代码如下:
public static void scannedGenericBeanDefinition() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
Set<BeanDefinition> beanDefinitions = provider.findCandidateComponents("com.buydeem.component");
for (BeanDefinition beanDefinition : beanDefinitions) {
System.out.println(beanDefinition.getBeanClassName() + " = " + beanDefinition.getClass());
}
}
打印结果如下:
com.buydeem.component.BookDao = class org.springframework.context.annotation.ScannedGenericBeanDefinition
com.buydeem.component.BookService = class org.springframework.context.annotation.ScannedGenericBeanDefinition
总结
上面的东西看完之后主要要明白下面几点:
- BeanDefinition就是用来描述一个Bean的特性的,容器创建Bean就是根据BeanDefinition中记录的信息来创建的。
- 定义BeanDefinition的相关配置文件或者注解最后都将转成的BeanDefinition。
- Spring中IOC容器基本上都具有对BeanDefinition的管理功能。
明白上面几点,有利于对spring中整体的了解,便于下一步学习。