简介

spring-bean 组件是 IoC 的核心,我们可以通过BeanFactory来获取所需的对象,对象的实例化、属性装配和初始化都可以交给 spring 来管理。

针对 spring-bean 组件,我计划分成两篇博客来讲解。本文会详细介绍这个组件,包括以下内容。下一篇再具体分析它的源码。

  1. spring-bean 组件的相关概念:实例化、属性装配、初始化、bean、beanDefinition、beanFactory。
  2. bean 组件的使用:注册bean、获取bean、属性装配、处理器等。

项目环境说明

正文开始前,先介绍下示例代码使用的环境等。

工程环境

JDK:1.8.0_231

maven:3.6.1

IDE:Spring Tool Suites4 for Eclipse 4.12

Spring:5.2.6.RELEASE

依赖引入

除了引入 spring,这里还额外引入了日志和单元测试。

    <properties>
<spring.version>5.2.6.RELEASE</spring.version>
</properties> <dependencies>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<type>jar</type>
</dependency>
</dependencies>

几个重要概念

实例化、属性装配和初始化

在 spring-bean 组件的设计中,这三个词完整、有序地描述了生成一个新对象的整个流程,是非常重要的理论基础。它们的具体含义如下:

  1. 实例化:使用构造方法创建出一个新对象。
  2. 属性装配:给对象的成员属性赋值。
  3. 初始化:调用对象的初始化方法。

下面使用一段代码来简单演示下这个流程。

public class UserService implements IUserService {

    private UserDao userDao;

    public UserService() {
super();
System.err.println("UserService构造方法被调用");
System.err.println(" ||");
System.err.println(" \\/");
} public void init() {
System.err.println("UserService的init方法被调用");
System.err.println(" ||");
System.err.println(" \\/");
} public UserDao getUserDao() {
return userDao;
} public void setUserDao(UserDao userDao) {
System.err.println("UserService的属性装配中");
System.err.println(" ||");
System.err.println(" \\/");
this.userDao = userDao;
} }

如果我们将这个 bean 交给 spring 管理,获取 bean 时会在控制台打印以下内容:

什么是bean

按照官方的说法, bean 是一个由 Spring IoC 容器实例化、组装和管理的对象。我认为,这种表述是错误的,通过registerSingleton方式注册的 bean,它就不是由 Spring IoC 容器实例化、组装,所以,更准确的表述应该是这样:

对象的实例,或者它的描述对象被注册到了 Spring IoC 容器,并且通过 Spring IoC 容器来获取得到的对象,就是 bean

举个例子,使用了 Spring 的项目中, Controller 对象、Service 对象、DAO 对象等都属于 bean。

至于什么是 IoC 容器,在 spring-bean 组件中,我认为,beanFactory 就属于 IoC 容器。

什么是beanFactory

从客户端来看,一个完整的 beanFactory 工厂一般包含以下功能:

  1. 注册别名。对应下图的AliasRegistry接口。
  2. 注册单例对象。对应下图的SingletonBeanRegistry接口。
  3. 注册BeanDefinition对象。对应下图的BeanDefinitionRegistry接口。
  4. 获取 bean。对应下图的BeanFactory接口。

在 spring-bean 组件中,DefaultListableBeanFactory就是一个完整的 beanFactory 工厂,也可以说是一个 IoC 容器。接下来的例子将直接使用它来作为 beanFactory。

至于其他的接口,这里也补充说明下。HierarchicalBeanFactory用于提供父子工厂的支持,ConfigurableBeanFactory用于提供配置 beanFactory 的支持,ListableBeanFactory用于提供批量获取 bean 的支持(不包含父工厂的 bean),AutowireCapableBeanFactory用于提供实例化、属性装配、初始化等一系列管理 bean 生命周期的支持。

什么是beanDefinition

beanDefinaition 是一个描述对象,用来描述 bean 的实例化、初始化等信息。

