共计 12305 个字符,预计需要花费 31 分钟才能阅读完成。
聚沙成塔!人不知; 鬼不觉 Spring 源码曾经间断更了两个月啦,视频也录制了不少了,对 Spring 源码剖析感兴趣的小伙伴戳这里哦 Spring 源码应该怎么学?~
明天咱们持续来看 Spring 源码中一个十分重要的概念:BeanDefinition。
1.BeanDefinition
在 Spring 容器中,咱们宽泛应用的是一个一个的 Bean,BeanDefinition 从名字上就能够看出是对于 Bean 的定义。
事实上就是这样,咱们在 XML 文件中配置的 Bean 的各种属性,亦或者用注解定义进去的 Bean 的各种属性,在真正生成 Bean 间接,咱们须要先对这些设置的属性进行解析,解析的后果须要有一个对象来承载,很显著,这个对象就是 BeanDefinition。
无论是通过 XML 中定义的 Bean 属性还是通过 Java 代码定义的 Bean 属性,都会先加载到 BeanDefinition 上,而后通过 BeanDefinition 来生成一个 Bean,从这个角度来说,BeanDefinition 和 Bean 的关系有点相似于类和对象的关系,BeanDefinition 是模板,Bean 是模板具体化之后的产物。
要了解 BeanDefinition,咱们从 BeanDefinition 的继承关系开始看起。
BeanDefinition 是一个接口,继承自 BeanMetadataElement 和 AttributeAccessor 接口。
- BeanMetadataElement:该接口只有一个办法 getSource,该办法返回 Bean 的起源。
- 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/Java 中的配置,这些办法其实都很好了解:
- 首先一开始定义了两个变量用来形容 Bean 是不是单例的,前面的 setScope/getScope 办法能够用来批改 / 获取 scope 属性。
- ROLE_xxx 用来形容一个 Bean 的角色,ROLE_APPLICATION 示意这个 Bean 是用户本人定义的 Bean;ROLE_SUPPORT 示意这个 Bean 是某些简单配置的撑持局部;ROLE_INFRASTRUCTURE 示意这是一个 Spring 外部的 Bean,通过 setRole/getRole 能够批改。
- setParentName/getParentName 用来配置 parent 的名称,这块可能有的小伙伴应用较少,这个对应着 XML 中的
<bean parent="">
配置,在之前的视频中松哥曾经和大家讲过了 Spring 中 parent 的应用了。 - setBeanClassName/getBeanClassName 这个就是配置 Bean 的 Class 全门路,对应 XML 中的
<bean class="">
配置。 - setLazyInit/isLazyInit 配置 / 获取 Bean 是否懒加载,这个对应了 XML 中的
<bean lazy-init="">
配置。 - setDependsOn/getDependsOn 配置 / 获取 Bean 的依赖对象,这个对应了 XML 中的
<bean depends-on="">
配置。 - setAutowireCandidate/isAutowireCandidate 配置 / 获取 Bean 是否是主动拆卸,对应了 XML 中的
<bean autowire-candidate="">
配置。 - setPrimary/isPrimary 配置 / 获取以后 Bean 是否为首选的 Bean,对应了 XML 中的
<bean primary="">
配置。 - setFactoryBeanName/getFactoryBeanName 配置 / 获取 FactoryBean 的名字,对应了 XML 中的
<bean factory-bean="">
配置,factory-bean 松哥在之前的视频中讲过,小伙伴们能够参考这里:Spring 源码应该怎么学?。 - setFactoryMethodName/getFactoryMethodName 和上一条成对呈现的,对应了 XML 中的
<bean factory-method="">
配置,不再赘述。 - getConstructorArgumentValues 返回该 Bean 构造方法的参数值。
- hasConstructorArgumentValues 判断上一条是否是空对象。
- getPropertyValues 这个是获取一般属性的汇合。
- hasPropertyValues 判断上一条是否为空对象。
- setInitMethodName/setDestroyMethodName 配置 Bean 的初始化办法、销毁办法。
- setDescription/getDescription 配置 / 返回 Bean 的形容。
- isSingleton Bean 是否为单例。
- isPrototype Bean 是否为原型。
- isAbstract Bean 是否形象。
- getResourceDescription 返回定义 Bean 的资源形容。
- getOriginatingBeanDefinition 如果以后 BeanDefinition 是一个代理对象,那么该办法能够用来返回原始的 BeanDefinition。
这个就是 BeanDefinition 的定义以及它里边办法的含意。
2.BeanDefinition 实现类
下面只是 BeanDefinition 接口的定义,BeanDefinition 还领有诸多实现类,咱们也来大抵理解下。
先来看一张继承关系图:
这么多实现类看着有点目迷五色,不过搞清楚了每一个接口和类的作用,再看就很容易了。
2.1 AbstractBeanDefinition
AbstractBeanDefinition 是一个抽象类,它依据 BeanDefinition 中定义的接口提供了相应的属性,并实现了 BeanDefinition 中定义的一部分办法。BeanDefinition 中本来只是定义了一系列的 get/set 办法,并没有提供对应的属性,在 AbstractBeanDefinition 中将所有的属性定义进去了。
前面其余的实现类也基本上都是在 AbstractBeanDefinition 的根底上实现的。
2.2 RootBeanDefinition
这是一个比拟罕用的实现类,对应了个别的元素标签。
2.3 ChildBeanDefinition
能够让子 BeanDefinition 定义领有从父 BeanDefinition 那里继承配置的能力,如果子 Bean 从父 Bean 获取配置,能够参考松哥之前的这篇文章:Spring BeanDefinition 也分父子?。
2.4 GenericBeanDefinition
GenericBeanDefinition 是从 Spring2.5 当前新退出的 BeanDefinition 实现类。GenericBeanDefinition 能够动静设置父 Bean,同时兼具 RootBeanDefinition 和 ChildBeanDefinition 的性能,因而,自从有了 GenericBeanDefinition 之后,RootBeanDefinition 和 ChildBeanDefinition 当初绝对就用的少了。
2.5 AnnotatedBeanDefinition
这个示意注解类型 BeanDefinition,用于示意通过注解配置的 Bean 定义。通过 AnnotatedBeanDefinition,咱们能够获取到被注解的 Bean 的相干信息,包含注解类型、属性值、办法等。这个接口提供了一种不便的形式来解决通过注解形式配置的 Bean,并且能够在运行时动静地获取和操作这些注解信息,当然,这是一个接口,它有三个实现类,别离是 AnnotatedGenericBeanDefinition、ScannedGenericBeanDefinition 以及 ConfigurationClassBeanDefinition。
2.6 AnnotatedGenericBeanDefinition
作为系统配置类的类会解析为 AnnotatedGenericBeanDefinition。
2.7 ScannedGenericBeanDefinition
这个是通过包扫描主动注册的 Bean,就会被解析为 ScannedGenericBeanDefinition。
2.8 ConfigurationClassBeanDefinition
这是一个公有的外部类。咱们通过 @Bean 注解定义的 Bean,最终会被解析为 ConfigurationClassBeanDefinition。
2.9 ClassDerivedBeanDefinition
ClassDerivedBeanDefinition 的作用是扩大并形容一个类派生的 Bean 的元数据。它是 AnnotatedBeanDefinition 接口的一个实现类,在 Spring 框架中用于示意通过类派生形式配置的 Bean 定义。
2.10 CreateFromClassBeanDefinition
这个是依照类型创立 Bean 的时候会用到。
差不多就这么多了,大部分咱们日常开发中其实都用不上,接下来松哥通过几个具体的案例来和小伙伴们演示这些 BeanDefinition 的具体用法。前面的文章,咱们再来剖析这些 BeanDefinition 在 Spring 源码中是如何利用的。
3. 实际
接下来我通过几个具体的案例来和小伙伴们演示各种不同的 BeanDefinition 的用法,明天我次要和小伙伴们演示咱们纯手动应用 BeanDefinition,而后剖析一下咱们平时的配置实质上应用的 BeanDefinition 是哪一个,明天咱们先不去源码剖析,单纯的就看看成果。
3.1 GenericBeanDefinition
先来看 GenericBeanDefinition,这个性能绝对比拟全,兼具 RootBeanDefinition 和 ChildBeanDefinition 的能力。
先来看一个简略用法:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(User.class);
MutablePropertyValues pValues = new MutablePropertyValues();
pValues.add("username", "javaboy");
bd.setPropertyValues(pValues);
beanFactory.registerBeanDefinition("user", bd);
User user = beanFactory.getBean("user", User.class);
System.out.println("user =" + user);
小伙伴们看到,咱们这里向 Spring 容器注册了一个 GenericBeanDefinition 类型的 BeanDefinition,GenericBeanDefinition 中蕴含了具体的 class 以及 Bean 的各个属性值。
如果咱们在 Bean 定义的时候,想要应用继承个性,也能够应用 GenericBeanDefinition:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition parentBD = new GenericBeanDefinition();
GenericBeanDefinition childBD = new GenericBeanDefinition();
parentBD.setBeanClass(Animal.class);
MutablePropertyValues pValues = new MutablePropertyValues();
pValues.add("name", "小黄");
parentBD.setPropertyValues(pValues);
childBD.setBeanClass(Dog.class);
childBD.setParentName("animal");
beanFactory.registerBeanDefinition("animal", parentBD);
beanFactory.registerBeanDefinition("dog", childBD);
Dog dog = beanFactory.getBean("dog", Dog.class);
System.out.println("dog =" + dog);
这次我间接定义了两个 GenericBeanDefinition,一个作为 parent,另外一个作为 child,为 child 设置 parentName,则 child 能够继承 parent 中的属性。下面的案例中,最终打印进去 dog 的 name 属性就是 小黄 ,这块小伙伴们能够参考松哥之前的文章:Spring BeanDefinition 也分父子?。
咱们平时通过 XML 文件定义的 Bean,最终解析后就是 GenericBeanDefinition。
例如上面这样:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.javaboy.demo.User" id="user"></bean>
</beans>
加载 XML 文件,创立容器:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {BeanDefinition bd = ctx.getBeanFactory().getBeanDefinition(beanDefinitionName);
System.out.println(beanDefinitionName + ">>>" + bd.getClass());
}
最终打印后果如下:
这个也好了解,毕竟咱们在 XML 中配置的时候,可能存在 parent,也可能不存在,用 GenericBeanDefinition 就可能应答各种状况。
3.2 RootBeanDefinition/ChildBeanDefinition
这两个惯例的性能其实都有,然而 RootBeanDefinition 个别能够用来做 parent,不能用作 child,即给 RootBeanDefinition 不能配置 parentName 属性。强行设置会抛出如下异样:
@Override
public void setParentName(@Nullable String parentName) {if (parentName != null) {throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
}
}
ChildBeanDefinition 则既能够做 parent 也能够做 child,然而 ChildBeanDefinition 在应用的应用必须指定 parent,即便 ChildBeanDefinition 作为 parent,也必须指定 parent,所以 ChildBeanDefinition 在应用的过程中有一点点局限性,因而目前被 GenericBeanDefinition 代替了。
来看一个简略的案例:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
RootBeanDefinition parentBD = new RootBeanDefinition();
parentBD.setBeanClass(Animal.class);
MutablePropertyValues pValues = new MutablePropertyValues();
pValues.add("name", "小黄");
parentBD.setPropertyValues(pValues);
ChildBeanDefinition childBD = new ChildBeanDefinition("animal");
childBD.setBeanClass(Dog.class);
beanFactory.registerBeanDefinition("animal", parentBD);
beanFactory.registerBeanDefinition("dog", childBD);
Dog dog = beanFactory.getBean("dog", Dog.class);
System.out.println("dog =" + dog);
3.3 AnnotatedGenericBeanDefinition
对于应用 @Configuration 注解标记的类,最终解析进去的 BeanDefinition 就是 AnnotatedGenericBeanDefinition。例如我有一个配置类如下:
@Configuration
public class JavaConfig {}
加载配置类并启动容器:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {BeanDefinition beanDefinition = ctx.getBeanFactory().getBeanDefinition(beanDefinitionName);
System.out.println(beanDefinitionName + ">>>" + beanDefinition.getClass());
}
最终打印后果如下:
3.4 ScannedGenericBeanDefinition
这个是那些通过包扫描注册到 Spring 容器中的 Bean,在一开始定义进去的 BeanDefinition 就是 ScannedGenericBeanDefinition。
例如我有如下 Bean:
@Service
public class UserService {}
而后配置包扫描:
@Configuration
@ComponentScan
public class JavaConfig {}
启动容器:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {BeanDefinition beanDefinition = ctx.getBeanFactory().getBeanDefinition(beanDefinitionName);
System.out.println(beanDefinitionName + ">>>" + beanDefinition.getClass());
}
最终打印后果如下:
3.5 ConfigurationClassBeanDefinition
当咱们通过 @Bean 注解去定义 Bean 的时候,那么被 @Bean 注解标记的类就会被解析为 ConfigurationClassBeanDefinition。
例如上面这个例子:
@Configuration
public class JavaConfig {
@Bean
User user() {return new User();
}
}
查看解析后的 BeanDefinition 如下:
3.6 CreateFromClassBeanDefinition
这个其实用的少,然而咱么既然讲到 Spring,松哥也说两句。
这个是当咱们想要创立一个对象,咱们心愿这个对象可能主动走一遍 Spring 中的各种后置处理器的时候,那么能够调用 createBean 办法,该办法外部应用了 CreateFromClassBeanDefinition。
例如如下代码:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
User user = beanFactory.createBean(User.class);
System.out.println("user =" + user);
应用这种形式去创立一个 Bean,这个 Bean 会走一遍 Spring 中 Bean 的后置处理器,其中,createBean 办法的外部就应用了 CreateFromClassBeanDefinition。
3.7 ClassDerivedBeanDefinition
ClassDerivedBeanDefinition 和 CreateFromClassBeanDefinition 其实比拟像,差异在于二者解决构造方法的形式不同。
而且 ClassDerivedBeanDefinition 是一个相当冷门的 BeanDefinition,在 GenericApplicationContext 的实现类中,能够应用 GenericXmlApplicationContext、StaticApplicationContext 或者 GenericGroovyApplicationContext,只有这三个类中 registerBean 办法用到了 ClassDerivedBeanDefinition,咱们常见的 AnnotationConfigApplicationContext 因为办法重写的缘故并未应用 ClassDerivedBeanDefinition。
StaticApplicationContext ctx = new StaticApplicationContext();
ctx.registerBean(User.class,()->{User user = new User();
user.setUsername("javaboy");
return user;
});
User user = ctx.getBean(User.class);
System.out.println("user =" + user);
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {BeanDefinition bd = ctx.getBeanDefinition(beanDefinitionName);
System.out.println(beanDefinitionName + ">>>" + bd.getClass());
}
咱们能够调用 registerBean 办法向 Spring 容器中注入一个 Bean,该办法第二个参数是一个 Bean 的生产者,如果不指定生产者,那么这个办法最终就是通过第一个参数反射创立 Bean,registerBean 办法的外部就是应用了 ClassDerivedBeanDefinition。
好啦,BeanDefinition 一共就是这七种,接下来我会通过几篇文章和大家重点介绍 GenericBeanDefinition、AnnotatedGenericBeanDefinition、ScannedGenericBeanDefinition 以及 ConfigurationClassBeanDefinition 这四种最为常见的 BeanDefinition。