创建可被发现的 Bean
CD 需要注入到 CD 播放器中进行播放,这样 CD 播放器才能正常运行。
CD 接口类
package soundsystem;
public interface CompactDisc {
void play();
}
CD接口的一个实现
package soundsystem;
import org.springframework.stereotype.Component;
@Component
public class SgtPepper implements CompactDisc {
private String title = "Sgt. Pepper's Loney Herts Club Band";
private String artist = "The Beatles";
@Override
public void play() {
System.out.println("Playing " + title + " by " + artist);
}
}
这个 CD 的实现类使用了@Component
注解,这个注解表明该类会作为组件类,并告知 Spring 要为这个类创建 Bean。
组件扫描
前面 CD 的实现类添加了注解,会作为一个组件类,但是组件扫描默认是不开启的,我们需要显式地配置一下 Spring,从而命令它去寻找带有@Component
注解的类,并为其创建 bean。
package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class CDPlayerConfig {
}
注解@ComponentScan
能够在 Spring 中启用组件扫描,如果没有其它配置的话,@ComponentScan
默认会扫描与配置类相同的包,Spring 会扫描这个包以及这个包下的所有子包,查找带有@Component
注解的类。
为组件扫描的 bean 命名
Spring 应用上下文中所有的 bean 都会给定一个 ID,前面虽然我们没有明确地为 SgtPeppers bean 指定 ID,但是 Spring 会根据类名为其指定一个 ID,默认将类名的第一个字母变为小写,也就是sgtPeppers
。
如果要为 bean 设置不同的 ID,可以将ID 作为值传给@Component
注解。
@Component("loneyHeartClub")
public class SgtPepper implements CompactDisc {
...
}
设置组件扫描的基础包
前面的代码中,我们没有为@ComponentScan
设置任何属性,所以它会以配置类所在包作为基础包来扫描组件。但是如果我们想将配置类放在单独的包中,使其与其它应用代码区分开来,那默认的就不行了。
@Configuration
@ComponentScan("soundsystem")
public class CDPlayerConfig {
}
或者显式地指定 basePackages:
@Configuration
@ComponentScan(basePackages = "soundsystem")
public class CDPlayerConfig {
}
可以同时指定多个基础包:
@Configuration
@ComponentScan(basePackages = {"soundsystem", "videos"})
public class CDPlayerConfig {
}
上面的基础包的指定都是以字符串的形式,但是这种方法是类型不安全的,如果你重构代码的话,可能就会出现错误。除了将包设置为简单的字符串类型外,@ComponentScan
还提供了另外一种方法,那就是将其指定为包中所包含的类或接口:
@Configuration
@ComponentScan(basePackageClasses = { SgtPepper.class, DVDPlayer.class})
public class CDPlayerConfig {
}
上面指定的类有些是组件类,为了更好区分,可以在这些包中创建一个空的扫描标记接口,然后使用这个接口类指定扫描包。
通过为 bean 添加注解实现自动装配
自动装配就是让 Spring 自动满足 bean 依赖的一种方法,在这个过程中,会在 Spring 应用上下文中寻找某个 bean 需求的其它bean。为了声明要进行自动装配,可以使用@Autowired
注解。
package soundsystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
@Override
public void play() {
}
}
上面代码在构造器上添加了@Autowired
注解,这表明当 Spring 创建 CDPlayer bean 的时候,会通过这个构造器来进行实例化并且传入一个可设置给 CompactDisc 类型的 bean。
@Autowired
不仅可以用在构造器上,实际上它可以用在任何方法上。
@Autowired
public void setCompactDisc(CompactDisc cd) {
this.cd = cd;
}
@Autowired
public void insertDisc(CompactDisc cd) {
this.cd = cd;
}
Spring 在尝试满足方法参数上所声明的依赖时,如果有且只有一个 bean(也就是一个实现)能匹配依赖需求的话,那么这个 bean 将会被包装进来。
如果没有匹配的 bean,那么在应用上下文创建的时候,Spring 会抛出一个异常,为了避免这个异常,你可以设置属性 required 为 false:
@Autowired(required = false)
public void insertDisc(CompactDisc cd) {
this.cd = cd;
}
默认是为 true 的,当设置为 false 的时候,在代码要记得在使用时判空。
如果有多个备案都满足依赖关系的话,Spring 会抛出一个异常,这表明没有明确指定要选择哪个 bean 进行装配。后面会讲自动装配中的歧义性。
Java 依赖注入规范里面的@Inject
和@Autowired
是同样的作用,大部分情况下可以互用。
网友评论