Spring 从两个角度来实现自动化装配:
- 组件扫描:Spring 会自动发现应用上下文中所创建的 bean。
- 自动装配:Spring 会自动满足 bean 之间的依赖关系。
组件
Spring 中使用 @Component 注解将一个类标注为组件类,并告知 Spring 要为这个类创建 bean,这样就不用显式配置了。代码如下: 首先,我们创建一个 CompactDisc 接口
java
package org.example.autoconfig;
public interface CompactDisc {
void play();
}
然后,创建一个 CompactDisc 接口的实现类 SgtPeppers,并使用 @Component 注解将该类标注为一个组件类。
java
package org.example.autoconfig;
import org.springframework.stereotype.Component;
@Component
public class SgtPeppers implements CompactDisc {
private String title = "Sgt. Pepper's Lonely Hearts Club Band";
private String artist = "The Beatles";
@Override
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
Spring 应用上下文会为所有的 bean 创建一个 ID,默认值就是类名的首字母小写。如果想要为 bean 设置 ID 值,只需要在 @Component 注解中传入 ID 的值即可。例如:
java
package org.example.autoconfig;
import org.springframework.stereotype.Component;
// 设置 SgtPeppers 类的 bean 的 ID 为 lonelyHeartsClub
@Component("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc {
...
}
组件扫描
Spring 的组件扫描默认是不开启的,需要显式地配置,从而 命令 Spring 去寻找带有 @Component 注解的类。有两种方式开启组件扫描。
- 在配置类(@Configuration 注解标注的类)中使用 @ComponentScan 注解,开启组件扫描。 @ComponentScan 默认会扫描与配置类相同的包以及这个包下的所有子包,带有 @Component 注解的类,并自动为其创建一个 bean。代码如下:
java
package org.example.autoconfig;
@Configuration
@ComponentScan
public class CDPlayerConfig {
...
}
如果想扫描指定的包,可以在 @ComponentScan 注解的 value 属性中,使用 String 类型的值指明包名称:
java
package org.example.autoconfig;
@Configuration
@ComponentScan("org.example.autoconfig")
public class CDPlayerConfig {
...
}
如果想要更加清晰地表明设置的是基础包,可以通过 basePackages 属性进行设置,可以设置一个或者多个基础包:
java
package org.example.autoconfig;
@Configuration
// 设置一个基础包
@ComponentScan(basePackages = "org.example.autoconfig")
// 设置多个基础包
// @ComponentScan(basePackages = {"org.example.autoconfig", "org.example.xmlconfig"})
public class CDPlayerConfig {
}
除了使用字符串设置包名称外,还可以将 basePackageClasses 属性设置为包中所包含的类或者接口,这些类所在的包将会作为组件扫描的基础包。
java
@Configuration
@ComponentScan(basePackageClasses = { CompactDisc.class, SgtPeppers.class})
public class CDPlayerConfig {
}
- 在 Spring 的 XML 配置文件中使用 <context:component-scan> 元素,开启组件扫描。这需要引入 Spring 的 context 命名空间。代码如下:
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启组件扫描, 并指定扫描的基础包为 soundsystem -->
<context:component-scan base-package="soundsystem"/>
</beans>
自动装配
简单来说,自动装配就是让 Spring 自动满足 bean 依赖的一种方法,在满足依赖的过程中,会在 Spring 应用上下文中寻找匹配某个 bean 需求的其他 bean。为了声明要进行自动装配,可以使用 Spring 的 @Autowired 注解。 首先,先声明一个 MediaPlayer 接口,代码如下:
java
package org.example.javaconfig;
public interface MediaPlayer {
void play();
}
然后,在它的实现类的构造函数上使用 @autowired 注解,当 Spring 创建 CDPlayer 类的 bean 的时候,会通过这个构造器来进行实例化并且传入一个 CompactDisc 类的 bean,实现自动装配,代码如下:
java
package org.example.javaconfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
// 使用在构造函数上,当Spring要创建CDPlayer的bean时,会自动注入CompactDisc的bean
@Autowired
public CDPlayer(CompactDisc cd){
this.cd = cd;
}
@Override
public void play() {
cd.play();
}
}
@Autowired 注解不仅能够用在构造器上,还能在属性的 Setter 方法上,如果 CDPlayer 类有一个 setCompactDisc() 方法,可以采用下面的注解形式进行自动装配:
java
@Autowired
public void setCompactDisc(CompactDisc cd){
this.cd = cd;
}
在 Spring 初始化 bean 之后,它会尽可能的去满足 bean 的依赖。不管是构造器、Setter 方法还是其他的方法,Spring 都会尝试满足方法参数上所声明的依赖。假如有且只有一个 bean 匹配依赖需求的话,那么这个 bean 将会被装配进来。 如果没有匹配的 bean,那么在应用上下文创建的时候,Spring 会抛出一个异常。为了避免异常的出现,可以将 @Autowired 的 required 属性设置为 false:
java
@Autowired(required=false)
public CDPlayer(CompactDisc cd){
this.cd = cd;
}
将 required 属性设置为 false 时,Spring 会尝试执行自动装配,但是如果没有匹配的 bean 的话,Spring 将会让这个 bean 处于未装配状态。但是,把 required 属性设置为 false 时,需要谨慎对待。如果在代码中没有进行 null 检查的话,这个处于未装配状态的属性有可能会出现 NullPointerException。 如果有多个 bean 都能满足依赖的话,Spring 将会抛出一个异常,表明没有明确指定要选择哪个 bean 进行自动装配。