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 元信息
属性说明
ClassBean 全类名,必须是具体类,不能用抽象类或接口
NameBean 的名称或者 ID
ScopeBean 的作用域(如:singleton、prototype等)
Constructor argumentsBean 构造器参数(用于依赖注入)
PropertiesBean 属性设置(用于依赖注入)
Autowiring modeBean 自动绑定模式(如:通过名称 byName)
Lazy initialization modeBean 延迟初始化模式(延迟和非延迟)
Initialization methodBean 初始化回调方法名称
Destruction methodBean 销毁回调方法名称
  • 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. 通过 BeanDefinitionBuilder
        BeanDefinitionBuilder 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 {

	@Override
	public 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";


	@Override
	public 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
    @Component
    public 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)) {
            // 命名方式 注册 BeanDefinition
            registry.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;
    }

    @Override
    public 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> {
    @Override
    public User getObject() throws Exception {
        return User.createUser();
    }

    @Override
    public 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 {

	@Nullable
	private Class<?> serviceType;

	@Nullable
	private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

	public void setServiceType(@Nullable Class<?> serviceType) {
		this.serviceType = serviceType;
	}

	@Nullable
	public Class<?> getServiceType() {
		return this.serviceType;
	}

	@Override
	public void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {
		this.beanClassLoader = beanClassLoader;
	}

	@Override
	protected 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 进行迭代 并返回一个对象
	*/
	@Override
	protected 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
	@Nullable
	public 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 注解
    @PostConstruct
    public 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);
        // 启动 applicationContext
        applicationContext.refresh();

        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        // 关闭 applicationContext
        applicationContext.close();
    }

    @Bean
    public UserFactory initUserFactory(){
        return new DefaultUserFacoty();
    }
}

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

public class DefaultUserFacoty implements  UserFactory ,InitializingBean {

    @Override
    public 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);
        // 启动 applicationContext
        applicationContext.refresh();

        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        // 关闭 applicationContext
        applicationContext.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);
        // 启动 applicationContext
        applicationContext.refresh();
        // 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
        System.out.println("Spring 应用上下文已启动...");
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        // 关闭 applicationContext
        applicationContext.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);
        // 启动 applicationContext
        applicationContext.refresh();
        // 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
        System.out.println("Spring 应用上下文已启动...");
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        // 关闭 applicationContext
        applicationContext.close();
    }

    @Bean(initMethod = "initMethod")
    @Lazy
    public 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 注解
    @PreDestroy
    public void perDestroy(){
        System.out.println(" @PreDestroy 销毁... ");
    }

    // 4.2 基于 @Bean initMethod 方法
    public void destoryMethod(){
        System.out.println(" @Bean$destoryMethod 初始化...");
    }

    @Override
    public 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);
        // 启动 applicationContext
        applicationContext.refresh();
        // 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
        System.out.println("Spring 应用上下文已启动...");
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        // 关闭 applicationContext
        applicationContext.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);
        // 启动 applicationContext
        applicationContext.refresh();
        // 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
        System.out.println("Spring 应用上下文已启动...");
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        System.out.println("Spring 应用准备关闭...");
        // 关闭 applicationContext
        applicationContext.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

how is Spring bean

what in the world are spring beans

Spring 核心

Spring中Bean命名源码分析

1.3.1.Bean的命名

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

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

SpringBoot基础篇Bean之动态注册

spring之Bean实例化过程

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值