在 spring-bean 组件中,beanDefinaition主要包含以下四种:

  1. RootBeanDefinition:beanFactory 中最终用于 createBean 的 beanDefinaition,不允许添加 parentName。在 BeanFactory 中以下三种实现类都会被包装成RootBeanDefinition用于 createBean。
  2. ChildBeanDefinition必须设置 parentName 的 beanDefinaition。当某个 Bean 的描述对象和另外一个的差不多时,我们可以直接定义一个ChildBeanDefinition,并设置它的 parentName 为另外一个的 beanName,这样就不用重新设置一份。
  3. GenericBeanDefinition:通用的 beanDefinaition,可以设置 parentName,也可以不用设置
  4. AnnotatedGenericBeanDefinition:在GenericBeanDefinition基础上增加暴露注解数据的方法。

spring-bean 组件提供了BeanDefinitionBuilder用于创建 beanDefinaition,下面的例子会频繁使用到。

使用例子

入门--简单地注册和获取bean

下面通过一个入门例子来介绍注册和获取 bean 的过程。

    @Test
public void testBase() {
// 创建BeanFactory对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 创建BeanDefinition对象
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition(); // 注册Bean
beanFactory.registerBeanDefinition("userService", rootBeanDefinition); // 获取Bean
IUserService userService = (IUserService)beanFactory.getBean("userService");
System.err.println(userService.get("userId"));
}

两种注册bean的方式

beanFactory 除了支持注册 beanDefinition,还允许直接注册 bean 实例,如下。

    @Test
public void testRegisterWays() {
// 创建BeanFactory对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 注册Bean-- BeanDefinition方式
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();
beanFactory.registerBeanDefinition("userService", rootBeanDefinition); // 注册Bean-- Bean实例方式
beanFactory.registerSingleton("userService2", new UserService()); // 获取Bean
IUserService userService = (IUserService)beanFactory.getBean("userService");
System.err.println(userService.get("userId"));
IUserService userService2 = (IUserService)beanFactory.getBean("userService2");
System.err.println(userService2.get("userId"));
}

当然,这种方式仅支持单例 bean 的注册,多例的就没办法了。

多种获取bean的方式

beanFactory 提供了多种方式来获取 bean 实例,如下。如果同时使用 beanName 和 beanType,获取到指定 beanName 的 bean 后会进行类型检查和类型类型,如果都不通过,将会报错。

    @Test
public void testGetBeanWays() {
// 创建BeanFactory对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 创建BeanDefinition对象
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition(); // 注册Bean
beanFactory.registerBeanDefinition("userService", rootBeanDefinition); // 获取Bean--通过BeanName
IUserService userService = (IUserService)beanFactory.getBean("userService");
System.err.println(userService.get("userId"));
// 获取Bean--通过BeanType
IUserService userService2 = beanFactory.getBean(IUserService.class);
System.err.println(userService2.get("userId"));
// 获取Bean--通过BeanName+BeanType的方式
IUserService userService3 = beanFactory.getBean("userService", IUserService.class);
System.err.println(userService3.get("userId"));
}

另外,通过 beanName 获取 bean,这个 beanName 包含以下三种形式:

  1. beanName。 如果对应的 bean 是FactoryBean,不会返回FactoryBean的实例,而是会返回FactoryBean.getObject方法的返回结果。
  2. alias。通过 alias 对应的 beanName 来获取 Bean。
  3. '&' + factorybeanName。可以返回FactoryBean的实例,形式为:一个或多个& + factorybeanName。
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        // 注册Bean--注册的是一个 FactoryBean
UserServiceFactoryBean userServiceFactoryBean = new UserServiceFactoryBean();
beanFactory.registerSingleton("userServiceFactoryBean", userServiceFactoryBean); // 注册BeanName的别名
beanFactory.registerAlias("userServiceFactoryBean", "userServiceAlias01"); // 通过BeanName获取
assertEquals(userServiceFactoryBean.getObject(), beanFactory.getBean("userServiceFactoryBean")); // 通过别名获取
assertEquals(userServiceFactoryBean.getObject(), beanFactory.getBean("userServiceAlias01")); // 通过&+FactoryBeanName的方式
assertEquals(userServiceFactoryBean, beanFactory.getBean("&UserServiceFactoryBean"));

