Spring Bean 综述

1,233 阅读6分钟

  作为 Java 程序员肯定避不开 Spring 相关的知识,而 Spring 容器对 Bean 的管理又是 Spring 功能中重要的一部分。作为初学者往往会对此存在一些疑惑,或者对从书上、互联网上看到的知识不能有个整体的认识。本篇文章将 Spring Bean 相关的知识做了个简单的大串讲,希望对初学者有所帮助。“综述”这个词参考论文的取名,你可以理解成综合描述,也可以理解成泛泛而谈。

何为 Bean ,何为 Spring 容器?

  面向对象语言在使用一个对象的时候分为两个步骤:

  1. 定义一个类 Class A
  2. 通过new 实例化一个 A 的对象。

  然后才能去使用这个对象。在 C++ 中我们使用一个对象的时候,都是自己去手动 new 一个。当然原生的 Java 也是通过 new去 新建对象,只不过这种方式需要程序员手动管理对象,不仅麻烦而且增加代码量。Spring 便应用而生了,Spring 的两大核心功能包括 IOC(依赖注入)和 AOP(面向切面编程)。IOC 说通俗点就是 Spring 帮我们管理类的对象,程序员想使用一个类的对象的时候不需要再主动去 new,只需要通过几种方式比如注解让 Spring 帮我们生成就可以了。要用的时候直接向 Spring 索取就好了。   所谓 Bean 就是 Spring 帮我们生成的对象,英文单词 Bean 是豌豆的意思,这样看来 Spring 像不像一个容器而各个对象就是各种豌豆呀。Spring 容器里面盛着各种 Bean(豌豆),这下明白何为Bean ,何为 Spring 容器了吧。

如何告诉 Spring 替我们生成 Bean?

  定义一个类后,如何让 Spring 帮我生成 Bean 呢?主要包括以下三种方式:

  1. 在 xml 中进行显示配置:Spring 最原始的生成 Bean 的方式,用户只需要将类生成 Bean 的信息配置到 XML 中,即可通过 Spring 生成 Bean。而当在程序中需要的 Bean 很多的时候,xml 配置文件将多的很难管理。这种方式已经逐渐被(2)(3)替代了。
  2. Spring 隐式发现和自动装配:只需要在类定义上加上@Compoennt、@Service、@Controller 等注解即可让 Spring 发现并生成 Bean,当然 Spring 要通过 @ComponentScan 开启自动扫描。这是目前最常用的方式。
  3. 在 Java 中进行显示装配:通过@Configuration 注解和 @Bean 显示的告诉 Spring 帮我们生成 Bean。很多初学者可能一开始不知道这种方式在何种情况下被用到。其实我总结下:当你发现方法 2 实现不了,又想用 new 的时候(但是 new 又不能让 Spring 帮我管理 Bean 了),就可以用这种方式。

Spring 生成 Bean 的流程是怎样的?

  手动生成对象很简单,直接 new 一下就可以了。Spring 帮我们生成 Bean 也只是简单的 new 一下吗?一起看下面这张图:

  可以看到,Spring 生成 Bean 不只是简单 new 这么简单,它要做的事情很多。具体的流程中每个节点的作用网上有很多文章讲的比我好,就不班门弄斧了。个人觉得可以参考 Spring Bean的生命周期(非常详细)这篇文章。我这里只想告诉大家掌握 Bean 的生命周期的才能更好的使用 Bean,因为很多时候我们需要在特定的流程中做一些特定的事情。当然了,面试的时候也会经常问到 Spring 的生命周期相关的问题,所以,记住它吧。

同一个类 Spring 帮我们生成几个 Bean ?

  在 new 一个类的对象的时候,我们有时候采用单例模式,也就是整个程序中该类的对象始终只有一个;有时候我们又采用多例模式,想要就 new 一个。那 Spring 帮我们生成 Bean 是采用哪种模式呢?当然这个得我们告诉 Spring,通过@Scope注解指定下就好了。Spring 生成 Bean 有五种方式分别是:

  1. singleton: 单例模式,这也是 Spring 默认生成 Bean 的方式
  2. prototype: 原型模式,每次 getBean 之后都会生成一个新的 Bean。但是需要注意的一点是获取 Bean 后 Spring 不在管理这个 Bean ,需要用户(程序员)自己去管理。需要用户自己去管理并不代表要手动释放该 Bean,而是只不能再从Spring 索取该对象了。
  3. request: request 表示针对每一次 HTTP 请求都会产生一个新的bean,同时该 bean 仅在当前 HTTP request 内有效。
  4. session: session 作用域表示针对每一次HTTP请求都会产生一个新的bean,同时该 Bean 仅在当前 http session 中有效。这种方式 Bean 的存活时间比(3)长,可以说(4)包含(3)。
  5. global session: global session 作用域类似于标准的 http session作用域,不过它仅仅在基于 portlet 的 web 应用中才有意义。portlet 规范定义了 global session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portlet 所共享。在 global session 作用域中定义的bean被限定于全局 portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。

  3/4/5 主要针对 web 程序的,一般的 Spring 应用程序只需要用到 1 和 2 就好了。

Spring 容器中的 Bean,该如何索取?

  很多时候,Spring 帮我们生成的 Bean ,在相应的地方通过 @Autowire 或者其他注解即可。但有的时候我们想从 Spring 容器中获取 Bean 并用自己的代码保存起来,不通过 @Autowire 的方式自动注入而是手动选择。在用 Spring 中使用工厂模式的时候,就能体会到从 Spring 获取 Bean 的重要性。当然,从 Spring 获取 Bean 有多种方式,可以参考下 Spring在代码中获取bean的几种方式 这篇文章。我一般使用继承 ApplicationContextAware 这种方式。这个网上也有很多现成的工具类,不在赘述。

  在这里想要强调的是,什么时机从 Spring 中索取 Bean 最合适。之前踩过的一个坑:在 B 的构造函数中获取想要类 A 的 Bean,出现了 NPE(null pointer exception) 。从 Spring 管理 Bean 生命周期的那张图可以看出,Spring 先执行所有类的构造函数来实例化所有 Bean。如果你在类 B 的构造函数中获取类 A 的 Bean,这个时候类 A 的对象极大可能还没实例化呢,所以出现 NPE,而在实例化之后的流程中获取 A 类 Bean 就不会有这个问题。比如继承InitialzationBean,在 afterPropertiesSet() 中获取 A 的 Bean 即可。

主题图片来自于网络,侵删!