4. Spring Bean 基础

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

  • Bean 类名(全限定名)

  • Bean 行为配置元素(作用域、自动绑定模式、生命周期)

  • 其他 Bean 引用,依赖

  • 配置设置,属性

4.2 BeanDefinition 元信息

4.2.1 元信息说明

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

4.2.2 BeanDefinition构建

  • 通过 BeanDefinitionBuilder

  • 通过AbstractBeanDefinition 以及派生类

// 通过 BeanDefinitionBuilder 创建 BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
AbstractBeanDefinition beanDefinition = builder.addPropertyValue("name", "foo")
    .addPropertyValue("age", 28).getBeanDefinition();
beanDefinition.addQualifier(new AutowireCandidateQualifier("user"));
beanDefinition.setScope("singleton");
​
// 通过 AbstractBeanDefinition 以及派生类创建 BeanDefinition
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(User.class);
MutablePropertyValues values = new MutablePropertyValues();
values.add("name", "foo")
    .add("age", 29);
genericBeanDefinition.setPropertyValues(values);

4.3 id 和 name

4.3.1 Bean 标识符

每个 Bean 拥有一个或者多个标识符(identifier),这些标识符在 Bean 所在的容器必须是唯一的。通常发,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名扩充,依赖注入以及依赖查找时可以使用

在基于 XML 的配置元信息中,开发人员可用 id 或者 name 属性来规定 Bean 的标识符。通畅 Bean 的标识符由字母组成,允许出现特殊字符,如果想要引入 Bean 的别名,可在 name 属性使用半角逗号或分号来间隔

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

4.3.2 Bean 名称生成器(BeanNameGenerator)

由 Spring Framework 2.0.3 引入,框架内建两种实现:

  • DefaultBeanNameGenerator:默认通用 BeanNameGenerator 实现

  • AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator 实现,起始于 Spring Framework 2.5

4.3.3 id 和 name

id 和 name 都是 bean 的标识符,id 最多一个,name 可有多个

4.4 别名

别名的价值:

  • 复用现有的 BeanDefinition

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

    • <alias name="myApp-dataSource" alias="subsystemA-dataSource">

    • <alias name="myApp-dataSource" alias="subsystemB-dataSource">

4.5 注册 Bean

4.5.1 注册方式

  • XML 配置元信息

    • <bean id="", class="">

  • Java 注解配置元信息

    • @Bean

    • @Component

    • @Import

  • Java API 配置元信息

    • 命名方式:BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)

    • 非命名方式:BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition, BeanDefinitionRegistry)

    • 配置类方式:AnnotatedBeanDefinitionReader#register(Class...)(就是注解注册中通过context.register("config", Config.class)方法进行注册)

4.5.2 注解注册

@Import(AnnotationBeanDefinitionDemo.Config.class)
public class AnnotationBeanDefinitionDemo {
    public static void main(String[] args) {
        // 创建context
        try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
            // 注册配置类
            context.register(AnnotationBeanDefinitionDemo.class);
            context.registerBean("config", Config.class);
            // 刷新容器(启动容器)
            context.refresh();
            Config config = context.getBean(Config.class);
            User user = context.getBean(User.class);
            System.out.println(user);
            System.out.println(config);
        }
    }
​
    static class Config {
        @Bean
        public User user() {
            User user = new User();
            user.setAge(28);
            user.setName("foo");
            return user;
        }
    }
}

由上面可以得出一个结论,Spring 不会进行重复注册,手动 registerBean 以及使用 @Import 注解同时注入了 Config 对象,但是不会报错,并且成功注入

4.5.3 Java API 方式注入

@Import(AnnotationBeanDefinitionDemo.Config.class)
public class AnnotationBeanDefinitionDemo {
    public static void main(String[] args) {
        try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
            context.register(AnnotationBeanDefinitionDemo.class);
            context.registerBean("config", Config.class);
            // 如果此处名字和下面@Bean中名字相同,会覆盖
            registerUserBeanDefinition(context, "newUser");
            registerUserBeanDefinition(context);
            context.refresh();
​
            System.out.println("Config 类型所有 Beans:" + context.getBeansOfType(Config.class));
            System.out.println("User 类型所有 Beans:" + context.getBeansOfType(User.class));
        }
    }
