Last updated: November 15, 2020 8:44 PM

定义Spring Bean

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

  • Bean 的类名
  • Bean 行为配置元素,如作用域、自动绑定的模式,生命周期回调等
  • 其他Bean 引用,又可称作合作者(collaborators)或者依赖(dependencies)
  • 配置设置,比如Bean 属性(Properties)

BeanDefinition 元信息

属性(Property) 说明
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的创建

通过 BeanDefinitionBuilder 构建

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

通过 AbstractBeanDefinition 以及派生类

GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
// 设置 Bean 类型
genericBeanDefinition.setBeanClass(User.class);
// 通过 MutablePropertyValues 批量操作属性
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("id", 1).add("name", "John");
// 通过 set MutablePropertyValues 批量操作属性
genericBeanDefinition.setPropertyValues(propertyValues);

命名Spring Bean

Bean 的名称

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

在基于XML 的配置元信息(并不仅限于xml文件,包括其他方式来配置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).

BeanNameGenerator

源代码org.springframework.beans.factory.support.BeanNameGenerator

public interface BeanNameGenerator {
	String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}

DefaultBeanNameGenerator

org.springframework.beans.factory.support.DefaultBeanNameGenerator:

public class DefaultBeanNameGenerator implements BeanNameGenerator {
	// 共有API,要继承旧版本,因此构造函数必须为public
    // 使用单例模式进行实现,以节约资源。
    public static final DefaultBeanNameGenerator INSTANCE = new DefaultBeanNameGenerator();

	@Override
	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		return BeanDefinitionReaderUtils.generateBeanName(definition, registry);
	}
}

Spring Bean 的别名

Bean 别名(Alias)的价值

  • 复用现有的BeanDefinition

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

    <alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
    <alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

代码讲解

xml 配置
<!-- 导入第三方 Spring XML 配置文件 -->
<import resource="classpath:/META-INF/dependency-lookup-context.xml" />

<!-- 将 Spring 容器中 "user" Bean 关联/建立别名 - "xiaomage-user" -->
<alias name="user" alias="xiaomage-user" />
Java使用
// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-definitions-context.xml");
// 通过别名 xiaomage-user 获取曾用名 user 的 bean
User user = beanFactory.getBean("user", User.class);
User xiaomageUser = beanFactory.getBean("xiaomage-user", User.class);
System.out.println("xiaomage-user 是否与 user Bean 相同:" + (user == xiaomageUser));

运行结果为

xiaomage-user 是否与 user Bean 相同:true

注册Spring Bean

BeanDefinition 注册

XML 配置元信息

<bean name=”...” ... />

Java 注解配置元信息

@Bean

定义
public static class Config {
    // 1. 通过 @Bean 方式定义
    // 通过 Java 注解的方式,定义了一个 Bean
    @Bean(name = {"user", "xiaomage-user"}) // Bean的 name 配置为可选项
    public User user() {
        User user = new User();
        user.setId(1L);
        user.setName("John");
        return user;
    }
}

@Component

定义

在Config类上增加@Component注解,定义当前类为Spring Bean(组件)

// 2. 通过 @Component 方式
@Component // 定义当前类作为 Spring Bean(组件)
public static class Config {
    // 通过 Java 注解的方式,定义了一个 Bean
    @Bean(name = {"user", "xiaomage-user"}) // Bean的 name 配置为可选项
    public User user() {
        User user = new User();
        user.setId(1L);
        user.setName("John");
        return user;
    }
}

@Import

在主类上通过使用@Import进行引入。

// 3. 通过 @Import 来进行导入
@Import(AnnotationBeanDefinitionDemo.Config.class)
public class AnnotationBeanDefinitionDemo {
    // 省略内容……
}

使用

上述通过三种不同的方法定义了bean: UserConfigComponent。那么,ConfigComponent都定义了bean,是否会生成不同的Bean呢?

下面的代码可以显示AnnotationBeanDefinitionDemoConfigUser着三个Bean是否有重复注册。

// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类)
applicationContext.register(AnnotationBeanDefinitionDemo.class);

// 启动 Spring 应用上下文
applicationContext.refresh();

// 按照类型依赖查找
System.out.println("Config 类型的所有 Beans: " + applicationContext.getBeansOfType(Config.class));
System.out.println("User 类型的所有 Beans: " + applicationContext.getBeansOfType(User.class));

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

输出结果为:

Config 类型的所有 Beans: {bean.definition.AnnotationBeanDefinitionDemo$Config=bean.definition.AnnotationBeanDefinitionDemo$Config@f6efaab}
User 类型的所有 Beans: {user=User{id=1, name='John'}}

Config只有一个,User也只有一个。就是说,在Spring容器中,不会进行重复注册。

Java API 配置元信息

命名方式

BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)

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

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

非命名方式

BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,Be anDefinitionRegistry)

public static void registerUserBeanDefinition(BeanDefinitionRegistry registry) {
    registerUserBeanDefinition(registry, null);
}

使用

// 通过 BeanDefinition 注册 API 实现
// 1.命名 Bean 的注册方式
registerUserBeanDefinition(applicationContext, "mercyblitz-user");
// 2. 非命名 Bean 的注册方法
registerUserBeanDefinition(applicationContext);

命名 Bean 的注册方式 的输出为:

User 类型的所有 Beans{mercyblitz-user=User{id=1, name='John'}}

非命名 Bean 的注册方式 的输出为:

