Spring Bean 基础

定义 Spring Bean

什么是 BeanDefinition?

BeanDefinition 是 Spring Framework 中定义 Bean 的配置元信息接口,包含:
• Bean 的类名
• Bean 行为配置元素,如作用域、自动绑定的模式,生命周期回调等
• 其他 Bean 引用,又可称作合作者(collaborators)或者依赖(dependencies)
• 配置设置,比如 Bean 属性(Properties)

BeanDefinition 元信息

BeanDefinition 元信息

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

BeanDefinition 构建

• 通过 BeanDefinitionBuilder
• 通过 AbstractBeanDefinition 以及派生类

public static void main(String[] args) {

    // 1.通过 BeanDefinitionBuilder 构建
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
    // 通过属性设置
    beanDefinitionBuilder
            .addPropertyValue("id", 1)
            .addPropertyValue("name", "小马哥");
    // 获取 BeanDefinition 实例
    BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
    // BeanDefinition 并非 Bean 终态,可以自定义修改

    // 2. 通过 AbstractBeanDefinition 以及派生类
    GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
    // 设置 Bean 类型
    genericBeanDefinition.setBeanClass(User.class);
    // 通过 MutablePropertyValues 批量操作属性
    MutablePropertyValues propertyValues = new MutablePropertyValues();
//        propertyValues.addPropertyValue("id", 1);
//        propertyValues.addPropertyValue("name", "小马哥");
    propertyValues
            .add("id", 1)
            .add("name", "小马哥");
    // 通过 set MutablePropertyValues 批量操作属性
    genericBeanDefinition.setPropertyValues(propertyValues);
}

命名 Spring Bean

Bean 的名称

每个 Bean 拥有一个或多个标识符(identifiers),这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名(Alias)来扩充。在基于 XML 的配置元信息中,开发人员可用 id 或者 name 属性来规定 Bean 的 标识符。通常Bean 的 标识符由字母组成,允许出现特殊字符。如果要想引入 Bean 的别名的话,可在name 属性使用半角逗号(“,”)或分号(“;”) 来间隔。Bean 的 id 或 name 属性并非必须制定,如果留空的话,容器会为 Bean 自动生成一个唯一的名称。Bean 的命名尽管没有限制,不过官方建议采用驼峰的方式,更符合 Java 的命名约定。

Bean 名称生成器(BeanNameGenerator)

由 Spring Framework 2.0.3 引入,框架內建两种实现:
• DefaultBeanNameGenerator:默认通用 BeanNameGenerator 实现
• AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator 实现,起始于
Spring Framework 2.5,关联的官方文档:
With component scanning in the classpath, Spring generates bean names for unnamed components, following the rules described earlier: essentially, taking the simple class name and turning its initial character to lower-case. However, in the (unusual) special case when there is more than one character and both the first and second characters are upper case, the original casing gets preserved.
These are the same rules as defined by java.beans.Introspector.decapitalize (which Spring uses here).

Spring Bean 的别名

Bean 别名(Alias)的价值

• 复用现有的 BeanDefinition
• 更具有场景化的命名方法,比如:

注册 Spring Bean

BeanDefinition 注册

• XML 配置元信息
<bean name=”…” … />

• Java 注解配置元信息
@Bean
@Component
@Import

• Java API 配置元信息
命名方式:BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
非命名方式:BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,Be
anDefinitionRegistry)
配置类方式:AnnotatedBeanDefinitionReader#register(Class…)

public static void registerUserBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
    BeanDefinitionBuilder beanDefinitionBuilder = genericBeanDefinition(User.class);
    beanDefinitionBuilder
            .addPropertyValue("id", 1L)
            .addPropertyValue("name", "小马哥");

    // 判断如果 beanName 参数存在时
    if (StringUtils.hasText(beanName)) {
        // 注册 BeanDefinition
        registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
    } else {
        // 非命名 Bean 注册方法
        BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(), registry);
    }
}

外部单例对象注册

Java API 配置元信息:SingletonBeanRegistry#registerSingleton

public static void main(String[] args) throws InterruptedException {
    // 创建 BeanFactory 容器
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    // 创建一个外部 UserFactory 对象
    UserFactory userFactory = new DefaultUserFactory();
    SingletonBeanRegistry singletonBeanRegistry = applicationContext.getBeanFactory();
    // 注册外部单例对象
    singletonBeanRegistry.registerSingleton("userFactory", userFactory);
    // 启动 Spring 应用上下文
    applicationContext.refresh();

    // 通过依赖查找的方式来获取 UserFactory
    UserFactory userFactoryByLookup = applicationContext.getBean("userFactory", UserFactory.class);
    System.out.println("userFactory  == userFactoryByLookup : " + (userFactory == userFactoryByLookup));

    // 关闭 Spring 应用上下文
    applicationContext.close();
}

实例化 Spring Bean