​
    public static void registerUserBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
        AbstractBeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(User.class)
                .addPropertyValue("age", 18)
                .addPropertyValue("name", "fooNew").getBeanDefinition();
        if (StringUtils.hasText(beanName)) {
            // 命名 Bean 注册方法
            registry.registerBeanDefinition(beanName, definition);
        } else {
            // 非命名 Bean 注册方法
            BeanDefinitionReaderUtils.registerWithGeneratedName(definition, registry);
        }
    }
​
    public static void registerUserBeanDefinition(BeanDefinitionRegistry registry) {
        registerUserBeanDefinition(registry, null);
    }
​
    @Component
    static class Config {
        @Bean
        public User user() {
            User user = new User();
            user.setAge(28);
            user.setName("foo");
            return user;
        }
    }
}
​
// 运行结果:
Config 类型所有 Beans:{config=com.gcl.bean.AnnotationBeanDefinitionDemo$Config@327b636c}
User 类型所有 Beans:{newUser=User{name='fooNew', age=18}, com.gcl.bean.User#0=User{name='fooNew', age=18}}

4.6 实例化 Bean

4.6.1 方法

  • 常规方式

    • 通过构造器(配置元信息: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.String, int boolean)

    • 通过 BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)

4.6.2 常规方式

public class BeanInstantiationDemo {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean-create.xml");
        System.out.println(context.getBean("user", User.class));
        System.out.println(context.getBean("factoryUser"));
        System.out.println(context.getBean("userByFactoryBean"));
        System.out.println(context.getBean("user") == context.getBean("factoryUser"));
    }
}
​
public class User {
    private String name;
    private int age;
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
​
    // 静态工厂方法创建bean
    public static User user() {
        User user = new User();
        user.setName("static");
        user.setAge(22);
        return user;
    }
}
​
// 抽象工厂创建bean
public interface UserFactory {
    User createUser();
}
​
public class DefaultUserFactory implements UserFactory {
    @Override
    public User createUser() {
        return User.user();
    }
}
​
//FactoryBean创建bean
public class UserFactoryBean implements FactoryBean<User> {
​
    private static final User USER = User.user();
​
    @Override
    public User getObject() throws Exception {
        return USER;
    }
​
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}
<?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" class="com.gcl.bean.User" factory-method="user"/>
​
    <!-- 工厂方法实例化 Bean -->
    <bean id="userFactory" class="com.gcl.bean.factory.DefaultUserFactory"/>
    <bean id="factoryUser" factory-bean="userFactory" factory-method="createUser"/>
​
    <!-- FactoryBean 实例化 Bean -->
    <bean id="userByFactoryBean" class="com.gcl.bean.factory.UserFactoryBean"/>
​
</beans>

4.6.3 非常规方式

代码

public class SpecialBeanInstantiationDemo {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:special-bean-create.xml");
​
        // 通过 applicationContext 可以获取到 AutowireCapableBeanFactory 对象
        AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
​
        // 获取到的是 FactoryBean#getObject 方法返回的对象
        ServiceLoader<UserFactory> loader = context.getBean("userFactoryServiceLoader", ServiceLoader.class);
        displayServiceLoader(loader);
​
        // 通过 AutowireCapableBeanFactory 对象实例化 Bean,通过该方法创建的 Bean 可以享受 Bean生命周期的所有阶段
        UserFactory factory = beanFactory.createBean(DefaultUserFactory.class);
        System.out.println(factory.createUser());
    }
​
    private static void displayServiceLoader(ServiceLoader<UserFactory> loader) {
        Iterator<UserFactory> iterator = loader.iterator();
        while (iterator.hasNext()) {
            UserFactory factory = iterator.next();
            System.out.println(factory.createUser());
        }
    }
}

