本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。

文章目录

  • 简述
  • Spring Bean 基础
    • 定义Spring Bean
    • BeanDefinition 元信息
    • 命名 Spring Bean
    • Spring Bean 的别名
    • 注册 Spring Bean
    • 实例化 Spring Bean
    • 初始化 Spring Bean
    • 延迟初始化 Spring Bean
    • 销毁 Spring Bean
  • 总结
  • 参考资料

简述

Spring 框架基础核心之一是 Bean 的概念。Spring beanSpring 框架在运行时管理的对象。Spring bean 是任何 Spring 应用程序的基本构建块。如何去定义 Spring Bean ,这种 Bean 和传统的 Java Bean 是有区别的。

Spring Framework 官方文档对 bean 的定义:

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.
Spring 中,构成应用程序主干并由 Spring IoC 容器管理的对象称为 beanBean 是由 Spring IoC 容器实例化,组装和以其他方式管理的对象。

Spring Bean 基础

Spring bean 只是由 Spring 容器管理的实例对象,它们是由框架创建和关联的,并放入 Spring 容器中,后续可以从那里获取它们。

定义Spring Bean

BeanDefinitionSpring Framework 中定义 Bean 的配置元信息接口,包含:

  • Bean 的类名 全称包含包名
  • Bean 行为配置元素,如作用域、自动绑定的模式、生命周期回调等
  • 其他 Bean 引用,又可称作合作者(Collaboratiors) 或者依赖(Dependencies)
  • 配置设置,比如 Bean 属性(Properties)
BeanDefinition 元信息
属性 说明
Class Bean 全类名,必须是具体类,不能用抽象类或接口
Name Bean 的名称或者 ID
Scope Bean 的作用域(如:singleton、prototype等)
Constructor arguments Bean 构造器参数(用于依赖注入)
Properties Bean 属性设置(用于依赖注入)
Autowiring mode Bean 自动绑定模式(如:通过名称 byName)
Lazy initialization mode Bean 延迟初始化模式(延迟和非延迟)
Initialization method Bean 初始化回调方法名称
Destruction method Bean 销毁回调方法名称
  • BeanDefinition 构建的两种方式

    1. 通过 BeanDefinitonBuilder
    2. 通过 AbstractBeanDefinition 以及派生类

User POJO

public class User {private Long id;private String name;public void setId(Long id) {this.id = id;}public void setName(String name) {this.name = name;}
}

BeanDefianitonDemo BeanDefiniton 两种构建方式的代码

public class BeanDefianitonDemo {public static void main(String[] args) {//1. 通过 BeanDefinitionBuilderBeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);// 通过属性设置beanDefinitionBuilder.addPropertyValue("id", 1);beanDefinitionBuilder.addPropertyValue("name", "小明");BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();// 2. 通过 AbstractBeanDefinition 以及派生类GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();// 设置 Bean 类型genericBeanDefinition.setBeanClass(User.class);// 通过 MutablePropertyValues 批量操作属性MutablePropertyValues propertyValues = new MutablePropertyValues();propertyValues.add("id", 1).add("name", "小红");genericBeanDefinition.setPropertyValues(propertyValues);}
}
命名 Spring Bean

每个 Bean 拥有一个或多个标识符(identifiers),这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名(Alias)来扩充。

通常 Bean 的 标识符由字母组成,允许出现特殊字符。如果要想引入 Bean 的别名的话,可在 name 属性使用半角逗号(“,”)或分号(“;”) 来间隔。

Beanidname 属性并非必须制定,如果留空的话,容器会为 Bean 自动生成一个唯一的名称。Bean 的命名尽管没有限制,官方建议采用驼峰的方式,更符合 Java 的命名约定。

Bean 名称生成器(BeanNameGenerator)由 Spring Framework 2.0.3 引入,框架內建两种实现:

  • DefaultBeanNameGenerator:默认通用 BeanNameGenerator 实现。
  • AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator 实现。

BeanNameGenerator 源码

public interface BeanNameGenerator {/*** Generate a bean name for the given bean definition.* @param definition the bean definition to generate a name for* @param registry the bean definition registry that the given definition* is supposed to be registered with* @return the generated bean name*/String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);}

DefaultBeanNameGenerator 源码