常规方式

  • 通过构造器(配置元信息:XML、Java 注解和 Java API )
  • 通过静态工厂方法(配置元信息:XML 和 Java API )
  • 通过 Bean 工厂方法(配置元信息:XML和 Java API )
  • 通过 FactoryBean(配置元信息:XML、Java 注解和 Java API )
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

   <!-- 静态方法实例化 Bean -->
    <bean id="user-by-static-method" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User"
          factory-method="createUser"/>

   <!-- 实例(Bean)方法实例化 Bean -->
   <bean id="user-by-instance-method" factory-bean="userFactory" factory-method="createUser"/>

   <!-- FactoryBean实例化 Bean -->
   <bean id="user-by-factory-bean" class="org.geekbang.thinking.in.spring.bean.factory.UserFactoryBean" />

    <bean id="userFactory" class="org.geekbang.thinking.in.spring.bean.factory.DefaultUserFactory"/>
</beans>

特殊方式

  • 通过 ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和 Java API )
  • 通过 AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
  • 通过 BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
        <property name="serviceType" value="org.geekbang.thinking.in.spring.bean.factory.UserFactory" />
    </bean>
</beans>
public class SpecialBeanInstantiationDemo {

    public static void main(String[] args) {
        // 配置 XML 配置文件
        // 启动 Spring 应用上下文
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");
        // 通过 ApplicationContext 获取 AutowireCapableBeanFactory
        AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
        ServiceLoader<UserFactory> serviceLoader = beanFactory.getBean("userFactoryServiceLoader", ServiceLoader.class);
        displayServiceLoader(serviceLoader);
//        demoServiceLoader();
        // 创建 UserFactory 对象,通过 AutowireCapableBeanFactory
        UserFactory userFactory = beanFactory.createBean(DefaultUserFactory.class);
        System.out.println(userFactory.createUser());
    }

    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());
        }
    }
}

在这里插入图片描述

初始化 Spring Bean

Bean 初始化(Initialization)

• @PostConstruct 标注方法

• 实现 InitializingBean 接口的 afterPropertiesSet() 方法

• 自定义初始化方法

  • XML 配置:<bean init-method=”init” … />
  • Java 注解:@Bean(initMethod=”init”)
  • Java API:AbstractBeanDefinition#setInitMethodName(String)
public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {

    // 1. 基于 @PostConstruct 注解
    @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 初始化中...");
    }

    @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 销毁中...");
    }

    @Override
    public void finalize() throws Throwable {
        System.out.println("当前 DefaultUserFactory 对象正在被垃圾回收...");
    }
}

假设以上三种方式均在同一 Bean 中定义,那么这些方法的执行顺序是怎样?
在这里插入图片描述

Bean 延迟初始化(Lazy Initialization)

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

@Configuration // Configuration Class
public class BeanInitializationDemo {

    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册 Configuration Class(配置类)
        applicationContext.register(BeanInitializationDemo.class);
        // 启动 Spring 应用上下文
        applicationContext.refresh();
        // 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
        System.out.println("Spring 应用上下文已启动...");
        // 依赖查找 UserFactory
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        System.out.println(userFactory);
        System.out.println("Spring 应用上下文准备关闭...");
        // 关闭 Spring 应用上下文
        applicationContext.close();
        System.out.println("Spring 应用上下文已关闭...");
    }

    @Bean(initMethod = "initUserFactory", destroyMethod = "doDestroy")
    @Lazy
    public UserFactory userFactory() {
        return new DefaultUserFactory();
    }
}

当某个 Bean 定义为延迟初始化,那么,Spring 容器返回的对象与非延迟的对象存在怎样的差异?

延迟初始化在applicationContext.getBean时进行初始化,非延迟初始化在容器刷新时初始化。

销毁 Spring Bean

Bean 销毁(Destroy)

• @PreDestroy 标注方法

• 实现 DisposableBean 接口的 destroy() 方法

• 自定义销毁方法

  • XML 配置:<bean destroy=”destroy” … />
  • Java 注解:@Bean(destroy=”destroy”)
  • Java API:AbstractBeanDefinition#setDestroyMethodName(String)

思考:假设以上三种方式均在同一 Bean 中定义,那么这些方法的执行顺序是怎样?

垃圾回收 Spring Bean

  1. 关闭 Spring 容器(应用上下文)
  2. 执行 GC
  3. Spring Bean 覆盖的 finalize() 方法被回调

面试题

如何注册一个 Spring Bean?
答:通过 BeanDefinition 和外部单体对象来注册

什么是 Spring BeanDefinition?
答:回顾“定义 Spring Bean” 和 “BeanDefinition 元信息”

Spring 容器是怎样管理注册 Bean
答:答案将在后续专题章节详细讨论,如:IoC 配置元信息读取和解析、依赖查找和注入以及 Bean 生命周期等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值