1、Spring Ioc 的基本原理:BeanFactory
Spring Ioc 容器的代表就是 BeanFactory
接口,它其中定义了一些获取 Bean 的方法 getBean()
。ApplicationContext
接口扩展了 BeanFactory,还提供了与 Spring AOP 集成、国际化处理、事件传播及提供不同层次的 context 实现。我们可以从 BeanFactory 和 ApplicationContext 任选一个来使用,但是 BeanFactory 过于低级,所以 ApplicationContext
用得更加广泛。
ApplicationContext 有多个具体的实现,以供我们从不同类型的配置文件中获取 Bean 实例。以下是比较常用的一些:
- AnnotationConfigApplicationContext:从一个或多个基于 Java 的配置类中加载应用上下文
- AnnotationConfigWebContextLoader:从一个或多个基于 Java 配置类中加载 Web 应用上下文
- ClassPathXmlApplicationContext:从类路径下面一个或多个配置文件中加载上下文
- FileSystemXmlApplicationContext:从文件系统中的一个或多个配置文件中加载上下文
- 其他……
以 AnnotationConfigApplicationContext 为例,其继承关系如下:
当获取到了 ApplicationContext 之后,我们就可以使用它来获取 Bean 实例。ApplicationContext 从 BeanFactory 继承了一系列方法用来获取 Bean 实例:
Object getBean(String var1) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> Map<String, T> getBeansOfType(Class<T> var1) throws BeansException;
这里我们给的是三种基本的方法,它们还有一些多态的方法。与其区别仅在于,增加了一些可选的参数。
以 AnnotationConfigApplicationContext 的使用为例。我们只需要使用注解指定一个配置类,并通过注解指定扫描的包的地址,然后系统会自动对包进行扫描,装载 Bean 并进行管理,
@Configurable
@ComponentScan(value = "me.shouheng.spring.componentscan")
public class ComponentScanConfiguration {
}
然后,我们就可以获取 AnnotationConfigApplicationContext 实例,并从中来获取系统管理的 Bean 了,
public static void main(String...args) {
// 指定 Spring 容器的配置类为 ComponentScanConfiguration
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ComponentScanConfiguration.class);
// 通过 Bean 类的 Class 类型获取 Bean 实例
FirstBean firstBean = context.getBean(FirstBean.class);
firstBean.say();
}
当然,自定了扫描的包之后,我们还需要通过注解来指定哪些类是我们的 Bean 实例,以及它们在 Spring 容器中的表现,比如单例还是每次实例化一个实例等等,稍后我们会对其进行总结。
除了使用 Java 类和注解,我们还可以在 XML 中配置 Spring 容器。在早期的 Spring 版本中,这是比较常用的一种配置方式。它的实现方式也很简单,即使用 ClassPathXmlApplicationContext 类,通过构造方法指定 XML 配置文件的路径,拿到了 ClassPathXmlApplicationContext 实例之后,用它的方法获取 Bean 即可,
public static void main(String...args) {
// 系统会到 resources 目录下面的 componentscan 目录下面的 ComponentScan.xml 文件中寻找 Bean 实例
ApplicationContext context = new ClassPathXmlApplicationContext("componentscan/ComponentScan.xml");
// 通过 Bean 类的 Class 类型获取 Bean 实例
FirstBean firstBean = context.getBean(FirstBean.class);
firstBean.say();
}
上面是 Spring 的容器作用的基本原理,即指定了扫描的路径之后,Spring 容器会对路径自动扫描,装载 Bean,然后自动对其进行管理。显然,相对于我们显式地进行管理,它使用起来更加方便。不过,上面是它作用的基本原理。Spring 容器理解起来并不难,只是它的各种配置方式比较灵活,需要整理下才能更好的掌握。下面我们对 Spring 的常用的配置方式进行总结。
2、Spring Ioc 的配置
如上所述,早期的 Spring 通过 XML 配置的方式比较多,后来则提出了通过注解和 Java 类来进行配置的方式。下面我们来对两种配置方式进行梳理。
2.1 Xml 配置的结构
当我们使用 XML 来配置 Bean 的时候,其基本的格式如下(这里我们省略了命名空间的声明):
<beans xmlns=...>
<import resource="../hello/HelloWorld.xml"/>
<bean id="universal" class="me.shouheng.spring.universal.UniversalBean"/>
<alias name="universal" alias="my_universal"/>
</beans>
在 XML 中主要下面几种类型标签供我们使用:
import
:用于导入其他的 XML 配置文件,这样就可以让我们对配置文件进行功能分类;bean
:用来声明 Bean;alias
:用来为一个 Bean 起一个别名;beans
:beans 根标签中还可以加入 beans 子标签;description
:文件描述
注:可以在 IDEA 中通过点击标签或者标签的属性进入到 spring-beans.xml
中查看各个标签的定义和解释。
2.1.1 beans 标签
beans 标签中有几个属性需要注意下,
属性 | 取值范围 | 说明 |
---|---|---|
profile | 字符串可类型 | 用来快速切换 beans 环境,比如生成环境和开发测试环境等 |
default-lazy-init | 取值范围为 defaul, true 和 false | 同 bean 标签的 lazy-init 属性含义相同,即延迟初始化,表示只有在用到的时候才会被初始化,默认为 default,即继承外部 beans 的属性,否则为 false |
default-autowire | 取值范围为 defaul, no, byName, byType 和 constructor | 同 bean 标签的 autowire 属性含义相同,即表示自动注入的方式。默认值为 default,即继承外部 beans 的属性,否则使用 no. |
default-autowire-candidates | 字符串类型 | 同 bean 标签的 autowire-candidate 属性,通过名称样式匹配注入的对象,比如 “Service", "data” 等。 |
default-init-method | 字符串类型 | 同 bean 标签的 init-method 属性,接收字符串类型。可以写入 Bean 的方法名称,当该 beans 标签中的任何 bean 被创建的时候,都会调用该方法。 |
default-destroy-method | 字符串类型 | 同 bean 标签的 destroy-method 属性,接收字符串类型。可以指定 Bean 的方法名称,当该 beans 标签中的任何 bean 被销毁的时候,都会调用该方法。 |
特别说明:
1. profile 属性的特别说明
profile
属性用来快速切换 beans 环境,比如生成环境和开发测试环境等。使用方式是在 XML 配置文件中的末尾处通过如下方式添加如下代码:
<beans profile="dev">
<context:component-scan base-package="me.shouheng.spring.componentscan"/>
</beans>
<beans profile="dev2">
<context:component-scan base-package="me.shouheng.spring.componentscan2"/>
</beans>
激活配置的方式有以下几种:
- 在 web.xml 中通过 context-param 指定,注意这里定义的是 default 值,在非生产环境,可以用系统变量
spring.profiles.active
进行覆盖:
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>production</param-value>
</context-param>
-
启动 main 方法的时候在
Run Configuration
中通过修改虚拟机参数 VM Options 进行修改。比如-Dspring.profiles.active="dev2"
-
单元测试的时候通过注解
@ActiveProfiles("dev2")
来指定。
2.1.2 import 标签
import 标签可以指定多种数据源的配置文件,常用的如下,包括 class 路径、resource 目录下的 xml 中,文件系统和网络:
<beans xmlns=...>
<import resource="classpath:"/>
<import resource="classpath*:"/>
<import resource="DiConfig.xml"/>
<import resource="file:"/>
<import resource="http:"/>
</beans>
这里 classpath
和 classpath*
的区别是:classpath
只会到你的 class 路径中查找找文件;而 classpath*
不仅包含 class 路径,还要到 jar 文件中(class 路径)进行查找。可以在 classpath
路径上使用通配符 *
进行模糊查找,比如 classpath:applicationContext-*.xml
。
2.1.3 alias 标签
用来为 bean 指定别名,接收两个参数,一个是 name,即 bean 原来的名称;一个是 alisa,即 bean 的新的别名。
<bean id="hello" class="me.shouheng.spring.hello.beanimp.HelloApiImpl"/>
<alias name="hello" alias="h"/>
2.1.4 bean 标签
bean 标签用来指定我们的 bean 实例的构造方式,用来为 bean 的属性赋值等等。它的属性和子元素比较多,可以通过 spring-bases.xml 中的 beansAttributes 和 beanElements 两个分组来查看。
1. bean 标签的属性
属性名称 | 类型和取值范围 | 说明 |
---|---|---|
id | 字符串类型 | bean 的唯一的 id,同一 beans 标签中同一 id 不可以被多次使用 |
name | 字符串类型 | 用来指定 bean 的名称,多个 name 之间使用 , , ; 或者 分割 |
class | 字符串类型 | 用来指定 bean 的类型,除非该 bean 只作为其他 bean 的父 bean 使用 |
parent | 字符串类型 | 用来指定父 bean 的 name |
scope | 字符串类型,取值 request, session, prototype 和 singleton | 用来表示 bean 以何种方式创建,以及生命周期。 |
abstract | 布尔类型 | 默认 false,用来指定该 bean 是否作为其他的 bean 的父 bean 使用。如果使 true,那么这种 bean 不会被初始化。 |
lazy-init | 取值范围为 defaul, true 和 false | 延迟初始化,表示只有在用到的时候才会被初始化,默认为 default,即继承外部 beans 的属性,否则为 false。 |
autowire | 取值范围为 defaul, no, byName, byType 和 constructor | 表示自动注入的方式,默认值为 default,即继承外部 beans 的属性,否则使用 no. |
depends-on | 字符串类型 | 对两个 Bean,A 和 B,若在 A 的配置中设置它的 depends-on 属性为 B,那么 A 会在 B 初始化完成之后再被创建,而 B 要等到 A 销毁了之后才能被销毁 |
autowire-candidate | ||
primary | 布尔类型 | |
init-method | 字符串类型 | 可以写入 Bean 的方法名称,当该 beans 标签中的任何 bean 被创建的时候,都会调用该方法。 |
destroy-method | 字符串类型 | 可以指定 Bean 的方法名称,当该 beans 标签中的任何 bean 被销毁的时候,都会调用该方法。 |
factory-method | 字符串类型 | 与 factory-bean 属性配合使用。系统创建 Bean 的时候,使用 factory-bean 指定的工厂 Bean 和 factory-method 指定工厂方法创建该 Bean 实例 |
factory-bean | 字符串类型 | 见 factory-method 属性 |
2. Bean 标签属性的特别说明
-
scope 属性的四个取值的说明
singleton
表示单例作用域,每个容器中只会存在一个实例,而且其完整生命周期完全由 Spring 容器管理。默认就是单例的。如果 Bean 是延迟初始化的,那么该 Bean 会在首次使用的时候创建并放在单例缓存池中。prototype
表示原型,每次向容器请求获取 Bean 都返回一个全新的 Bean,即每次都创建的全新 Bean。request
表示每个请求需要容器创建一个全新 Bean。比如提交表单的数据必须是对每次请求新建一个 Bean 来保持这些表单数据,请求结束释放这些数据。session
作用域表示每个会话需要容器创建一个全新 Bean。比如对于每个用户一般会有一个会话,该用户的用户信息需要存储到会话中,此时可以将该 Bean 配置为 web 作用域。
-
延迟初始化 lazy-init:
- 延迟初始化适用于可能需要加载很大资源,而且很可能在整个应用程序生命周期中很可能使用不到的 Bean。
- 在
<beans>
标签中通过制定default-lazy-init="true"
可以将内部全部 bean 延迟初始化。
-
init-method和destroy-method:在 Bean 标签中通过这 init-method 和 destroy-method 分别指定初始化时和销毁时调用的方法。
- init-method:指定初始化方法,在构造器注入和 setter 注入完毕后执行;
- destroy-method:指定销毁方法,只有
singleton
作用域能销毁,prototype
作用域的一定不能,其他作用域不一定能; - 可以使用
@PostConstruct
和@PreDestroy
注解标记指定的方法来实现生命周期回调,要使用这种方式还要在 XML 中加入<context:annnotation-config>
标签,或者使用<context:component-scan/>
; - 实现生命周期的第三种选择是实现
InitializingBean
和DisposableBean
接口。
-
自动装配属性 autowire:自动装配就是指由 Spring 来自动地注入依赖对象,无需人工参与。Spring 支持
no
、byName
、byType
、constructor
和default
5 种自动装配,默认是no
指不支持自动装配。byName
表示按照名称装配,byType
表示按照类型自动装配,constructor
也是按照类型来装配的,只是用于构造器注入方式。我们来总结一下自动装配相关的内容:- 不是所有类型都能自动装配,Object、基本数据类型等无法自动装配;
- 数组、集合、字典类型的根据类型自动装配和普通类型的自动装配是有区别的,它们需要根据泛型信息注入;
- 自动装配的缺点就是没有了配置,在查找注入错误时非常麻烦,还有比如基本类型没法完成自动装配,所以可能经常发生一些莫名其妙的错误;
- 自动装配注入和配置注入同时存在时,配置注入的数据会覆盖自动装配注入的数据;
- 自动装配要求被装配的 Bean 中的对象实例有 Setter 方法。
- 下面是自动装配的一个示例:
我们定义如下的 FirstBean,它其中有一个 SecondBean 类型的实例字段,并且有一个 Setter 方法:
public class FirstBean {
private SecondBean secondBean;
public void setSecondBean(SecondBean secondBean) {
this.secondBean = secondBean;
}
}
那么,我们按照如下的方式进行自动装配:
<bean name="firstBean" class="me.shouheng.spring.autowire.FirstBean" autowire="byName"/>
<bean name="secondBean" class="me.shouheng.spring.autowire.SecondBean" />
3. bean 标签的子元素
这里的标签主要有:meta
、constructor-arg
、qualifier
、lookup-method
和 replaced-method
。
-
meta
标签:用来给 Bean 添加一个任意的 Metadata 信息,以键值对的形式; -
constructor-arg
标签:用来通过构造函数注入属性值,以创建 Bean 实例。它可以包含的属性包括index
属性:字符串类型,用来指定当前参数在构造函数的参数列表中的位置,以放在当构造函数参数列表中存在两个相同类型参数的情形type
属性:字符串类型,用来指定当前构造函数的参数的类型。name
属性:字符串类型,用来指定当前构造函数的参数的名称。ref
属性:字符串类型,用来指示当前的参数所引用的 bean,也可以通过<ref bean='...'/>
形式指定。value
属性:字符串类型,用来指示当前的参数引用的值,也可以通过<value>...<value/>
形式指定。
下面是一个使用实例,
<!-- 使用属性进行赋值 -->
<bean id="universal2" class="me.shouheng.spring.universal.UniversalBean">
<constructor-arg name="hiTo" value="Spring"/>
</bean>
<!-- 或者使用下面这种方式 -->
<bean id="hello" class="me.shouheng.spring.hello.beanimp.HelloApiImpl">
<constructor-arg name="value">
<value>My simple value.</value>
</constructor-arg>
</bean>
显然,constructor-arg
标签的 index
, type
和 name
属性用来指定当前的参数是构造函数中的哪一个。而 ref
和 value
用来指定当前参数的值。ref
和 value
只是ref
和 value
标签的一个补充。除了使用属性来直指定,我们还可以使用更加强大的子标签的形式来为构造函数的参数赋值。它的子标签包括 bean
, ref
, idref
, value
, null
, array
, list
, set
, map
和 props
,分别对应各种不同的数据类型。它们与 property
标签支持的类型基本一致,稍后我们会进行说明。
property
标签:用来对类的属性进行注入,这要求该类提供了该属性的 setter 方法。它提供的属性包括:name
属性:字符串类型,用来指定当前构造函数的参数的名称。ref
属性:字符串类型,用来指示当前属性所引用的 bean,也可以通过<ref bean='...'/>
形式指定。value
属性:字符串类型,用来指示属性的值,也可以通过<value>...<value/>
形式指定。
显然,name
用来指定当前属性的名称,ref
和 value
用来指定属性的值。此外,还可以通过使用子元素来指定属性的值。它在 constructor-arg
标签支持的子元素的基础上增加了 meta
标签。稍后我们会进行说明。
-
qualifier
标签:即限定描述符,用于细粒度选择候选者。它有两个子标签,type
和value
分别用来指定候选的 Bean 的类型和值。 -
lookup-method
标签:它只有两个标签name
和bean
。用来使容器覆写指定的方法,并将 bean 属性指定的 bean 作为结果返回。比如
public interface Boss {
Car haveCar();
}
<bean id="bmw" class="org.hope.spring.bean.lookup.Car" p:brand="奔驰GLC260" />
<bean id="boss" class="org.hope.spring.bean.lookup.Boss">
<lookup-method name="haveCar" bean="bmw"/>
</bean>
此时使用 boss 实例的时候,调用 haveCar 方法将返回 bwm 对应的 bean。
replaced-method
标签:与上面的标签类似,只是用来替换 bean 中的某个方法。比如下面的实例中,用来将 yiJianLian 中的onPlace()
方法替换为 yao。这里的 yao 不是一个普通的类,它需要实现 MethodReplacer 接口,其实就类似于代理模式的应用。
<bean id="yao" class="org.hope.spring.bean.replacedmethod.Yao">
</bean>
<bean id="yiJianLian" class="org.hope.spring.bean.replacedmethod.YiJianLian">
<replaced-method name="onPlace" replacer="yao"/>
</bean>
4. 赋值标签
这里我们来梳理下上面提到的一系列的赋值标签:bean
, ref
, idref
, value
, null
, array
, list
, set
, map
和 props
。下面是一个基本的数据类型注入的例子,
<bean id="hello" class="me.shouheng.spring.hello.beanimp.HelloApiImpl">
<!-- 注入普通的字符串类型 -->
<property name="string">
<value>Simple String</value>
</property>
<!-- 注入数组类型 -->
<property name="stringArray">
<array value-type="java.lang.String">
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>
<!-- 注入列表类型 -->
<property name="stringList">
<list value-type="java.lang.String">
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
<!-- 注入集合类型 -->
<property name="stringSet">
<set value-type="java.lang.String">
<value>1</value>
<value>2</value>
<value>1</value>
</set>
</property>
<!-- 注入哈希类型 -->
<property name="stringMap">
<map>
<entry value-type="java.lang.String" value="1" key="1"/>
<entry key="2" value="2"/>
</map>
</property>
</bean>
2.2 XML 配置方式总结
2.2.1 获取 bean 实例的几种方式
-
通过
<bean>
标签和constructor-arg
或者propertry
标签初始化 bean; -
通过通过静态工厂方法获取实例,即只通过
factory-method
指定。比如下面的例子中,这里要求 UniversalBean 类中包含一个静态的 staticFactory 工厂方法,并且工厂方法的参数是hiTo1
和hiTo2
:<bean id="universal4" class="me.shouheng.spring.universal.UniversalBean" factory-method="staticFactory"> <constructor-arg name="hiTo1" value="Spring"/> <constructor-arg name="hiTo2" value="Winter"/> </bean>
-
通过实例工厂方法获取实例:需要同时使用
factory-bean
和factory-method
标签。以下面的程序为例,此时 FactoryBean 实例方法 factory 中包含两个参数hiTo1
和hiTo2
:<bean id="factory" class="me.shouheng.spring.universal.FactoryBean"/> <bean id="universal5" factory-bean="factory" factory-method="factory"> <constructor-arg name="hiTo1" value="Spring"/> <constructor-arg name="hiTo2" value="Winter"/> </bean>
2.2.2 p 命名空间
可以使用 p 命名空间来简化 setter 注入,比如上面的 refBean
可以使用如下的方式进行简化:
<bean id="refBean" class="me.shouheng.spring.di.RefBean" p:diBean-ref="di"/>
注意这里需要引用 p 命名空间:
xmlns:p="http://www.springframework.org/schema/p"
它的规则其实就是 p:字段名="值"
,如是字段是引用类型,那么就是 p:字段名-ref= “Bean 的 id”
。
2.2.3 循环依赖
所谓的循环依赖就是:A 创建的时候需要使用 B,B 需要用到 C,而 C 又要用到 A。按照注入的方式不同又分成:构造器循环依赖 和 setter 方法循环依赖。构造器循环依赖表示 Bean 在创建的时候依赖于其他的 Bean 而造成的循环依赖,此种循环依赖无解,只能通过抛出 BeanCurrentlyInCreationException 异常表示循环依赖。对于 setter 方法循环依赖又分成两种情形:单例作用域的循环依赖 和 prototype 作用域的循环依赖。前者是可以解决的,而后者无法解决。因为单例作用域的 Bean 在创建之后会被放在 Bean 创建池中待用,而 prototype 作用域的 Bean 不会被 Spring 容器缓存,无法提前暴露创建完毕的 Bean。
2.3 自动装配
2.3.1 基于 XML 的自动装配
所谓的自动装配,在 XML 中,就是指通过 XML 指定扫描的包之后,Spring 自动装载使用注解声明的 Bean. 它的使用非常简单,使用 context 命名空间中的 component-scan
来指定要扫描的包即可。
<!-- 注意,使用以上配置我们需要引用 context 命名空间,可以使用分隔符来指定多个扫描的包 -->
<context:component-scan base-package="me.shouheng.spring.componentscan"/>
默认地,它会扫描 @Component
, @Repository
, @Service
, @Controller
注解声明的类,并将其加载到容器中。这四个注解可以使用各自的 value 字段指定该 Bean 的 id(默认将首字母小写之后作为 Bean 的名字)。这个几个注解分别用到下面的几种类型的类上面:
- @Component 通用的构造型注解,标识该类为 Spring 组件。
- @Controller 标识该类定义为 Spring MVC 控制器。
- @Repository 标识该类定义为数据仓库。
- @Service 标识该类定义为服务。
自动装配的时候,可以通过为字段添加 @Autowired
注解来实现,也可以不指定注解,但是要给字段提供 Setter 方法和构造方法。通常,建议使用基于构造方法的注入方式。@Autowired
注解有一个 required
的布尔类型字段,默认为 true,表示如果找不到可装配的 Bean 就抛异常;将其设置为 false 时,如果找不到也不会抛异常,但是如果最终没有注入值,又恰好用到了它,可能会在运行时抛出 NPE 异常。
关于 <context:annnotation-config>
与 <context:component-scan/>
标签的区别:如果用 <context:annotation-config/>
,我们还需要配置 Xml 注册 Bean,而使用 <context:component-scan />
的话,注册的步骤都免了。当然前提是我们对需要扫描的类使用的注解(比如 @Componet
, @Service
等),而如果同时使用两个配置的话,<context:annotation-config/>
会被忽略掉。就是说 <context:component-scan/>
的功能是包含 <context:annnotation-config>
的,前者不仅可以使用注解配置,还可以扫描自动包下面的 Bean。
2.3.2 基于注解的自动装配
当然,同样的功能你还可以通过使用基于类的配置方式实现。这里我们用 @Configurable
将其声明为一个配置类,然后使用 @ComponentScan
注解指定要扫描的包路径。这里的 @ComponentScan
注解与 XML 中的 component-scan
相似。
// 基于注解的自动装配
@Configurable
@ComponentScan(value = "me.shouheng.spring.componentscan")
public class ComponentScanConfiguration {
}
// 通过如下的方式来获取一个上下文
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ComponentScanConfiguration.class);
2.4 基于 Java 配置
与基于注解自动扫描的方式不同的是,基于纯 Java 的配置方式中,只需要使用 @Configurable
注解即可。与在 XML 中不同的是,这种配置方式中你可以直接使用 Java 方法来生成 Bean 实例,并直接使用该实例的方法来为其赋值。因此,本质上讲,它只是把 XML 中的工作转换到了 Java 中进行处理。在定义 Bean 的方法之上使用 @Bean
注解即可将返回的类型作为一个 Bean 使用:
@Configuration
public class JavaConfig {
@Bean("first")
public FirstBean firstBean() {
return new FirstBean(secondBean());
}
@Bean("second")
public SecondBean secondBean() {
return new SecondBean();
}
}
2.5 混合使用多种配置方式
以上提供了基于 Java 和基于 XML 的两种配置方式,实际上我们可以混合使用多种不同类型的配置方式。这里我们简单列举一下好了:
- 在 XML 中引用基于 XML 配置:
<import resource="AspectConfig.xml "/>
- 在 XML 中应用基于 Java 的配置:
<bean class="me.shouheng.spring.javaconfig.JavaConfig"/>
,没错就是将配置类当作 Bean 引入; - 在基于 Java 的配置中引用基于 Java 的配置:
@Import(value = {JavaConfig.class})
- 在基于 Java 的配置中引用基于 XML 的配置:
@ImportResource(value = {"classpath:aop/AOPConfig.xml"})
总结
这篇文章总结了 Spring IoC 容器的一些基本的配置方式。其目的在于通过阅读了这篇文章之后,使读者能够了解 Spring 都提供了哪些 API,以便在使用的时候,能够使用它们来解决问题。