积少成多!不知不觉 Spring 源码已经更到第五篇啦~java
看源码以前,要先会用功能,这是最基本的,由于在源码讲解中,默认你们已经熟知 Spring 基本用法了,若是还不熟悉 Spring 的基本用法,能够看一下松哥发布在 B 站上的免费入门视频:www.bilibili.com/video/BV1Wv…。spring
上篇文章和小伙伴们介绍了 Spring 源码中的 EntityResolver,这个是用来解决 XML 文件校验问题的。ide
接下来原本应该接着第二弹的 XML 文件解析流程继续往下走了,考虑到接下来咱们会涉及到一个重要的概念 BeanDefinition,而有的小伙伴对此可能还不熟悉,所以本文松哥就先来和你们捋一捋 BeanDefinition 是什么东西!测试
本文是 Spring 源码解读第五篇,阅读本系列前面文章有助于更好的理解本文:this
在 Spring 容器中,咱们普遍使用的是一个一个的 Bean,BeanDefinition 从名字上就能够看出是关于 Bean 的定义。spa
事实上就是这样,咱们在 XML 文件中配置的 Bean 的各类属性,这些属性不只仅是和对象相关,Spring 容器还要解决 Bean 的生命周期、销毁、初始化等等各类操做,咱们定义的关于 Bean 的生命周期、销毁、初始化等操做总得有一个对象来承载,那么这个对象就是 BeanDefinition。代理
XML 中定义的各类属性都会先加载到 BeanDefinition 上,而后经过 BeanDefinition 来生成一个 Bean,从这个角度来讲,BeanDefinition 和 Bean 的关系有点相似于类和对象的关系。code
要理解 BeanDefinition,咱们从 BeanDefinition 的继承关系开始看起。cdn
BeanDefinition 是一个接口,继承自 BeanMetadataElement 和 AttributeAccessor 接口。视频
咱们来看下 AttributeAccessor:
public interface AttributeAccessor {
void setAttribute(String name, @Nullable Object value);
@Nullable
Object getAttribute(String name);
@Nullable
Object removeAttribute(String name);
boolean hasAttribute(String name);
String[] attributeNames();
}
复制代码
这里定义了元数据的访问接口,具体的实现则是 AttributeAccessorSupport,这些数据采用 LinkedHashMap 进行存储。
这是 BeanDefinition 所继承的两个接口。接下来咱们来看下 BeanDefinition 接口:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
void setParentName(@Nullable String parentName);
@Nullable
String getParentName();
void setBeanClassName(@Nullable String beanClassName);
@Nullable
String getBeanClassName();
void setScope(@Nullable String scope);
@Nullable
String getScope();
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
void setDependsOn(@Nullable String... dependsOn);
@Nullable
String[] getDependsOn();
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
void setPrimary(boolean primary);
boolean isPrimary();
void setFactoryBeanName(@Nullable String factoryBeanName);
@Nullable
String getFactoryBeanName();
void setFactoryMethodName(@Nullable String factoryMethodName);
@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();
void setRole(int role);
int getRole();
void setDescription(@Nullable String description);
@Nullable
String getDescription();
ResolvableType getResolvableType();
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
@Nullable
String getResourceDescription();
@Nullable
BeanDefinition getOriginatingBeanDefinition();
}
复制代码
BeanDefinition 中的方法虽然多,可是结合咱们平时在 XML 中的配置,这些方法其实都很好理解:
<bean parent="">
配置。<bean class="">
配置。<bean lazy-init="">
配置。<bean depends-on="">
配置。<bean autowire-candidate="">
配置。<bean primary="">
配置。<bean factory-bean="">
配置,factory-bean 松哥在以前的入门视频中讲过,小伙伴们能够参考这里:www.bilibili.com/video/BV1Wv…。<bean factory-method="">
配置,再也不赘述。这个就是 BeanDefinition 的定义以及它里边方法的含义。
上面只是 BeanDefinition 接口的定义,BeanDefinition 还拥有诸多实现类,咱们也来大体了解下。
先来看一张继承关系图:
这么多实现类看着有点眼花缭乱,不过搞清楚了每个接口和类的做用,再看就很容易了。
AbstractBeanDefinition 是一个抽象类,它根据 BeanDefinition 中定义的接口提供了相应的属性,并实现了 BeanDefinition 中定义的一部分方法。BeanDefinition 中本来只是定义了一系列的 get/set 方法,并无提供对应的属性,在 AbstractBeanDefinition 中将全部的属性定义出来了。
后面其余的实现类也基本上都是在 AbstractBeanDefinition 的基础上完成的。
这是一个比较经常使用的实现类,对应了通常的元素标签。
可让子 BeanDefinition 定义拥有从父 BeanDefinition 那里继承配置的能力。
GenericBeanDefinition 是从 Spring2.5 之后新加入的 BeanDefinition 实现类。GenericBeanDefinition 能够动态设置父 Bean,同时兼具 RootBeanDefinition 和 ChildBeanDefinition 的功能。
表示注解类型 BeanDefinition,拥有获取注解元数据和方法元数据的能力。
使用了 @Configuration 注解标记配置类会解析为 AnnotatedGenericBeanDefinition。
理论讲了这么多,接下来咱们经过几行代码来实践下,验证一下咱们前面所说的对不对。
首先项目中添加 spring-context 依赖,以下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
复制代码
而后咱们来建立一个 User 类,以下:
public class User {
private String username;
private String address;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
复制代码
接下来咱们先来验证 RootBeanDefinition。咱们本身纯手工定义一个 RootBeanDefinition,而且将之注册到 Spring 容器中去。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username", "javaboy");
pvs.add("address", "www.javaboy.org");
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class, null, pvs);
ctx.registerBeanDefinition("user",rootBeanDefinition);
ctx.refresh();
User bean = ctx.getBean(User.class);
System.out.println(bean);
复制代码
MutablePropertyValues 是定义对象中的一个一个属性,构造 RootBeanDefinition 的时候,咱们传入了类名称和属性集合,最终把 rootBeanDefinition 注册到容器中去。剩下的事情由容器完成,而后咱们就能够从容器中获取到 User 对象了。
最终输出结果以下:
User{username='javaboy', address='www.javaboy.org'}
复制代码
看了这个例子,小伙伴们应该可以大体明白,咱们在 XML 中定义的各类属性,就是先被解析到 BeanDefinition 中,而后再注册到 Spring 容器中去,最后拿到咱们须要的 Bean。
ChildBeanDefinition 具备从父 Bean 继承数据的能力,咱们来看下这个怎么用。
首先新建一个 Person 类,Person 类在 User 类的基础上增长一个 nickname 属性,这样 Person 就能够继承到 User 的 username 和 address 两个属性的值了:
public class Person {
private String username;
private String address;
private String nickname;
@Override
public String toString() {
return "Person{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
}
复制代码
接下来自定义 ChildBeanDefinition:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username", "javaboy");
pvs.add("address", "www.javaboy.org");
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class, null, pvs);
ctx.registerBeanDefinition("user",rootBeanDefinition);
ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition("user");
childBeanDefinition.setBeanClass(Person.class);
childBeanDefinition.getPropertyValues().add("nickname", "江南一点雨");
ctx.registerBeanDefinition("person", childBeanDefinition);
ctx.refresh();
User user = ctx.getBean(User.class);
Person person = ctx.getBean(Person.class);
System.out.println("user = " + user);
System.out.println("person = " + person);
复制代码
首先定义 RootBeanDefinition 并注册到 Spring 容器中,而后再定义 ChildBeanDefinition,ChildBeanDefinition 继承了 RootBeanDefinition 中现有的属性值。
最后咱们从 Spring 容器中获取 User 和 Person,打印结果以下:
user = User{username='javaboy', address='www.javaboy.org'}
person = Person{username='javaboy', address='www.javaboy.org', nickname='江南一点雨'}
复制代码
能够看到,Person 确实继承了 User 的属性值。
RootBeanDefinition 和 ChildBeanDefinition 均可以被 GenericBeanDefinition 代替,效果同样,以下:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username", "javaboy");
pvs.add("address", "www.javaboy.org");
GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
rootBeanDefinition.setBeanClass(User.class);
rootBeanDefinition.setPropertyValues(pvs);
ctx.registerBeanDefinition("user",rootBeanDefinition);
GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
childBeanDefinition.setParentName("user");
childBeanDefinition.setBeanClass(Person.class);
childBeanDefinition.getPropertyValues().add("nickname", "江南一点雨");
ctx.registerBeanDefinition("person", childBeanDefinition);
ctx.refresh();
User user = ctx.getBean(User.class);
Person person = ctx.getBean(Person.class);
System.out.println("user = " + user);
System.out.println("person = " + person);
复制代码
运行结果以下:
user = User{username='javaboy', address='www.javaboy.org'}
person = Person{username='javaboy', address='www.javaboy.org', nickname='江南一点雨'}
复制代码
能够看到,和前面的运行效果一致。
在咱们本系列前面文章(Spring 源码第一篇开整!配置文件是怎么加载的?)的案例中,默认使用的也是 GenericBeanDefinition,以下:
如今 Spring Boot 普遍流行以后,Java 配置使用愈来愈多,以 @Configuration 注解标记配置类会被解析为 AnnotatedGenericBeanDefinition;以 @Bean 注解标记的 Bean 会被解析为 ConfigurationClassBeanDefinition。
咱们新建一个 MyConfig 配置类,以下:
@Configuration
public class MyConfig {
@Bean
User user() {
return new User();
}
}
复制代码
查看获取到的 BeanDefinition 结果以下:
而其余 @Service、@Controller、@Repository 以及 @Component 等注解标记的 Bean 则会被识别为 ScannedGenericBeanDefinition。这个我就不一一演示了,小伙伴们能够自行测试哦。
好啦,今天主要是和小伙伴们介绍一下 Spring 中的 BeanDefinition。经过上面的原理+案例,相信小伙伴们已经明白,咱们经过 XML 或者 Java 注解配置的 Bean,咱们定义的东西会先被解析成 BeanDefinition,而后再经过 BeanDefinition 来生成咱们所须要的 Bean。
下篇文章咱们就来看这个 BeanDefinition 究竟是怎么从 XML 文件中生成的。
好啦,小伙伴们若是以为有收获,记得点个在看鼓励下松哥哦~