1 Spring Bean
1.1 Spring Bean 概念
Spring Bean 就是被 Spring IOC 容器初始化、装配和管理的对象。这些 beans 通过容器中配置的元数据创建。
Spring Bean 的定义包含了容器所需要的元数据,包括如何创建一个 bean,生命周期,依赖等等。
1.2 Spring Bean 配置方式/提供配置元数据
XML 配置文件、注解的配置、Java 的配置。
1.3 Spring 配置文件包含了哪些信息
Spring 配置文件是个 XML 文件,这个文件包含了类信息,描述了如何配置它们,以及如何相互调用。
1.4 Spring Bean 作用域
- singleton:单例模式,默认作用域,整个 Spring 容器生命周期内只创建这一个 Bean。
- prototype:原型模式(多例模式),Spring 容器初始化时不创建 Bean 的实例,而在每次通过 getBean 获取 Bean 时会创建一个新的 Bean 实例。
- request:请求模式,用于 Web 开发,在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。
- session:会话模式,用于 Web 开发,在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP session,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。
我们可以通过 XML <bean> 元素中的 scope 属性或者 @Scope 注解来设置 Bean 的作用域,例如:
- XML 中设置作用域:
<bean id="" class="" scope="prototype" /> - 注解设置作用域:
@Scope("prototype")或者@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
在上述五种作用域中,实际开发中 singleton 和 prototype 是最常用的两种。
// 基于 XML
// <bean id="user" class="com.thr.pojo.User" scope="prototype"/>
// 基于注解
@ComponentScan(basePackages = "com.thr.pojo")
@Configuration
public class PojoConfig {
public PojoConfig() { System.out.println("PojoConfig容器初始化成功..."); }
@Bean(name = "user")
@Scope(value = "singleton")
public User user(){ return new User(); }
}1.5 Spring Bean 是否有线程安全/线程并发问题,如何解决
线程安全问题主要是:多个线程操作同一个对象的时候是存在资源竞争,这主要发生在该对象是有状态的情况下。其中,对象有状态是指对象存储了数据,无状态是指对象没有存储数据。
原型模式,由于每次创建新对象,因此不会出现线程安全问题。
单例模式,Spring 框架没有对单例 Bean 进行多线程的封装处理。因此对象无状态(dao service)时,不存在线程安全问题;对象有状态(view model)时,存在线程安全问题。
线程安全问题解决:
一是,作用域更改为原型模式,但是会创建和销毁很多对象。
二是,在 bean 中尽量只定义不变的成员变量。
三是,synchronized 关键字,使用时间换空间的方式,使用加锁保证访问不冲突。
四是,在类中定义一个 ThreadLocal 类的变量,将可变变量存在 ThreadLocal 中。推荐!
ThreadLocal 涉及到 Java 线程,这个类可以让每个线程都有自己的专属本地变量。
Spring 中使用 ThreadLocal 方式来处理线程并发问题,如 RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder 等。
1.6 bean 的生命周期
详细可见:Spring-32 Bean 生命周期

Bean 生命周期主要包含四个部分:实例化、填充属性、初始化、销毁。
实例化,实例化一个 bean 对象。
填充属性,为 bean 设置相关属性和依赖。
初始化,包含五个步骤。第一步,若实现了 Aware 接口,则调用相应方法;第二步,执行 BeanPostProcessor 的 postProcessBeforeInitialization 方法;第三步,若实现了
InitializingBean 接口,则执行 afterPropertiesSet 方法;第四步,若包含 init-method 属性,则执行指定方法;第五步,执行 BeanPostProcessor 的 postProcessAfterInitialization 方法,与第二步相对应。
销毁,包含两个步骤。第一步,若实现了 DisposableBean 接口,执行 destroy 方法;第二部,若包含 destroy-method 属性,则执行指定方法。
实例化:
- Bean 容器找到配置文件中 Spring Bean 的定义。
- Bean 容器利用 Java Reflection API 创建一个 Bean 的实例。
填充属性: - 如果涉及到一些属性值利用
set()方法设置一些属性值。
初始化: - 如果 Bean 实现了
BeanNameAware接口,调用setBeanName()方法,传入 Bean 的名字。 - 如果 Bean 实现了
BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。 - 如果 Bean 实现了
BeanFactoryAware接口,调用setBeanFactory()方法,传入BeanFactory对象的实例。 - 与上面的类似,如果实现了其他
*.Aware接口,就调用相应的方法。 - 如果有和加载这个 Bean 的 Spring 容器相关的
BeanPostProcessor对象,执行postProcessBeforeInitialization()方法 - 如果 Bean 实现了
InitializingBean接口,执行afterPropertiesSet()方法。 - 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
- 如果有和加载这个 Bean 的 Spring 容器相关的
BeanPostProcessor对象,执行postProcessAfterInitialization()方法
销毁: - 当要销毁 Bean 的时候,如果 Bean 实现了
DisposableBean接口,执行destroy()方法。 - 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
其他参考:Spring Bean 的生命周期(非常详细) - Chandler Qian - 博客园
1.7 哪些是重要的 bean 生命周期方法? 你能重载它们吗?
有两个重要的 bean 生命周期方法,第一个是 setup ,它是在容器加载 bean 的时候被调用。第二个方法是 teardown ,它是在容器卸载类的时候被调用。
bean 标签有两个重要的属性( init-method 和 destroy-method )。用它们你可以自己定制初始化和注销方法。它们也有相应的注解( @PostConstruct 和 @PreDestroy )。
1.8 Spring 内部 Bean 概念(Spring Inner Bean)
当一个 bean 仅被用作另一个 bean 的属性时,它能被声明为一个内部 bean。
内部 bean 可以用 setter 注入和构造方法注入的方式来实现;通常是匿名的;作用域一般是 prototype。
1.9 将一个类声明为 bean 的注解有哪些?
我们一般使用 @Autowired 注解自动装配 bean,要想把类标识成可用于 @Autowired 注解自动装配的 bean 的类, 采用以下注解可实现:
@Component:通用的注解,可标注任意类为Spring组件。如果一个 Bean 不知道属于哪个层,可以使用@Component注解标注。@Repository: 对应持久层即 Dao 层,主要用于数据库相关操作。@Service: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。@Controller: 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。
1.10 Spring 中可以注入一个 null 和一个空字符串?
可以。
1.11 Spring 如何解决出现同名的 bean
- 同一个配置文件内同名的 Bean,以最上面定义的为准。
- 不同配置文件中存在同名 Bean,后解析的配置文件会覆盖先解析的配置文件。
- 同文件中 ComponentScan 和 @Bean 出现同名 Bean。同文件下 @Bean 的会生效,@ComponentScan 扫描进来不会生效。通过@ComponentScan 扫描进来的优先级是最低的,原因就是它扫描进来的 Bean 定义是最先被注册的。
1.12 Spring 如何解决循环依赖问题
- 构造器的循环依赖: 这种依赖 spring 是处理不了的,直接抛出 BeanCurrentlyIncreationException 异常。
- 单例模式下的 setter 循环依赖: 通过“三级缓存”处理循环依赖。
- 非单例循环依赖: 无法处理。