SPI 文件

com.gcl.bean.factory.DefaultUserFactory

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 https://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <!-- ServiceLoaderFactoryBean 对象,获取时,其实真正获取到的是 ServiceLoader 对象,因为 FactoryBean 对应的 Bean 是 getObject 方法返回的对象 -->
    <bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
        <!-- ServiceLoader<UserFactory> 配置 ServiceLoader 对象泛型类型 -->
        <property name="serviceType" value="com.gcl.bean.factory.UserFactory"/>
    </bean>
​
</beans>

4.7 初始化 Bean

  • @PostConstruct 标注方法

  • 实现 InitializingBean 接口的 afterPropertiesSet 方法

  • 自定义初始化方法

    • XML 配置: <bean init-method="init" .../>

    • Java 注解:@Bean(initMethod="init")

    • Java API:AbstractBeanDefinition#setInitMethodName(String)(其实上述两个自定义方法底层就是调用的setInitMethodName 方法)

上述三种方式执行顺序?@PostConstructor > InitializingBean > 自定义初始化方法。

public class DefaultUserFactory implements UserFactory, InitializingBean {
​
    @Override
    public User createUser() {
        return User.user();
    }
​
    @PostConstruct
    public void init() {
        System.out.println("@PostConstructor : UserFactory 初始化中...");
    }
​
    public void initUserFactory() {
        System.out.println("@自定义初始化方法  : UserFactory 初始化中...");
    }
​
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean#afterPropertiesSet  初始化方法  : UserFactory 初始化中...");
    }
}
​
@Configuration
public class BeanInitializationDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
​
        // 注册配置类
        context.register(BeanInitializationDemo.class);
​
        // 启动容器
        context.refresh();
​
        // 依赖查找
        UserFactory factory = context.getBean(UserFactory.class);
        System.out.println(factory.createUser());
​
        // 关闭容器
        context.close();
    }
​
    @Bean(initMethod = "initUserFactory")
    public UserFactory factory() {
        return new DefaultUserFactory();
    }
}

// 运行结果为:
//@PostConstructor : UserFactory 初始化中...
//InitializingBean#afterPropertiesSet  初始化方法  : UserFactory 初始化中...
//@自定义初始化方法  : UserFactory 初始化中...
//User{name='static', age=22}

由结果可知三种实例化方式的优先级为:

@PostConstructor > InitializingBean > 自定义初始化方法(init-mehtod、initMethod)

4.8 延迟初始化 Bean

  • XML 配置:<bean lazy-init="true" .../>

  • Java 注解:@Lazy(true)

以上一小节例子为例:

public class BeanInitializationDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
​
        // 注册配置类
        context.register(BeanInitializationDemo.class);
​
        // 启动容器
        context.refresh();
        System.out.println("Spring 应用上下文以启动...");
​
        // 依赖查找
        UserFactory factory = context.getBean(UserFactory.class);
        System.out.println(factory.createUser());
​
        // 关闭容器
        context.close();
    }
​
    @Bean(initMethod = "initUserFactory")
    @Lazy
    public UserFactory factory() {
        return new DefaultUserFactory();
    }
}
// 如果不加@Lazy注解,“Spring 应用上下文以启动...”这句话后于初始化方法输出,如下:
@PostConstructor : UserFactory 初始化中...
InitializingBean#afterPropertiesSet  初始化方法  : UserFactory 初始化中...
@自定义初始化方法  : UserFactory 初始化中...
Spring 应用上下文以启动...
User{name='static', age=22}
// 如果加上 @Lazy 注解,输出结果如下:
Spring 应用上下文以启动...
@PostConstructor : UserFactory 初始化中...
InitializingBean#afterPropertiesSet  初始化方法  : UserFactory 初始化中...
@自定义初始化方法  : UserFactory 初始化中...
User{name='static', age=22}