User 类型的所有 Beans{ioc.overview.domain.User#0=User{id=1, name='John'}}

如果将上述3中方式一起使用,则输出为:

User 类型的所有 Beans{mercyblitz-user=User{id=1, name='John'}, ioc.overview.domain.User#0=User{id=1, name='John'}, user=User{id=1, name='John'}}

配置类方式

AnnotatedBeanDefinitionReader#register(Class...)

该方式之前已经使用过,代码为:

// 注册 Configuration Class(配置类)
applicationContext.register(AnnotationBeanDefinitionDemo.class);

实例化Spring Bean - Bean 实例化(Instantiation)

常规方式

配置META-INF/bean-instantiation-context.xml

<!-- 静态方法实例化 Bean -->
<bean id="user-by-static-method" class="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="bean.factory.UserFactoryBean" />

<bean id="userFactory" class="bean.factory.DefaultUserFactory"/>
public class DefaultUserFactory implements UserFactory {}

通过构造器

包括带参构造器和不带参构造器。(配置元信息:XML、Java 注解和Java API )

通过静态工厂方法

采用类的静态方法。(配置元信息:XML 和Java API )
构造Domain 的User类,并加入静态方法:

@Data
public class User {
    private Long id;
    private String name;
    
    public static User createUser() {
        User user = new User();
        user.setId(1L);
        user.setName("John");
        return user;
    }
}

使用该静态方法:

// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
User user = beanFactory.getBean("user-by-static-method", User.class);
System.out.println(user);

输出为:

User{id=1, name='John'}

通过Bean 工厂方法(很少采用)

实例的工厂方法。抽象工厂模式。(配置元信息:XML和Java API )

构造抽象工厂类:

public interface UserFactory {
    default User createUser() { return User.createUser(); }
}

然后构造工厂实现类,因为Interface中已经定义默认方法,所以DefaultUserFactory无需实现该方法:

public class DefaultUserFactory implements UserFactory {}

使用:

BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
User user = beanFactory.getBean("user-by-static-method", User.class);
User userByInstanceMethod = beanFactory.getBean("user-by-instance-method", User.class);
System.out.println(user);
System.out.println(userByInstanceMethod);

System.out.println(user == userByInstanceMethod);

输出为:

User{id=1, name='John'}
User{id=1, name='John'}
flase

通过FactoryBean

(配置元信息:XML、Java 注解和Java API )

Bean的FactoryBean实现:

public class UserFactoryBean implements FactoryBean {

    @Override
    public Object getObject() throws Exception {
        return User.createUser();
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}

代码:

// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
User user = beanFactory.getBean("user-by-static-method", User.class);
User userByInstanceMethod = beanFactory.getBean("user-by-instance-method", User.class);
User userByFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);
System.out.println(user);
System.out.println(userByInstanceMethod);
System.out.println(userByFactoryBean);

System.out.println(user == userByInstanceMethod);
System.out.println(user == userByFactoryBean);
System.out.println(userByInstanceMethod == userByFactoryBean);

输出为:

User{id=1, name='John'}
User{id=1, name='John'}
User{id=1, name='John'}
flase
flase
flase

特殊方式

配置META-INF/special-bean-instantiation-context.xml

<bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
        <property name="serviceType" value="bean.factory.UserFactory" />
    </bean>

通过ServiceLoaderFactoryBean

(配置元信息:XML、Java 注解和Java API )

创建文件META-INF/services/bean.factory.UserFactory,其中,bean.factory.UserFactory是实现类的全名字,该文件中包括了实现类的全名,如有多个,分多行写入。

BeanFactory beanfactory = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");

ServiceLoader<UserFactory> serviceLoader = beanfactory.getBean("userFactoryServiceLoader", ServiceLoader.class);

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

输出为:

User{id=1, name='John'}

重构后代码为:

// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanfactory = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");

ServiceLoader<UserFactory> serviceLoader = beanfactory.getBean("userFactoryServiceLoader", ServiceLoader.class);

demoServiceLoader();

public static void demoServiceLoader() {
    ServiceLoader<UserFactory> 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());
    }
}

输出为:

User{id=1, name='John'}

如有多个,则会依次输出所有的bean。

通过AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)

通过 ApplicationContext 获取 AutowireCapableBeanFactory:

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

// 创建 UserFactory 对象,通过 AutowireCapableBeanFactory
UserFactory userFactory = beanFactory.createBean(DefaultUserFactory.class);
System.out.println(userFactory.createUser());

输出为:

User{id=1, name='John'} // displayServiceLoader 输出
User{id=1, name='John'} // System.out.println(userFactory.createUser()) 输出

通过BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)

初始化Spring Bean (Initialization)

@PostConstruct标注方法

在具体类中加入@PostConstruct标注

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

运行:

@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();
        // 依赖查找 UserFactory
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        // 关闭 Spring 应用上下文
        applicationContext.close();
    }

    @Bean
    public UserFactory userFactory() {
        return new DefaultUserFactory();
    }
}

输出为:

@PostConstruct : UserFactory 初始化中...

实现InitializingBean接口的 afterPropertiesSet()方法

在具体类中:

public class DefaultUserFactory implements UserFactory, InitializingBean {

    // 1. 基于 @PostConstruct 注解
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct : UserFactory 初始化中...");
    }
   
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中...");
    }
}

输出为:

@PostConstruct : UserFactory 初始化中...
InitializingBean#afterPropertiesSet() : UserFactory 初始化中...

自定义初始化方法

使用Java注解@Bean(initMethod=" ")

@Bean(initMethod = "initUserFactory", destroyMethod = "doDestroy")
@Lazy(value = false)
public UserFactory userFactory() {
    return new DefaultUserFactory();
}
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 初始化中...");
    }
}

输出为:

@PostConstruct : UserFactory 初始化中...
InitializingBean#afterPropertiesSet() : UserFactory 初始化中...
自定义初始化方法 initUserFactory() : UserFactory 初始化中...

使用Java API AbstractBeanDefinition#setInitMethodName(String)