public class DefaultBeanNameGenerator implements BeanNameGenerator {@Overridepublic String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {return BeanDefinitionReaderUtils.generateBeanName(definition, registry);}}

AnnotationBeanNameGenerator 源码

public class AnnotationBeanNameGenerator implements BeanNameGenerator {private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";@Overridepublic String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {// 判断是否为 注解 bean 定义if (definition instanceof AnnotatedBeanDefinition) {String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);if (StringUtils.hasText(beanName)) {// Explicit bean name found.return beanName;}}// Fallback: generate a unique default bean name.// 回调,生成默认的bean 名称return buildDefaultBeanName(definition, registry);}
}

BeanDefinitionReaderUtils#generateBeanName()

if (isInnerBean) {// Inner bean: generate identity hashcode suffix.// 嵌套bean,bean里面生成bean :生成 定义的 hash编码 作为后缀,使用 # 来连接前缀和后缀id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);}
else {// Top-level bean: use plain class name with unique suffix if necessary.// 顶层 bean return uniqueBeanName(generatedBeanName, registry);
}

BeanDefinitionReaderUtils#uniqueBeanName()

public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {String id = beanName;int counter = -1;// Increase counter until the id is unique.// counter 计数器 从0开始while (counter == -1 || registry.containsBeanDefinition(id)) {counter++;// GENERATED_BEAN_NAME_SEPARATOR # 来连接 bean名称和计数器id = beanName + GENERATED_BEAN_NAME_SEPARATOR + counter;}return id;
}
Spring Bean 的别名

Bean 别名使开发人员可以覆盖已配置的 Bean,并用不同的对象定义替换它们。当 bean 定义是无法控制的外部资源继承时,这个时候别名非常有用。

Bean 别名(Alias)的价值

  • 复用现有的 BeanDefinition,不能无中生有
  • 更具有场景化的命名方法,比如:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

呼应上面更具场景化的命名方法,下面将列举一个实际场景中出现的做法。

src/main/resources/spring/applicationContext-tx.xml

<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close"><property name="className" value="${jdbc.driverClassName}"/><property name="uniqueName" value="dataSource"/><property name="minPoolSize" value="0"/><property name="maxPoolSize" value="5"/><property name="allowLocalTransactions" value="false" /><property name="driverProperties"><props><prop key="user">${jdbc.username}</prop><prop key="password">${jdbc.password}</prop><prop key="url">${jdbc.url}</prop></props></property>
</bean><bean id="jtaTransactionManager" factory-method="getTransactionManager"class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig, dataSource"destroy-method="shutdown"/>

dataSource bean 定义期望使用 XA 数据源,但是由于 HSQLDB 不提供 XA 数据源,因此必须依靠 LrcXADataSource 来解决此限制。但这意味着 DataSource 将更改为使用不同的 classNamedriverProperties ,因为上下文环境配置来自外部工件,因此实现起来比较复杂。

src/test/resources/spring/applicationContext-test.xml

<import resource="classpath:spring/applicationContext-tx.xml" /><bean id="testDataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close"><property name="className" value="bitronix.tm.resource.jdbc.lrc.LrcXADataSource"/><property name="uniqueName" value="testDataSource"/><property name="minPoolSize" value="0"/><property name="maxPoolSize" value="5"/><property name="allowLocalTransactions" value="false" /><property name="driverProperties"><props><prop key="user">${jdbc.username}</prop><prop key="password">${jdbc.password}</prop><prop key="url">${jdbc.url}</prop><prop key="driverClassName">${jdbc.driverClassName}</prop></props></property>
</bean><alias name="testDataSource" alias="dataSource"/>

testDataSource 与继承的 dataSource 具有相同的 Class 类型,但它可以具有不同的对象配置。每次开发人员希望在需要 dataSource 依赖项时都将使用 dataSource 数据源,而不是原始数据源 testDataSource。这可以通过别名关键字来实现,该关键字指示依赖项注入容器将原始数据源定义替换为新版本。

注册 Spring Bean

BeanDefinition 注册的三种方式

  1. XML 配置元信息

<bean name=”…” … />

  1. Java 注解配置元信息
  • @Bean
  • @Component
  • @Import
  1. Java API 配置元信息
  • 命名方式:

BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)

  • 非命名方式:

BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,BeanDefinitionRegistry)

  • 配置类方式:

AnnotatedBeanDefinitionReader#register(Class…)

这里介绍第二种和第三种注册方式。

@Bean 注册 Bean

import com.feng.spring.bean.pojo.User;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;public class AnnotationBeanDefinitionDemo {public static void main(String[] args) {// 创建 BeanFactory 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册 Configuration Class (配置类)applicationContext.register(Config.class);// 刷新引用上下文applicationContext.refresh();System.out.println(" User Beans :"+ applicationContext.getBeansOfType(User.class));// 显示地关闭 Spring 应用上下文applicationContext.close();}public static class Config{/*** 通过 Java 注解的方式,定义一个 Bean*/@Bean(name = "user")public User user(){User user = new User();user.setId(1L);user.setName("小明");return user;}}
}

@Component 注册 Bean

import com.feng.spring.bean.pojo.User;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;public class AnnotationBeanDefinitionDemo {public static void main(String[] args) {// 创建 BeanFactory 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册 Configuration Class (配置类)applicationContext.register(Config.class);// 刷新引用上下文applicationContext.refresh();System.out.println(" User Beans :"+ applicationContext.getBeansOfType(User.class));// 显示地关闭 Spring 应用上下文applicationContext.close();}// 使用 @Component 注册 Bean@Componentpublic static class Config{/*** 通过 Java 注解的方式,定义一个 Bean*/@Bean(name = "user")public User user(){User user = new User();user.setId(1L);user.setName("小明");return user;}}
}

@Import 注册 Bean

@Import(AnnotationBeanDefinitionDemo.Config.class)
public class AnnotationBeanDefinitionDemo {...
// 此处代码同上
...
}

Java API 注册 Bean , 命名方式和非命名方式

public class AnnotationBeanDefinitionDemo {public static void main(String[] args) {// 创建 BeanFactory 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();registerBeanDefinition(applicationContext, "xiaoming-user");registerBeanDefinition(applicationContext);// 刷新引用上下文applicationContext.refresh();System.out.println(" User Beans :"+ applicationContext.getBeansOfType(User.class));// 显示地关闭 Spring 应用上下文applicationContext.close();}public static void registerBeanDefinition(BeanDefinitionRegistry registry,String beanName){BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);beanDefinitionBuilder.addPropertyValue("id", 1L).addPropertyValue("name", "小明");if (StringUtils.hasText(beanName)) {// 命名方式 注册 BeanDefinitionregistry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());}else{BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(), registry);}}
}
实例化 Spring Bean

Bean 实例化 (Instantiation)

  • 常规方式

    • 通过构造器(配置元信息:XML、Java 注解和 Java API )
    • 通过静态工厂方法(配置元信息:XML 和 Java API )
    • 通过 Bean 工厂方法(配置元信息: XML和 Java API )
    • 通过 FactoryBean (配置元信息: XML、Java 注解和 Java API )
  • 特殊方式
    • 通过 ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和 Java API )
    • 通过 AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
    • 通过 BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)

常规方式
通过静态工厂方法

创建 bean-instantiation-context.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 http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user-by-static-method" class="com.feng.spring.bean.pojo.User"factory-method="createUser"/>
</beans>

创建 User#createUser 静态方法

public class User {private Long id;private String name;public void setId(Long id) {this.id = id;}public void setName(String name) {this.name = name;}public static User createUser() {User user = new User();user.setName("小明");user.setId(1L);return user;}@Overridepublic String toString(){return "User{" +"id = " + id +", name = " + name +"}";}
}

BeanInstantiationDemo bean 实例化示例

import com.feng.spring.bean.pojo.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** bean 实例化 示例*/
public class BeanInstantiationDemo {public static void main(String[] args) {// 配置 XML 配置文件// 启动 Spring 应用上下文BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");User userByStaticMethod = beanFactory.getBean("user-by-static-method", User.class);System.out.println(userByStaticMethod);}
}

通过 Bean 工厂方法
创建 User 的工厂方法:
UserFactory.class

public interface UserFactory {// 采用 Java 1.8 实现方式 default User createUser(){return new User();}
}

DefaultUserFactory.class

public class DefaultUserFacoty implements  UserFactory {}

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 http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 工厂方法实例化 Bean--><bean id="user-by-instance-method" factory-bean="userFacoty" factory-method="createUser"/><bean id="userFacoty" class="com.feng.spring.bean.factory.DefaultUserFacoty"/>
</beans>
/*** bean 实例化 示例*/
public class BeanInstantiationDemo {public static void main(String[] args) {// 配置 XML 配置文件// 启动 Spring 应用上下文BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");User userByInstanceMethod = beanFactory.getBean("user-by-instance-method", User.class);System.out.println(userByInstanceMethod);}
}

注意:
上面使用了 Java 1.8 的新特性 接口默认实现,需要修改编译级别,在 pom.xml 文件中添加如下代码

    <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build>

通过 FactoryBean,实现 UserFactoryBean

public class UserFactoryBean implements FactoryBean<User> {@Overridepublic User getObject() throws Exception {return User.createUser();}@Overridepublic Class<?> getObjectType() {return User.class;}
}

配置 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 http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- FactoryBean 实例化 Bean--><bean id="user-by-factory-bean" class="com.feng.spring.bean.factory.UserFactoryBean"/>
</beans>

这种实现方式比较特殊,这里并不是直接去定义一个 User Bean ,而是定义一个 FactoryBean 去链接 User 对应的 实现方法和实现类型。

public class BeanInstantiationDemo {public static void main(String[] args) {// 配置 XML 配置文件// 启动 Spring 应用上下文BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");User userByFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);System.out.println(userByFactoryBean);}
}

特殊方式
通过 ServiceLoaderFactoryBean
在介绍这个方法前,先看 Java 1.6 中的 ServiceLoader

public final class ServiceLoader<S>implements Iterable<S>
{private static final String PREFIX = "META-INF/services/";...
}

META-INF/services/ 是资源管理的目录,Spring 中的 ServiceLoaderFactoryBean 整合了 ServiceLoader 这个对应的资源目录,下面看下 ServiceLoader 是如何进行操作的。

创建一个全类名作为名称的文件没有后缀,内容填写对应的实现类 com.feng.spring.bean.factory.DefaultUserFacoty


ServiceLoaderDemo.class

import java.util.Iterator;
import java.util.ServiceLoader;public class ServiceLoaderDemo {public static void main(String[] args) {demoServiceLoader();}public static void demoServiceLoader(){// service 加载ServiceLoader<UserFactory> serviceLoader = ServiceLoader.load(UserFactory.class, Thread.currentThread().getContextClassLoader());// 使用迭代器 进行迭代Iterator<UserFactory> iterator = serviceLoader.iterator();while (iterator.hasNext()) {UserFactory userFactory = iterator.next();System.out.println(userFactory.createUser());}}
}

如何使用 ServiceLoaderFactoryBean 进行初始化 Bean
创建 special-bean-instantiation-context.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 http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean"><property name="serviceType" value="com.feng.spring.bean.factory.UserFactory"/></bean>
</beans>

注册一个 ServiceLoaderFactoryBean 其中 serviceType 类型为 UserFactory ,可以根据 AbstractServiceLoaderBasedFactoryBean$serviceType 这个类属性进行判断,serviceTypeServiceLoader 所关注的对象,对 serviceType 属性进行赋值后,将影响 createInstance() 进行初始化创建。

public abstract class AbstractServiceLoaderBasedFactoryBean extends AbstractFactoryBean<Object>implements BeanClassLoaderAware {@Nullableprivate Class<?> serviceType;@Nullableprivate ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();public void setServiceType(@Nullable Class<?> serviceType) {this.serviceType = serviceType;}@Nullablepublic Class<?> getServiceType() {return this.serviceType;}@Overridepublic void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {this.beanClassLoader = beanClassLoader;}@Overrideprotected Object createInstance() {Assert.notNull(getServiceType(), "Property 'serviceType' is required");return getObjectToExpose(ServiceLoader.load(getServiceType(), this.beanClassLoader));}...
}

return getObjectToExpose(ServiceLoader.load(getServiceType(), this.beanClassLoader));

初始化场景将执行 getObjectToExpose() 方法

public class ServiceFactoryBean extends AbstractServiceLoaderBasedFactoryBean implements BeanClassLoaderAware {/*** 对 ServiceLoader 进行迭代 并返回一个对象*/@Overrideprotected Object getObjectToExpose(ServiceLoader<?> serviceLoader) {Iterator<?> it = serviceLoader.iterator();if (!it.hasNext()) {throw new IllegalStateException("ServiceLoader could not find service for type [" + getServiceType() + "]");}return it.next();}@Override@Nullablepublic Class<?> getObjectType() {return getServiceType();}
}

由此可以写出执行方法:

public class SpecialBeanInstantiationDemo {public static void main(String[] args) {// 配置 xml 配置文件// 启动 Spring 应用上下文BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");ServiceLoader<UserFactory> serviceLoader = beanFactory.getBean("userFactoryServiceLoader", ServiceLoader.class);// 使用迭代器 进行迭代Iterator<UserFactory> iterator = serviceLoader.iterator();while (iterator.hasNext()) {UserFactory userFactory = iterator.next();System.out.println(userFactory.createUser());}}
}

通过 AutowireCapableBeanFactory

public class AutowireCapableBeanFactoryDemo {public static void main(String[] args) {// 配置 xml 配置文件,启动 Spring 应用上下文ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();UserFactory userFactory = beanFactory.createBean(DefaultUserFacoty.class);System.out.println(userFactory.createUser());}
}
初始化 Spring Bean

Bean 初始化(Initialization) 的三种方式

  1. @PostConstruct 标注方法
  2. 实现 InitializingBean#afterPropertiesSet() 方法
  3. 自定义初始化方法
    • XML 配置:<bean init-method=”init” … />
    • Java 注解:@Bean$initMethod
    • Java API:AbstractBeanDefinition#setInitMethodName(String)

基于 @PostConstruct 标注方法:
创建 DefaultUserFacoty#init 方法,并标注 @PostConstruct 注解

public class DefaultUserFacoty implements  UserFactory {// 1. 基于 @PostConstruct 注解@PostConstructpublic void init(){System.out.println(" @PostConstruct 初始化。。。 ");}
}

创建 BeanInitializationDemo 类,并通过 @BeanDefaultUserFacoty 进行注册,此时将会回调 DefaultUserFacoty 类中被 @PostConstruct 注解的方法。

/*** Bran 初始化 示例*/
@Configuration  // 配置 类
public class BeanInitializationDemo {public static void main(String[] args) {// 创建 BeanFactory 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册 Configuration 类applicationContext.register(BeanInitializationDemo.class);// 启动 applicationContextapplicationContext.refresh();UserFactory userFactory = applicationContext.getBean(UserFactory.class);// 关闭 applicationContextapplicationContext.close();}@Beanpublic UserFactory initUserFactory(){return new DefaultUserFacoty();}
}

实现 InitializingBean#afterPropertiesSet() 方法:
DefaultUserFacoty 实现 InitializingBean 接口,并实现 afterPropertiesSet 方法。

public class DefaultUserFacoty implements  UserFactory ,InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println(" InitializingBean#afterPropertiesSet 初始化..  ");}
}

自定义初始化方法(这里只介绍一下 @Bean$initMehtod 如何实现)
Java 注解:@Bean$initMethod
创建 DefaultUserFacoty#initMethod 方法

public class DefaultUserFacoty implements  UserFactory {// 3.2 基于 @Bean initMethod 方法public void initMethod(){System.out.println(" @Bean$initMethod 初始化...");}
}
public interface UserFactory {default User createUser(){return new User();}void initMethod();
}

BeanInitializationDemo 类中对 @Bean$initMethod 属性进行赋值,赋予 DefaultUserFacoty 中初始化的方法名。

/*** Bran 初始化 示例*/
@Configuration  // 配置 类
public class BeanInitializationDemo {public static void main(String[] args) {// 创建 BeanFactory 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册 Configuration 类applicationContext.register(BeanInitializationDemo.class);// 启动 applicationContextapplicationContext.refresh();UserFactory userFactory = applicationContext.getBean(UserFactory.class);// 关闭 applicationContextapplicationContext.close();}@Bean(initMethod = "initMethod")public UserFactory initUserFactory(){return new DefaultUserFacoty();}
}

以上三种方法在同一个方法中同时出现,其执行顺序是:

优先于
优先于
PostConstant
InitializingBean
自定义
延迟初始化 Spring Bean

Spring 官方API:

By default, ApplicationContext implementations eagerly create and configure all singleton beans as part of the initialization process. Generally, this pre-instantiation is desirable, because errors in the configuration or surrounding environment are discovered immediately, as opposed to hours or even days later. When this behavior is not desirable, you can prevent pre-instantiation of a singleton bean by marking the bean definition as lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.


译文:

默认情况下,Spring 容器在初始化过程中会创建和配置所有单例的bean。这种提前实例化是可取的,因为配置环境错误会被立即发现而不需要过多的时间。如果不采取这种行为,可以将单例的bean标记为延迟初始化。一个延迟初始化的bean告诉Spring IoC容器去创建这个bean实例化对象当它第一次被调用时而不是在容器启动时立即创建。

设计模式中有一种延迟加载的模式和延迟初始化的效果类似。

Bean 延迟初始化(Lazy Initialization) 的两种方式:

  • XML 配置:<bean lazy-init=”true” … />
  • Java 注解:@Lazy(true)

默认情况下是非延迟加载,基于 XML 配置和 Java 注解所产生的效果是类似的,只要在被加载的 Bean 上注解 @Lazy ,那么这个 Bean 就是延迟加载。

@Bean(initMethod = "initMethod")
@Lazy
public UserFactory initUserFactory(){return new DefaultUserFacoty();
}

下面例子通过对容器启动完成后,打印一句话,之后对 UserFactory 进行依赖查找,通过打印的顺序看下,延迟加载的特性。

非延迟加载例子,打印结果。

/*** Bran 初始化 示例*/
@Configuration  // 配置 类
public class BeanInitializationDemo {public static void main(String[] args) {// 创建 BeanFactory 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册 Configuration 类applicationContext.register(BeanInitializationDemo.class);// 启动 applicationContextapplicationContext.refresh();// 非延迟初始化在 Spring 应用上下文启动完成后,被初始化System.out.println("Spring 应用上下文已启动...");UserFactory userFactory = applicationContext.getBean(UserFactory.class);// 关闭 applicationContextapplicationContext.close();}@Bean(initMethod = "initMethod")public UserFactory initUserFactory(){return new DefaultUserFacoty();}
}

控制台输出:

@PostConstruct 初始化。。。
InitializingBean#afterPropertiesSet 初始化..
@Bean$initMethod 初始化...
Spring 应用上下文已启动..

延迟加载例子,打印结果。

/*** Bran 初始化 示例*/
@Configuration  // 配置 类
public class BeanInitializationDemo {public static void main(String[] args) {// 创建 BeanFactory 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册 Configuration 类applicationContext.register(BeanInitializationDemo.class);// 启动 applicationContextapplicationContext.refresh();// 非延迟初始化在 Spring 应用上下文启动完成后,被初始化System.out.println("Spring 应用上下文已启动...");UserFactory userFactory = applicationContext.getBean(UserFactory.class);// 关闭 applicationContextapplicationContext.close();}@Bean(initMethod = "initMethod")@Lazypublic UserFactory initUserFactory(){return new DefaultUserFacoty();}
}

打印结果:

Spring 应用上下文已启动...
@PostConstruct 初始化。。。
InitializingBean#afterPropertiesSet 初始化..
@Bean$initMethod 初始化...

从结果对比可以看出,延迟初始化是在容器启动完成后,依赖查找对应的 Bean 时,才会触发 Bean 的初始化。
延迟初始化是一种按需初始化。

Spring 容器返回的对象与非延迟的对象存在怎样的差异?
差异在于依赖查找和依赖注入时,是否在容器启动前后进行初始化。
相同在于注册 Bean 时,容器都是无差别进行注册。

销毁 Spring Bean

Bean 销毁(Destroy) 的三种方式:

  1. @PreDestroy 标注方法
  2. 实现 DisposableBean 接口的 destroy() 方法
  3. 自定义销毁方法
    • XML 配置:<bean destroyMethod=”destroyMethod” … />
    • Java 注解:@Bean(destroyMethod=”destoryMethod”)
    • Java API:AbstractBeanDefinition#setDestroyMethodName(String)

销毁 Bean 和初始化 Bean 相似,下面通过代码来讲述一下:

DefaultUserFacoty

/*** 默认 {@link UserFactory} 实现*/
public class DefaultUserFacoty implements  UserFactory,DisposableBean {// 1. 基于 @PreDestroy 注解@PreDestroypublic void perDestroy(){System.out.println(" @PreDestroy 销毁... ");}// 4.2 基于 @Bean initMethod 方法public void destoryMethod(){System.out.println(" @Bean$destoryMethod 初始化...");}@Overridepublic void destroy() throws Exception {System.out.println(" DisposableBean#destroy 销毁... ");}
}
@Configuration  // 配置 类
public class BeanInitializationDemo {public static void main(String[] args) {// 创建 BeanFactory 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册 Configuration 类applicationContext.register(BeanInitializationDemo.class);// 启动 applicationContextapplicationContext.refresh();// 非延迟初始化在 Spring 应用上下文启动完成后,被初始化System.out.println("Spring 应用上下文已启动...");UserFactory userFactory = applicationContext.getBean(UserFactory.class);// 关闭 applicationContextapplicationContext.close();}@Bean(destroyMethod = "destoryMethod")public UserFactory initUserFactory(){return new DefaultUserFacoty();}
}

控制台输出:

Spring 应用上下文已启动...
@PreDestroy 销毁...
DisposableBean#destroy 销毁...
@Bean$destoryMethod 初始化...

由控制台输出顺序得出,以上三种方法在同一个方法中同时出现,其执行顺序是:

优先于
优先于
PreDestroy
DisposableBean
自定义

销毁触发的条件是什么?
通过在 关闭 applicationContext applicationContext.close(); 语句上下打印两条语句可以进行判断:

/*** Bran 初始化 示例*/
@Configuration  // 配置 类
public class BeanInitializationDemo {public static void main(String[] args) {// 创建 BeanFactory 容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册 Configuration 类applicationContext.register(BeanInitializationDemo.class);// 启动 applicationContextapplicationContext.refresh();// 非延迟初始化在 Spring 应用上下文启动完成后,被初始化System.out.println("Spring 应用上下文已启动...");UserFactory userFactory = applicationContext.getBean(UserFactory.class);System.out.println("Spring 应用准备关闭...");// 关闭 applicationContextapplicationContext.close();System.out.println("Spring 应用已关闭...");}@Bean(initMethod = "initMethod",destroyMethod = "destoryMethod")public UserFactory initUserFactory(){return new DefaultUserFacoty();}
}

控制台输出:

Spring 应用上下文已启动...
Spring 应用准备关闭...
@PreDestroy 销毁...
DisposableBean#destroy 销毁...
@Bean$destoryMethod 初始化...
Spring 应用已关闭...

通过对比控制台输出语句可以得出,Spring Bean 销毁是由 applicationContext.close(); 触发的。

总结

本文主要介绍 Spring Bean 的生命周期过程中的阶段,讨论 Spring 容器如何针对各个阶段编写代码。

参考资料

Spring Bean

  • https://www.baeldung.com/spring-bean

how is Spring bean

  • http://dolszewski.com/spring/spring-bean/

what in the world are spring beans

  • https://stackoverflow.com/questions/17193365/what-in-the-world-are-spring-beans

Spring 核心

  • https://time.geekbang.org/course/detail/265-187472

Spring中Bean命名源码分析

  • https://www.cnblogs.com/heliusKing/p/11741403.html

1.3.1.Bean的命名

  • https://www.kancloud.cn/liuqingyu/spring-doc/654226

How to create an alias for a bean and inner bean in Spring Framework

  • http://mrbool.com/how-to-create-an-alias-for-a-bean-and-inner-bean-in-spring-framework/28549

为什么我喜欢Spring bean别名(Why I like Spring bean aliasing

  • https://vladmihalcea.com/why-i-like-spring-bean-aliasing/

SpringBoot基础篇Bean之动态注册

  • https://juejin.im/post/5bcc54a9f265da0aaa054b3a

spring之Bean实例化过程

  • https://www.jianshu.com/p/1d0fb520e5c0

Spring Bean 是什么?相关推荐

  1. spring Bean自动装配

    spring Bean自动装配 自动装配是使用spring满足bean依赖的一种方式. spring会在应用上下文中为某个bean寻找其依赖的bean. spring自动装配需要从两个角度来实现,或者 ...

  2. Spring Bean 中的线程安全

    在 使用Spring框架时,很多时候不知道或者忽视了多线程的问题.因为写程序时,或做单元测试时,很难有机会碰到多线程的问题,因为没有那么容易模拟多线 程测试的环境.但如果不去考虑潜在的漏洞,它就会变成 ...

  3. Spring Bean配置方式之一:Java配置

    简介: Spring bean 是使用传统的 XML 方法配置的.在这篇文章中,您将学习使用基于纯 Java 的配置而非 XML 来编写 Spring bean 并配置它们.本文将介绍可用来配置 be ...

  4. Spring bean 实现生命周期的三种解决方案

    Spring bean 实现生命周期的三种解决方案 参考文章: (1)Spring bean 实现生命周期的三种解决方案 (2)https://www.cnblogs.com/javawebsoa/a ...

  5. Spring ----Bean的生命周期

    这Spring框架中,一旦把一个bean纳入到Spring IoC容器之中,这个bean的生命周期就会交由容器进行管理, 一般担当管理者角色的是BeanFactory或ApplicationConte ...

  6. 【Spring实战】注入非Spring Bean对象

    2019独角兽企业重金招聘Python工程师标准>>> 大家经常用到Spring IOC去管理对象之间的依赖关系,但一般情况下都有一个前提:这些Bean对象必须是通过Spring容器 ...

  7. spring bean作用域_Spring面试知识点,这是我见过最全面的 - 知识铺

    知识铺: 致力于打造轻知识点,持续更新每次的知识点较少,阅读不累.不占太多时间,不停地来唤醒记忆深处的知识点. Q1.什么是Spring框架? Spring是最流行的企业应用程序框架之一.Spring ...

  8. 【一步一步学习spring】spring bean管理(上)

    1. spring 工厂类 我们前边的demo中用到的spring 工厂类是ClassPathXmlApplicationContext,从上图可以看到他还有一个兄弟类FileSystemApplic ...

  9. Spring bean加载多个配置文件

    在一个大的项目结构,Spring bean配置文件位于不同的文件夹以便于维护和模块化.例如,Spring-Common.xml在common 文件夹中,Spring-Connection.xml 在c ...

  10. spring bean生命周期管理--转

    Life Cycle Management of a Spring Bean 原文地址:http://javabeat.net/life-cycle-management-of-a-spring-be ...

最新文章

  1. 007_Curator框架二
  2. 图解虚拟机中CentOS安装使用和编程
  3. Python查找包含指定字符串的所有Office文档
  4. 中低频量化交易策略研发04_ 简单的择时策
  5. 类型约束的本质:泛型是不完备类型,只有合乎要求的构造才能正确使用和访问。...
  6. python什么是可变参数_详解Python的三种可变参数
  7. 百度云推送push的使用
  8. deepin linux 安装 磁盘管理,在微软Windows系统上编辑深度Deepin Linux系统的磁盘
  9. 【51单片机】按键控制多个舵机(可用于机械臂控制)
  10. 团队作业8----第二次项目冲刺(Beta阶段) 第四天
  11. 把一个人的特点写具体作文_把一个人的特点写具体作文800字
  12. Java phantomjs 网页截图
  13. Python环境迁移
  14. matlab绘制矩阵色块图
  15. softmax与sigmoid的区别
  16. strtok是分割字符串,查找中间最长的单元
  17. “您的访问出错了”网页自动跳转贴吧404
  18. ftp服务器云盘,​企业网盘和FTP服务器对比,到底哪个更好用?
  19. Docker学习总结(13)——从零开始搭建Jenkins+Docker自动化集成环境
  20. 武汉市2022年东湖高新区外资企业投资发展补贴政策申报指南

热门文章

  1. 优势k歌软件功能说明
  2. fancybox ajax post,javascript - 将jquery fancybox显示为ajax成功
  3. 简单来看看什么是侧信道攻击
  4. STM32 以太网W5500
  5. 麒麟信安云推动河北省某检察院“智慧检务”跨越式发展
  6. Git补丁简单用法介绍(打补丁.diff 和 .patch 和 git apply、git am应用)
  7. n1 openwrt 挂载u盘_[Openwrt 扩展上篇]USB挂载U盘启动Samba共享
  8. 买个云服务器有啥用_买了一台云服务器可以干嘛
  9. 【君思智慧园区】数字化园区管理系统
  10. ubuntu 系统连接 xiaomi手机