可以看到延迟初始化 Bean 在容器启动时并未进行初始化,只进行了实例化,只有在真正使用时,才会触发初始化逻辑,非延迟初始化则是在容器启动时就进行初始化操作。

4.9 销毁 Bean

  • @PreDestroy 标注方法

  • 实现 DisposableBean 接口的 destroy 方法

  • 自定义销毁方法

    • XML 配置:<bean destroy="destroy" .../>

    • Java 注解:@Bean(destroy="destroy")

    • Java API:AbstractBeanDefinition#setDestroyMethodName(String)(上述两种方法底层就是调用的 setDestroyMethodName 方法)

public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {
​
    @Override
    public User createUser() {
        return User.user();
    }
​
    @PostConstruct
    public void init() {
        System.out.println("@PostConstructor : UserFactory 初始化中...");
    }
​
    @PreDestroy
    public void preDestroy() {
        System.out.println("@PreDestroy : UserFactory 销毁后...");
    }
​
    public void initUserFactory() {
        System.out.println("自定义初始化方法  : UserFactory 初始化中...");
    }
​
    public void destroyUserFactory() {
        System.out.println("自定义销毁方法 : UserFactory 销毁后...");
    }
​
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean#afterPropertiesSet 初始化方法  : UserFactory 初始化中...");
    }
​
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean#destroy 销毁方法 : UserFactory 销毁后...");
    }
}
​
@Configuration
public class BeanInitializationDemo {
    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
​
        // 注册配置类
        context.register(BeanInitializationDemo.class);
​
        // 启动容器
        context.refresh();
        System.out.println("Spring 应用上下文以启动...");
​
        // 依赖查找
        UserFactory factory = context.getBean(UserFactory.class);
        System.out.println(factory.createUser());
​
        // 关闭容器
        context.close();
    }
​
    @Bean(initMethod = "initUserFactory", destroyMethod = "destroyUserFactory")
    @Lazy
    public UserFactory factory() {
        return new DefaultUserFactory();
    }
}
// 输出结果为:
Spring 应用上下文以启动...
@PostConstructor : UserFactory 初始化中...
InitializingBean#afterPropertiesSet 初始化方法  : UserFactory 初始化中...
自定义初始化方法  : UserFactory 初始化中...
User{name='static', age=22}
@PreDestroy : UserFactory 销毁后...
DisposableBean#destroy 销毁方法 : UserFactory 销毁后...
自定义销毁方法 : UserFactory 销毁后...

销毁 Bean 优先级:@PreDestroy > DisposableBean > 自定义销毁方法(destroy-method、destroyMethod)

4.10 回收 Bean

  1. 关闭 Spring 容器(应用上下文)

  2. 执行 GC

  3. Speing Bean 覆盖的 finalize() 方法

也就是说在 Spring 关闭以后,当发生 GC 时会回收容器中的 Bean,回收方法按照 GC 方法来进行,可达性分析以及 finalize 自救

4.11 面试题

4.11.1 如何注册 Bean

通过 BeanDefinition (XML、注解@Bean,@Component等、Java API方式(BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)等))和外部单体对象来注册(外部单体对象生命周期并不由 Spring 来管理,但是也可以被容器托管),如下:

public class SingletonBeanRegistrationDemo {
    public static void main(String[] args) {
        // 创建 Spring 容器(上下文)
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 创建外部单体对象
        DefaultUserFactory factory = new DefaultUserFactory();
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 注册外部单体对象
        beanFactory.registerSingleton("userFactory", factory);
        context.refresh();
        // 依赖查找
        UserFactory factory1 = context.getBean(UserFactory.class);
        System.out.println(factory == factory1);
    }
}
// 打印结果:true

4.11.2 什么是 Spring BeanDifinition

BeanDefinition 实际上是关于 Bean 定义的元数据信息,包括scope、lazy、init-method、destroy-method、beanName、primary、role、autowire等

4.11.3 Spring 容器是如何注册 Bean 的

IOC 配置元信息读取和解析、依赖查找和注入以及 Bean 生命周期等(后续详解)