bean冲突的处理

通过 beanType 的方式获取 bean,如果存在多个同类型的 bean且无法确定最优先的那一个,就会报错。

    @Test
public void testPrimary() {
// 创建BeanFactory对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 创建BeanDefinition对象
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
BeanDefinition rootBeanDefinition2 = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition(); // 注册Bean
beanFactory.registerBeanDefinition("UserRegisterBeanDefinition", rootBeanDefinition);
beanFactory.registerBeanDefinition("UserRegisterBeanDefinition2", rootBeanDefinition2);
beanFactory.registerSingleton("UserRegisterSingleton", new User("zzs002", 19));
beanFactory.registerSingleton("UserRegisterSingleton2", new User("zzs002", 18)); // 获取Bean--通过BeanType
User user = beanFactory.getBean(User.class);
System.err.println(user);
}

运行以上方法,将出现 NoUniqueBeanDefinitionException 的异常。

针对上面的这种问题,可以采取两种解决方案:

  1. 设置BeanDefinition对象的 isPrimary = true。这种方式不适用于 registerSingleton 的情况。
  2. 为 beanFactory 设置比较器。

其中,1 方案要优先于 2 方案。

    @Test
public void testPrimary() {
// 创建BeanFactory对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 为BeanFactory设置比较器
beanFactory.setDependencyComparator(new OrderComparator() {
@Override
public Integer getPriority(Object obj) {
return obj.hashCode();
}
}); // 创建BeanDefinition对象
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
// rootBeanDefinition.setPrimary(true); // 设置BeanDefinition对象为isPrimary
BeanDefinition rootBeanDefinition2 = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition(); // 注册Bean
beanFactory.registerBeanDefinition("userRegisterBeanDefinition", rootBeanDefinition);
beanFactory.registerBeanDefinition("userRegisterBeanDefinition2", rootBeanDefinition2);
beanFactory.registerSingleton("userRegisterSingleton", new User("zzs002", 19));
beanFactory.registerSingleton("userRegisterSingleton2", new User("zzs002", 18)); // 获取Bean--通过BeanType
User user = beanFactory.getBean(User.class);
System.err.println(user);
}

获取多例对象

默认情况下,我们从 beanFactory 获取到的 bean 都是单例的,即同一个对象,实际项目中,有时我们需要获取到多例的 bean,这个时候就可以通过设置 beanDefinition 的 scope 来处理。如下:

    @Test
public void testScope() {
// 创建BeanFactory对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 注册Bean-- BeanDefinition方式
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();
rootBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
beanFactory.registerBeanDefinition("userService", rootBeanDefinition); // 获取Bean--通过BeanType
IUserService userService1 = beanFactory.getBean(IUserService.class);
IUserService userService2 = beanFactory.getBean(IUserService.class);
assertNotEquals(userService1, userService2);
}

使用TypeConverter获取自定义类型的对象

当我们同时使用 beanName + beanType 来获取 bean 时,如果获取到的 bean 不是指定的类型,这时,不会立即报错,beanFactory 会尝试使用TypeConverter来强制转换。而这个类型转换器我们可以自定义设置,如下。

    @Test
public void testTypeConverter() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 注册类型转换器
beanFactory.setTypeConverter(new TypeConverterSupport() {
@SuppressWarnings("unchecked")
@Override
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
// 将User转换为UserVO
if(UserVO.class.equals(requiredType) && User.class.isInstance(value)) {
User user = (User)value;
return (T)new UserVO(user);
}
return null;
}
}); BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
beanFactory.registerBeanDefinition("User", rootBeanDefinition); UserVO bean = beanFactory.getBean("User", UserVO.class);
Assert.assertTrue(UserVO.class.isInstance(bean));
}

属性装配

如果我想在UserService中注入UserDao,首先,需要在UserService中添加定义的 setter/getter 方法,如下:

public class UserService implements IUserService {

    private UserDao userDao;

    public void save(User user) {
System.err.println("Service save user:" + user);
userDao.save(user);
} public UserDao getUserDao() {
return userDao;
} public void setUserDao(UserDao userDao) {
this.userDao = userDao;
} }

