注:本系列源码分析基于spring 5.2.2.RELEASE,本文的分析基于 annotation 注解方式,gitee仓库链接:gitee.com/funcy/sprin….
1. 什么是 BeanDefinition
BeanDefinition 从名称上来看,就是bean定义,用来定义 spring bean 的信息。
在 java 中,定义一个类的元信息(构造方法,成员变量,成员方法等),使用的是Class类,一个.class文件加载到jvm后,都会生成一个Class对象,在对象实例化时,就根据这个Class对象的信息来生成。
在 spring 中,也有这么一个类来定义 bean 的信息,这个类就是 BeanDefinition,它定义了spring bean如何生成,如何初始化,如何销毁等,我们来看看它支持的部分方法:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
* 设置父 BeanDefinition
* BeanDefinition 有个类似继承的概念,这里指定了父BeanDefinition
* 实例化bean时,会合并父BeanDefinition
*/
void setParentName(@Nullable String parentName);
/**
* 获取父Bean
*/
@Nullable
String getParentName();
/**
* 设置beanClass名称
* 实例化时,会实例化成这个 Class 的对象
*/
void setBeanClassName(@Nullable String beanClassName);
/**
* 获取beanClass名称
*/
@Nullable
String getBeanClassName();
/**
* 设置bean的作用范围,单例或原型
*/
void setScope(@Nullable String scope);
/**
* 获取bean的作用范围,单例或原型
*/
@Nullable
String getScope();
/**
* 设置懒加载
*/
void setLazyInit(boolean lazyInit);
/**
* 是否为懒加载
*/
boolean isLazyInit();
/**
* 设置该Bean依赖的所有Bean
* 即 @DependsOn 指定的bean
*/
void setDependsOn(@Nullable String... dependsOn);
/**
* 返回该Bean的所有依赖。
*/
@Nullable
String[] getDependsOn();
/**
* 设置是否作为自动注入的候选对象。
*/
void setAutowireCandidate(boolean autowireCandidate);
/**
* 是否作为自动注入的候选对象
*/
boolean isAutowireCandidate();
/**
* 设置是否为主要的,当容器中存在多个同类型的bean时,可以只返回主要的
* 就是 @Primary 的作用
*/
void setPrimary(boolean primary);
/**
* 是否为主要的bean
*/
boolean isPrimary();
/**
* 针对factoryBean
* 指定factoryBean的名称
*/
void setFactoryBeanName(@Nullable String factoryBeanName);
/**
* 针对factoryBean
* 获取factoryBean的名称
*/
@Nullable
String getFactoryBeanName();
/**
* 设置工厂方法的名称
* 例如 @Bean 标记的方法
*/
void setFactoryMethodName(@Nullable String factoryMethodName);
/**
* 返回工厂方法的名称
* 例如 @Bean 标记的方法
*/
@Nullable
String getFactoryMethodName();
/**
* 获取构造就去的参数值
*/
ConstructorArgumentValues getConstructorArgumentValues();
/**
* 构造方法是否有参数
*/
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
/**
* 获取属性值
* 这里可以自主设置值作为构造方法、工厂方法的参数
*/
MutablePropertyValues getPropertyValues();
/**
* 是否有属性值
*/
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
/**
* 设置初始化方法名称
*/
void setInitMethodName(@Nullable String initMethodName);
/**
* 获取初始化方法名称
*/
@Nullable
String getInitMethodName();
/**
* 设置销毁方法名称
*/
void setDestroyMethodName(@Nullable String destroyMethodName);
/**
* 获取销毁方法名称
*/
@Nullable
String getDestroyMethodName();
/**
* 是否为单例bean
*/
boolean isSingleton();
/**
* 是否为原型bean
*/
boolean isPrototype();
/**
* 如果这个 Bean 是被设置为 abstract,那么不能实例化,常用于作为 父bean 用于继承
*/
boolean isAbstract();
...
}
可以看到,BeanDefinition支持的方法非常多,有很多是我们平时使用时指定的:
setScope(...):设置bean的作用范围,由@Scope指定setLazyInit(...):设置懒加载,由@Lazy指定setDependsOn(...):设置bean依赖,由@DependsOn指定setPrimary(...):设置为主要bean,由@Primary指定setFactoryMethodName(...):设置工厂方法名称,例如由@Bean标记的方法
以上是在注解时代可以指定的,还有些是在xml时代指定的,如:
setInitMethodName(...):设置初始化方法 ,由init-method指定setDestroyMethodName(...):设置销毁方法,由destroy-method指定
2. spring 提供了哪些BeanDefinition
BeanDefinition 是一个接口,我们当然不能直接使用,接下来我们来看看Spring提供了哪些BeanDefinition:
spring提供的BeanDefinition基本就是上图所示的几种了,这里我们主要看这向种:
RootBeanDefinitionChildBeanDefinitionGenericBeanDefinitionScannedGenericBeanDefinitionAnnotatedGenericBeanDefinition
2.1 RootBeanDefinition 和 ChildBeanDefinition
在前面提到了BeanDefinition继子的概念,这里就是用来处理继承的,一般来说,我们可以在RootBeanDefinition定义公共参数,然后在ChildBeanDefinition中定义各自的内容,示例如下:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// RootBeanDefinition
RootBeanDefinition root = new RootBeanDefinition();
root.setBeanClass(User.class);
root.getPropertyValues().add("name", "123");
// 这种注册方法仅做展示,实际项目中不建议使用
// 使用项目建议使用的 BeanDefinitionRegistryPostProcessor 提供的方法
context.registerBeanDefinition("root", root);
// ChildBeanDefinition
ChildBeanDefinition child1 = new ChildBeanDefinition("root");
child1.getPropertyValues().add("age", "11");
// 这种注册方法仅做展示,实际项目中不建议使用
// 使用项目建议使用的 BeanDefinitionRegistryPostProcessor 提供的方法
context.registerBeanDefinition("child1", child1);
// ChildBeanDefinition
ChildBeanDefinition child2 = new ChildBeanDefinition("root");
child2.getPropertyValues().add("age", "12");
// 这种注册方法仅做展示,实际项目中不建议使用
// 使用项目建议使用的 BeanDefinitionRegistryPostProcessor 提供的方法
context.registerBeanDefinition("child2", child2);
// 启动容器
context.refresh();
User rootUser = (User) context.getBean("root");
User child1User = (User) context.getBean("child1");
User child2User = (User) context.getBean("child2");
System.out.println(rootUser);
System.out.println(child1User);
System.out.println(child2User);
}
运行结果:
User{name='123', age=null}
User{name='123', age=11}
User{name='123', age=12}
可以看到,child1与child1进行成功地从RootBeanDefinition继承到了属性。
2.2 GenericBeanDefinition
这是个通用的BeanDefinition,直接继承了AbstractBeanDefinition,它自身提供的方法如下:
可以看到,它自身提供的方法并不多,其操作基本继承AbstractBeanDefinition,一般情况下,我们要生成自己的BeanDefinition 时,只需要使用这个类就可以了,这里也提供一个示例:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition();
userBeanDefinition.setBeanClass(User.class);
userBeanDefinition.getPropertyValues().add("name", "123");
userBeanDefinition.getPropertyValues().add("age", "11");
// 这种注册方法仅做展示,实际项目中不建议使用
// 使用项目建议使用的 BeanDefinitionRegistryPostProcessor 提供的方法
context.registerBeanDefinition("user", userBeanDefinition);
// 启动容器
context.refresh();
User user = (User) context.getBean("user");
System.out.println(user);
}
2.3 ScannedGenericBeanDefinition
ScannedGenericBeanDefinition 继承了 GenericBeanDefinition,同时也实现了 AnnotatedBeanDefinition接口,本身提供的方法并不多:
其操作基本来自GenericBeanDefinition,这里就不提供示例了。
2.4 AnnotatedGenericBeanDefinition
AnnotatedGenericBeanDefinition 继承了 GenericBeanDefinition,同时也实现了 AnnotatedBeanDefinition接口,本身提供的方法并不多:
其操作基本来自GenericBeanDefinition,这里就不提供示例了。
3. 操作spring容器中已有的BeanDefinition
上面的例子都是往spring容器中添加BeanDefinition,我们要如何操作spring容器中已有的BeanDefinition呢?
3.1 demo 准备
这里首先准备一个demo:
首先准备两个service:
@Service
public class Service01 {
private String name;
public void hello() {
System.out.println("hello " + name + ", from service01");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Service
public class Service02 {
private String name;
public void hello() {
System.out.println("hello " + name + ", from service02");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
接着是主要类:
@ComponentScan
public class Demo02Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext();
context.register(Demo02Main.class);
context.refresh();
Service01 service01 = (Service01) context.getBean("service01");
Service02 service02 = (Service02) context.getBean("service02");
service01.hello();
service02.hello();
}
}
运行 ,结果如下:
hello null, from service01
hello null, from service02
这说明我们的容器已经启动成功了,service01与service02也初始化成功了。
3.2 不成功的尝试
这里我们反推下,service01与service02已经初始化成功了,就说明容器中必然service01与service02对应的beanDefifnition,我们想操作这个beanDefifnition,就必须要先获取这个beanDefifnition。
如何获取spring中已经存在的beanDefifnition呢?参考第2节的示例,如果你认为在context.refresh()前获取,像这样:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Demo02Main.class);
// 在这里获取 beanDefinition,会报错
BeanDefinition service01Bd = context.getBeanDefinition("service01");
service01Bd.getPropertyValues().addPropertyValue("name", "123");
context.refresh();
Service01 service01 = (Service01) context.getBean("service01");
Service02 service02 = (Service02) context.getBean("service02");
service01.hello();
service02.hello();
}
运行,发现会报错:
Exception in thread "main" org.springframework.beans.factory
.NoSuchBeanDefinitionException: No bean named 'service01' available
聪明如你,一定会想到,在context.refresh()前获取会报错,那在之后呢?代码像这样:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Demo02Main.class);
context.refresh();
// 获取 beanDefinition,修改不起作用
BeanDefinition service01Bd = context.getBeanDefinition("service01");
service01Bd.getPropertyValues().addPropertyValue("name", "123");
Service01 service01 = (Service01) context.getBean("service01");
Service02 service02 = (Service02) context.getBean("service02");
service01.hello();
service02.hello();
}
运行,结果如下:
确实没有报错,但是我们的修改也没起作用。在代码里,我们给service01的name属性指定值为123,运行结果还是null,没起作用的原因是service01是在context.refresh()进行初始化 的,后面再怎么对它的BeanDefinition修改,也体现不到它身上。
那么究竟要怎么做呢?
3.2 BeanDefinitionRegistryPostProcessor:定制化 beanDefinition
接下来我们要放出大招了,这个大招就是:BeanFactoryPostProcessor,关于这个的介绍,可以它的介绍,可以参考spring组件之 BeanFactoryPostProcessor,这里直接给下结论:
BeanFactoryPostProcessor中文名叫spring beanFactory 的后置处理器,可以用来定制化beanFactory的一些行为。spring 为我们提供了两种BeanFactoryPostProcessor:
BeanFactoryPostProcessor:定制化beanFactory的行为BeanDefinitionRegistryPostProcessor:定制化beanDefinition的行为
很明显,我们应该使用BeanDefinitionRegistryPostProcessor,直接实现这个接口:
@Component
public class MyBeanDefinitionRegistryPostProcessor
implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
BeanDefinition service01Bd = registry.getBeanDefinition("service01");
service01Bd.getPropertyValues().addPropertyValue("name", "123");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
// BeanDefinitionRegistryPostProcessor 是 BeanFactoryPostProcessor 的子接口
// postProcessBeanFactory(...) 来自 BeanFactoryPostProcessor,我们这里不处理
}
}
main方法跟最初保持一致:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Demo02Main.class);
context.refresh();
Service01 service01 = (Service01) context.getBean("service01");
Service02 service02 = (Service02) context.getBean("service02");
service01.hello();
service02.hello();
}
运行,结果如下:
hello 123, from service01
hello null, from service02
可以看到 service01 的name确实变成123了。
实际上,BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry主要就是使用BeanDefinitionRegistry来完成BeanDefinition的操作,它支持的方法如下:
重要操作如下:
getBeanDefinition(...):获取BeanDefinition,存在则返回,不存在则报错,得到BeanDefinition后,就可以对其进行各种操作了registerBeanDefinition(...):注册BeanDefinition,可以自定义BeanDefinition对象,然后调用该方法注册到容器中,前面的例子中,我们是在context.refresh()前调用context.registerBeanDefinition,这里需要说的是忘了前面的方法吧,并不是所有的情况下,都能拿到context.refresh()的context(例如springboot中),因此推荐使用BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry方法注册BeanDefinitionremoveBeanDefinition(...):移除BeanDefinition,一般情况下应该不会用到containsBeanDefinition(...):判断是否包含某个BeanDefinition
4. 总结
本文主要介绍了BeanDefinition的作用:
BeanDefinition的方法- 几个类型的
BeanDefinition的使用 - 使用
BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry操作BeanDefinition,主要是注册、修改BeanDefinition
本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。
本系列的其他文章