写在前面
该文对spring初学者不友好,学习讨论spring-beans模块源码的大佬们请批评指正。
一、初识BeanDefinition
1、什么是BeanDefinition?
BeanDefinition 是 Spring Framework 中定义 Bean 的配置元信息接口,包含:
- Bean 的类名
- Bean 行为配置元素,如作用域、自动绑定的模式,生命周期回调等
- 其他 Bean 引用,又可称作合作者(collaborators)或者依赖(dependencies)
- 配置设置,比如 Bean 属性(Properties)
2、BeanDefinition元信息
属性(Property) | 说明 |
---|
Class Bean | 全类名,必须是具体类,不能用抽象类或接口 | Name | Bean 的名称或者 ID | Scope | Bean 的作用域(如:singleton、prototype 等) | Constructor arguments | Bean 构造器参数(用于依赖注入) | Properties | Bean 属性设置(用于依赖注入) | Role | 角色提示(用户bean、外部配置、后台创建) | primary | 是否是首要Bean | Autowiring mode | Bean 自动绑定模式(如:通过名称 byName) | Lazy initialization mode | Bean 延迟初始化模式(延迟和非延迟) | Initialization method | Bean 初始化回调方法名称 | Destruction method | Bean 销毁回调方法名称 |
3、手动构建BeanDefinition
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder
.addPropertyValue("id", 1)
.addPropertyValue("name", "张三");
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
beanDefinition.xxxx
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(User.class);
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues
.add("id", 1)
.add("name", "张三");
genericBeanDefinition.setPropertyValues(propertyValues);
二、Bean深入
1、Bean注册到IOC容器
XML配置元信息
<bean name=”...” ... />
Java注解配置元信息
@Bean(name = {"user", "ts-user"})
public User user() {
User user = new User();
user.setId(1L);
user.setName("张三");
return user;
}
@Component
public static class Config {
@Import(Config.class)
@Component
public class AnnotationBeanDefinitionDemo {
Java API配置元信息
1、配置类方式:AnnotatedBeanDefinitionReader#register(Class…)
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(AnnotationBeanDefinitionDemo.class);
applicationContext.refresh();
2、使用BeanDefinitionRegistry 注册
public static void registerUserBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder beanDefinitionBuilder = genericBeanDefinition(User.class);
beanDefinitionBuilder
.addPropertyValue("id", 1L)
.addPropertyValue("name", "张三");
if (StringUtils.hasText(beanName)) {
registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
} else {
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(), registry);
}
}
public static void registerUserBeanDefinition(BeanDefinitionRegistry registry) {
registerUserBeanDefinition(registry, null);
}
2、Bean命名
每个 Bean 拥有一个或多个标识符(identifiers),这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名(Alias)来扩充。
Bean 的 id 或 name 属性并非必须制定,如果留空的话,容器会为 Bean 自动生成一个唯一的名称。Bean 的命名尽管没有限制,不过官方建议采用驼峰的方式,更符合 Java 的命名约定。
自定义命名
在xml中我们可以使用id属性自定义命名: 每个bean的id(身份证号)都不同,但可以有相同的name (人名)。 但配置文件中允许出现两个name相同的<bean>,在用getBean()返回实例时,后面一个Bean被返回,应该是前面那 个<bean>被后面同名的<bean>覆盖了。有鉴于此,为了避免不经意的同名覆盖的现象,尽量用id属性而不要用name属性。
<bean id="user" class="com.cxf.demo.User">
<property name="id" value="1"/>
<property name="name" value="张三"/>
</bean>
使用注解时,我们可以使用value指定Bean的id:
@Bean("myId")
public User user() {
User user = new User();
user.setId(1L);
user.setName("张三");
return user;
}
自动命名
创建Bean的时候,其id或name属性并非必须,如果留空的话,容器会为Bean自动生成一个唯一的名称。
这个Bean的名称生成器接口就是BeanNameGenerator接口,它只有一个方法。
public interface BeanNameGenerator {
String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}
BeanNameGenerator接口有两个实现类,第一个是DefaultBeanNameGenerator,默认通用 BeanNameGenerator 实现,它使用以下方式生成一个Bean的名称。
BeanDefinitionReaderUtils.generateBeanName(definition, registry);
public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
String generatedBeanName = definition.getBeanClassName();
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
}
if (isInnerBean) {
return generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}
return uniqueBeanName(generatedBeanName, registry);
}
public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
String id = beanName;
int counter = -1;
String prefix = beanName + GENERATED_BEAN_NAME_SEPARATOR;
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = prefix + counter;
}
return id;
}
BeanNameGenerator接口第二个实现类是AnnotationBeanNameGenerator,基于注解扫描的 BeanNameGenerator 实现,起始于Spring Framework 2.5。
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) {
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
return beanName;
}
}
return buildDefaultBeanName(definition, registry);
}
@Nullable
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
AnnotationMetadata amd = annotatedDef.getMetadata();
Set<String> types = amd.getAnnotationTypes();
String beanName = null;
for (String type : types) {
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
if (attributes != null) {
Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {
Set<String> result = amd.getMetaAnnotationTypes(key);
return (result.isEmpty() ? Collections.emptySet() : result);
});
if (isStereotypeWithNameValue(type, metaTypes, attributes)) {
Object value = attributes.get("value");
if (value instanceof String) {
String strVal = (String) value;
if (StringUtils.hasLength(strVal)) {
if (beanName != null && !strVal.equals(beanName)) {
throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
"component names: '" + beanName + "' versus '" + strVal + "'");
}
beanName = strVal;
}
}
}
}
}
return beanName;
}
别名
bean的别名通常用于场景化的情况。
1、使用xml方式为指定bean创建别名
<alias name="user" alias="new-user" />
<bean id="app:dataSource" class="...">
<alias name="app:dataSoure" alias="user:dataSoure"/>
<alias name="app:dataSoure" alias="device:dataSoure"/>
</bean>
2、使用代码方式:
@Component
public class AliasConfiguration implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerAlias("originalBeanName", "newAlias");
beanFactory.registerAlias("originalBeanName", "newAlias2");
beanFactory.registerAlias("otherOriginalBeanName", "newAlias3");
}
}
指定别名后,与id一样用。
3、使用注解方式指定多个名称
@Bean(name = {"user", "my-user"})
public User user() {
User user = new User();
user.setId(1L);
user.setName("张三");
return user;
}
3、Bean实例化
通过构造器、GETSET(配置元信息:XML、Java 注解和 Java API )
<bean id="user" class="com.demo.domain.User">
<property name="id" value="1"/>
<property name="name" value="张三"/>
<property name="city" value="qingdao"/>
</bean>
通过静态工厂方法(配置元信息:XML 和 Java API )
<!-- 静态方法实例化 Bean -->
<bean id="user-by-static-method" class="com.demo.domain.User"
factory-method="createUser"/>
public class User {
private Long id;
private String name;
public static User createUser() {
User user = new User();
user.setId(1L);
user.setName("张三");
return user;
}
通过 Bean 工厂方法(配置元信息:XML和 Java API )
<!-- 实例(Bean)方法实例化 Bean -->
<bean id="user-by-instance-method" factory-bean="userFactory" factory-method="createUser"/>
<bean id="userFactory" class="com.demo.factory.DefaultUserFactory"/>
public class DefaultUserFactory implements UserFactory {
}
public interface UserFactory {
default User createUser() {
return User.createUser();
}
}
通过 FactoryBean(配置元信息:XML、Java 注解和 Java API )
<!-- FactoryBean实例化 Bean -->
<bean id="user-by-factory-bean" class="com.demo.factory.UserFactoryBean" />
import org.springframework.beans.factory.FactoryBean;
public class UserFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return User.createUser();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
通过 ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和 Java API )
1、在META-INF/services/目录下创建接口的全限定名文件 文件名:com.demo.factory.UserFactory 文件内容为文件名指定接口的实现类(多个实现类可以依次换行写):
com.demo.factory.DefaultUserFactory
com.demo.factory.DefaultUserFactory2
2、写代码使用ServiceLoader加载bean
public static void demoServiceLoader() {
ServiceLoader<UserFactory> serviceLoader = ServiceLoader.load(UserFactory.class, Thread.currentThread().getContextClassLoader());
displayServiceLoader(serviceLoader);
}
private static void displayServiceLoader(ServiceLoader<UserFactory> serviceLoader) {
Iterator<UserFactory> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
UserFactory userFactory = iterator.next();
System.out.println(userFactory.createUser());
}
}
3、(进阶)使用spring容器注册可以使用UserFactory的ServiceLoaderFactoryBean
<bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
<property name="serviceType" value="com.demo.UserFactory" />
</bean>
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");
ServiceLoader<UserFactory> serviceLoader = beanFactory.getBean("userFactoryServiceLoader", ServiceLoader.class);
displayServiceLoader(serviceLoader);
}
4、ServiceLoader基本原理
@Override
public final T getObject() throws Exception {
if (isSingleton()) {
return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
}
else {
return createInstance();
}
}
@Override
protected Object createInstance() {
Assert.notNull(getServiceType(), "Property 'serviceType' is required");
return getObjectToExpose(ServiceLoader.load(getServiceType(), this.beanClassLoader));
}
protected abstract Object getObjectToExpose(ServiceLoader<?> serviceLoader);
通过 AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");
AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
UserFactory userFactory = beanFactory.createBean(DefaultUserFactory.class);
System.out.println(userFactory.createUser());
通过 BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
在上面1、Bean注册到IOC容器中-Java API配置元信息中介绍过。
public static void registerUserBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder beanDefinitionBuilder = genericBeanDefinition(User.class);
beanDefinitionBuilder
.addPropertyValue("id", 1L)
.addPropertyValue("name", "张三");
if (StringUtils.hasText(beanName)) {
registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
} else {
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(), registry);
}
}
public static void registerUserBeanDefinition(BeanDefinitionRegistry registry) {
registerUserBeanDefinition(registry, null);
}
4、Bean的初始化
初始化bean的方法的几种方式
public class DefaultUserFactory implements UserFactory, InitializingBean {
@PostConstruct
public void init() {
System.out.println("@PostConstruct : UserFactory 初始化中...");
}
public void initUserFactory() {
System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中...");
}
}
@Bean(initMethod = "initUserFactory")
public UserFactory userFactory() {
return new DefaultUserFactory();
}
Java API方式设置自定义初始化方法,就是使用AbstractBeanDefinition#setInitMethodName(String)
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(User.class);
genericBeanDefinition.setInitMethodName("initUserFactory");
Bean的延迟初始化
关于Bean的懒加载更多详情请点击: Springboot之Bean懒加载的实现详解
Bean的销毁
Bean的销毁方法与Bean的初始化方法如出一辙。
public class DefaultUserFactory implements UserFactory, DisposableBean {
@PreDestroy
public void preDestroy() {
System.out.println("@PreDestroy : UserFactory 销毁中...");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean#destroy() : UserFactory 销毁中...");
}
public void doDestroy() {
System.out.println("自定义销毁方法 doDestroy() : UserFactory 销毁中...");
}
}
applicationContext.close(); 调用之后,也就是Spring应用上下文关闭时会调用Bean的销毁方法。
Bean的销毁源码分析
1、实现DisposableBean 接口,重写destroy方法
ApplicationContext.close() ——> org.springframework.context.support.AbstractApplicationContext#doClose ——> org.springframework.context.support.AbstractApplicationContext#destroyBeans ——> org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons ——> org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons ——> org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton ——> org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroyBean
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
Set<String> dependencies;
synchronized (this.dependentBeanMap) {
dependencies = this.dependentBeanMap.remove(beanName);
}
if (dependencies != null) {
if (logger.isTraceEnabled()) {
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
}
for (String dependentBeanName : dependencies) {
destroySingleton(dependentBeanName);
}
}
if (bean != null) {
try {
bean.destroy();
}
catch (Throwable ex) {
if (logger.isWarnEnabled()) {
logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);
}
}
}
Set<String> containedBeans;
synchronized (this.containedBeanMap) {
containedBeans = this.containedBeanMap.remove(beanName);
}
if (containedBeans != null) {
for (String containedBeanName : containedBeans) {
destroySingleton(containedBeanName);
}
}
synchronized (this.dependentBeanMap) {
for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, Set<String>> entry = it.next();
Set<String> dependenciesToClean = entry.getValue();
dependenciesToClean.remove(beanName);
if (dependenciesToClean.isEmpty()) {
it.remove();
}
}
}
this.dependenciesForBeanMap.remove(beanName);
}
2、标注@PreDestroy的方法 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#CommonAnnotationBeanPostProcessor ——> org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#setDestroyAnnotationType 中,将destroyAnnotationType设置好 ——> org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
return this.emptyLifecycleMetadata;
}
List<LifecycleElement> initMethods = new ArrayList<>();
List<LifecycleElement> destroyMethods = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<LifecycleElement> currInitMethods = new ArrayList<>();
final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
currDestroyMethods.add(new LifecycleElement(method));
if (logger.isTraceEnabled()) {
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
});
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
new LifecycleMetadata(clazz, initMethods, destroyMethods));
}
5、Bean 垃圾回收(GC)
public class DefaultUserFactory {
@Override
public void finalize() throws Throwable {
System.out.println("当前 DefaultUserFactory 对象正在被垃圾回收...");
}
}
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(BeanInitializationDemo.class);
applicationContext.refresh();
System.out.println("Spring 应用上下文已启动");
UserFactory bean = applicationContext.getBean(UserFactory.class);
System.out.println(bean.createUser());
applicationContext.close();
bean=null;
System.gc();
Thread.sleep(5000);
由此可见,当IOC容器关闭的时候,bean才会被垃圾回收。 实际中IOC容器关闭也就意味着应用关闭了,bean的存活时间基本上是等于IOC容器,等于应用的。
|