在注册 bean 时需要注意,UserDao的 bean 也需要注册,而且需要更改 userServiceBeanDefinition 的 autowireType 为按 beanType 注入或按 beanName 注入。

    @Test
public void testPopulate() {
// 创建BeanFactory对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 创建BeanDefinition对象
AbstractBeanDefinition userServiceBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();
// userServiceBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
userServiceBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
AbstractBeanDefinition userDaoBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserDao.class).getBeanDefinition(); // 注册Bean
beanFactory.registerBeanDefinition("userService", userServiceBeanDefinition);
beanFactory.registerBeanDefinition("userDao", userDaoBeanDefinition); // 获取Bean
IUserService userService = (IUserService)beanFactory.getBean("userService");
userService.save(null);
}

运行以上方法,属性装配正常。

bean 实例化、属性装配和初始化的处理器

前面讲到,我们将 bean 的实例化、属性装配和初始化都交给了 spring 处理,然而,有时我们需要在这些节点对 bean 进行自定义的处理,这时就需要用到 beanPostProcessor。

这里我简单演示下如何添加处理器,以及处理器的执行时机,至于处理器的具体实现,我就不多扩展了。

    @Test
public void testPostProcessor() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 添加实例化处理器
beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if(UserService.class.equals(beanClass)) {
System.err.println("实例化之前的处理。。 --> ");
}
return null;
} @Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if(UserService.class.isInstance(bean)) {
System.err.println("实例化之后的处理。。 --> ");
}
return true;
}
}); // 添加属性装配处理器
beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { @Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if(UserService.class.isInstance(bean)) {
System.err.println("设置参数前对参数进行调整 --> ");
}
return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
} @Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
if(UserService.class.isInstance(bean)) {
System.err.println("设置参数前对参数进行检查依赖关系 --> ");
}
return InstantiationAwareBeanPostProcessor.super.postProcessPropertyValues(pvs, pds, bean, beanName);
} }); // 添加初始化处理器
beanFactory.addBeanPostProcessor(new BeanPostProcessor() { @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(UserService.class.isInstance(bean)) {
System.err.println("初始化前,对Bean进行改造。。 --> ");
}
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(UserService.class.isInstance(bean)) {
System.err.println("初始化后,对Bean进行改造。。 --> ");
}
return bean;
}
}); AbstractBeanDefinition userServiceBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();
AbstractBeanDefinition userDaoBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserDao.class).getBeanDefinition();
userServiceBeanDefinition.setInitMethodName("init");
userServiceBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
beanFactory.registerBeanDefinition("userService", userServiceBeanDefinition);
beanFactory.registerBeanDefinition("userDao", userDaoBeanDefinition); IUserService userService = (IUserService)beanFactory.getBean("userService");
System.err.println(userService.get("userId"));
}

运行以上方法,控制台打印出了整个处理流程。实际开发中,我们可以通过设置处理器来改变改造生成的 bean 。

以上,基本介绍完 spring-bean 组件的使用,下篇博客再分析源码,如果在分析过程中发现有其他特性,也会在这篇博客的基础上扩展。

相关源码请移步:spring-beans

本文为原创文章,转载请附上原文出处链接:https://www.cnblogs.com/ZhangZiSheng001/p/13126053.html

