1.1. Introduction to the Spring IoC Container and Beans

This chapter covers the Spring Framework implementation of the Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.

引用 Spring 官网中的一段话,我们可以看出来,Spring 的 IOC 也被称为 DI

什么是 IOC

IOC:Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想

什么是 DI

DI:Dependency Injection,即“依赖注入”,组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台

Spring 的注入

注入模型(XML)

Autowiring modes:自动**装配模型,仅仅针对 XML 配置方式**

Mode Explanation
no (Default) No autowiring. Bean references must be defined by ref elements. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system.
byName Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master and uses it to set the property.
byType Lets a property be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens (the property is not set).
constructor Analogous to byType but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.
  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
  4. default-autowire="byType">
  5. <bean id="testService" class="org.wesoft.spring.xml.TestService">
  6. </bean>
  7. <bean id="myService" class="org.wesoft.spring.xml.MyService">
  8. </bean>
  9. </beans>

在注入模型中,如:default-autowire=”byType”,此时如果注入的对象是一个接口声明,但是这个接口有多个实现类,那么就会报错

注入技术(Annotation)

那么 @Autowired 注解方式是如何注入 bean 的呢?
**

  • 通过类型寻找 bean,如果找到了,则进行注入
  • 如果找不到通过名称寻找 bean,如果找到了,则进行注入

注解方式的注入,使用的不是模型,而是注入技术,也就是说 @Autowired != default-autowire
**

@Autowired != default-autowire=”byType” @Autowired != default-autowire=”byName”

@Autowired 与 @Resource

@Autowired @Resource
注入方式 先根据类型,再根据名称注入 先根据名称,再根据类型注入
提供者 Spring 自身提供 JDK 提供
处理者 AutowiredAnnotationBeanPostProcessor CommonAnnotationBeanPostProcessor

这里还有一种注入方式是 @Inject,但是 @Inject 是 Java EE 6 规范 JSR 330 – Dependency Injection for Java 中定义的一种规范,需要引入相关 jar 包

Spring 的注入方式有几种

两种,或者三种自动注入方式

  • 构造方法注入

使用构造方法注入,与参数有关,每个参数都代表一个依赖项,如果没有特别指定构造方法,默认会选择与依赖项相关的最长的构造函数

  • setter 方法注入

依赖项提供 setter 方法,spring 根据注入模型,进行注入,如:注入模型是 byType,就会根据类型通过 setter 方法进行注入(与 setter 方法的名称无关),如:注入模型是 byName,就会根据 setter 方法的名称进行注入

  • method 注入(也可以算是一种注入的方式)

提供一个方法,然后调用 applicationContext 的 getBean 进行注入

  1. protected Command createCommand() {
  2. // notice the Spring API dependency!
  3. return this.applicationContext.getBean("command", Command.class);
  4. }

singleton 与 prototype

  • @Scope(“singleton”) :Spring 在初始化容器的时候,就会将对象放入单例池(singletonObjects)
  • @Scope(“prototype”):在 getBean(“xxx”) 的时候才会实例化该对象,并且每次 get 都会重新实例化

那么问题来了,如果单例的对象中,依赖了原型对象,问题一:原型什么时候被实例化?问题二:在单例对象中调用依赖的原型对象的方法,会每次都实例化么?

问题一:原型什么时候被实例化?

在实例化单例对象的时候,原型对象也跟着被实例化了。

问题二:调用原型方法,会每次都实例化么?

在单例对象中调用依赖的原型对象的方法,不会每次都实例化原型对象。

那么问题来了,业务场景下,问题三:需要这个原型bean,每次都会被初始化,那么应该如何做呢?

问题三:需要这个原型bean,每次都会被初始化,应该如何做呢?

方法一:将单例类继承 ApplicationContextAware 接口,然后每次都 getBean 获取 这个对象(此种方法侵入性太高,不推荐使用)

  1. public class CommandManager implements ApplicationContextAware {
  2. private ApplicationContext applicationContext;
  3. public Object process(Map commandState) {
  4. // grab a new instance of the appropriate Command
  5. Command command = createCommand();
  6. // set the state on the (hopefully brand new) Command instance
  7. command.setState(commandState);
  8. return command.execute();
  9. }
  10. protected Command createCommand() {
  11. // notice the Spring API dependency!
  12. return this.applicationContext.getBean("command", Command.class);
  13. }
  14. public void setApplicationContext(
  15. ApplicationContext applicationContext) throws BeansException {
  16. this.applicationContext = applicationContext;
  17. }
  18. }

方法二:注入 ApplicationContext 对象,通过该对象获取 bean,和继承 ApplicationContextAware 接口相同

  1. @Service
  2. public class MyService {
  3. @Autowired
  4. ApplicationContext applicationContext;
  5. public void getProtoTypeBean() {
  6. System.out.println(applicationContext.getBean(ProtoTypeBean.class).hashCode());
  7. }
  8. }

方法三:使用 @Lookup 注解,使用此注解,需要将类设置成 abstract

  1. @Service
  2. public abstract class MyService {
  3. @Lookup
  4. public abstract ProtoTypeBean lookupProtoTypeBean();
  5. public void getProtoTypeBeanForLookup() {
  6. System.out.println(lookupProtoTypeBean().hashCode());
  7. }
  8. }