Spring源码系列(一)--详解介绍bean组件的更多相关文章

  1. Spring源码系列 — BeanDefinition

    一.前言 回顾 在Spring源码系列第二篇中介绍了Environment组件,后续又介绍Spring中Resource的抽象,但是对于上下文的启动过程详解并未继续.经过一个星期的准备,梳理了Spri ...

  2. Spring Boot源码中模块详解

    Spring Boot源码中模块详解 一.源码 spring boot2.1版本源码地址:https://github.com/spring-projects/spring-boot/tree/2.1 ...

  3. Spring源码系列 — 注解原理

    前言 前文中主要介绍了Spring中处理BeanDefinition的扩展点,其中着重介绍BeanDefinitionParser方式的扩展.本篇文章承接该内容,详解Spring中如何利用BeanDe ...

  4. Spring源码系列 — Bean生命周期

    前言 上篇文章中介绍了Spring容器的扩展点,这个是在Bean的创建过程之前执行的逻辑.承接扩展点之后,就是Spring容器的另一个核心:Bean的生命周期过程.这个生命周期过程大致经历了一下的几个 ...

  5. Spring源码系列(四)--spring-aop是如何设计的

    简介 spring-aop 用于生成动态代理类(底层是使用 JDK 动态代理或 cglib 来生成代理类),搭配 spring-bean 一起使用,可以使 AOP 更加解耦.方便.在实际项目中,spr ...

  6. Spring源码系列 — BeanDefinition扩展点

    前言 前文介绍了Spring Bean的生命周期,也算是XML IOC系列的完结.但是Spring的博大精深,还有很多盲点需要摸索.整合前面的系列文章,从Resource到BeanDefinition ...

  7. Android源码下载方法详解

    转自:http://www.cnblogs.com/anakin/archive/2011/12/20/2295276.html Android源码下载方法详解 相信很多下载过内核的人都对这个很熟悉 ...

  8. 【Java】HashMap源码分析——常用方法详解

    上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...

  9. 【转】ANDROID自定义视图——onMeasure,MeasureSpec源码 流程 思路详解

    原文地址:http://blog.csdn.net/a396901990/article/details/36475213 简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量—— ...

随机推荐

  1. bzoj 1072状压DP

    1072: [SCOI2007]排列perm Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2293  Solved: 1448[Submit][St ...

  2. 【Python】利用python自动发送邮件

    前言 在训练网络的过程中,需要大量的时间,虽然可以预估网络训练完成时间,但蹲点看结果着实有点不太聪明的亚子. 因此,参照师兄之前发的python利用smtp自动发邮件的代码,我作了些调整,并参照网上的 ...

  3. 【JUC】CyclicBarrier和Semaphore的使用

    CyclicBarrier的使用 CyclicBarrier:可以让一组检测到一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有的屏障拦截的线程才会继续执行,线程进入屏障通过Cyclic ...

  4. Linux学习(一):常用命令

    init 0:关机 init 3:命令行模式 init 5:图形界面模式 init 6:重启 shutdown -h now:立马关机 ls:文件列表 参数:-l 详细列表 cd:切换目录 用法实例: ...

  5. 飞机调度 Now or Later? LA 3211 (2-SAT问题)

    洛谷题目传送门 题目描述 有n架飞机需要着陆.每架飞机都可以选择“早着陆”和“晚着陆”两种方式之一,且必须选择一种.第i架飞机的早着陆时间为Ei,晚着陆时间为Li,不得在其他时间着陆.你的任务是为这些 ...

  6. [工具-004]如何从apk中提取AndroidManifest.xml并提取相应信息

    跟上一篇类似,我们也需要对APK的一些诸如umengkey,ADkey,TalkingData进行验证,那么我们同样需要解压apk文件,然后提取其中的AndroidManifest.xml.然后解析x ...

  7. Java中的集合类型体系(一)

    Java中的集合类型体系(一) 提问:为什么需要集合? 通常情况下,程序需要根据运行时才知道创建了多少对象.若非程序运行时,而在开发阶段,我们并不知道创建了多少对象,甚至不知道对象的准确类型,为了满足 ...

  8. shell日期格式化、加减运算

    #!/bin/bash echo i love you输出:i love you =======================================反引号的作用============== ...

  9. 【转】eclipse找不到或无法加载主类

    Eclipse错误: 找不到或无法加载主类或项目无法编译10种解决大法! 今天启动项目,又遇到找不到或无法加载主类的情况,清除项目后无法编译,bin文件夹下没有.class文件,至少遇到3次这个问题了 ...

  10. 03.Django-ORM

    ORM 1. 数据库配置 配置使用sqlite3,mysql,oracle,postgresql等数据库 sqlite3数据库配置 DATABASES = { 'default': { # 默认使用的 ...