spring从两个角度来实现自动化装配
public interface CompactDisc {
void play();
}
@Component
public class SgtPeppers implements CompactDisc {
public void play() {
...
}
}
使用@Component注解表明该类会作为组件类,并告知spring要为此类创建bean
@Configuration
@ComponentScan
public class CDPlayerConfig {
}
若没有其他配置,@ComponentScan 默认会扫描与配置类相同的包,即spring将会扫描此包以及此包下的所有子包,查找带有@Component
注解的类,并自动为其创建一个bean.
当使用XML启用组件扫描,可以使用spring context命名空间的
元素
spring应用上下文中所有的bean都会给定一个ID,上述案例中尽管没有明确为bean命名,但是spring会根据类名为其指定一个ID(将类名的第一个字母变为小写),例如SgtPeppers 指定的ID为sgtPeppers.
也可以为此bean设置不同的Id
@Component("testabc")
public class SgtPeppers implements CompactDisc {
......
}
另一种为bean命名的方式,不使用@Component注解,而使用Java依赖注入规范(Java Dependency Injection)中提供的@Named
注解
@Named("abcd")
public class SgtPeppers implements CompactDisc {
......
}
@Component与@Named在大多数场景中,它们是可以相互替换的,两者之间有一些细微的差异.
查阅了一些资料,稍微了解了一下二者的细微差异:
@Named JSR-330不提供组合模型,@Component是可组合注解
例:
// @Named 不能这样组合的
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Named("test")
public class Test {
......
}
// @Component 可以组合
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component("test2")
public class Test2 {
......
}
我们在启用组件扫描时,用到的@ComponentScan
注解,没有为其设置任何属性,那么,它会按照默认规则,以配置类所在的包作为基础包进行组件扫描.
如果我们将配置类 放在单独的包中,是其与其他应用代码分开,此时默认的基础包就不能满足我们的需求了.
这时可以使用 @ComponentScan
的 value
属性,指明包的名称
@Configuration
@ComponentScan("autoconfig")
public class CDPlayerConfig {
}
若还想清晰的表明此包为基础包,可以使用 basePackages
属性设置
@Configuration
@ComponentScan(basePackages ="autoconfig")
public class CDPlayerConfig {
}
basePackages
属性是复数形式,所以可以设置扫描多个基础包
@Configuration
@ComponentScan(basePackages ={"autoconfig","autoconfig2"})
public class CDPlayerConfig {
}
basePackages
属性是类型不安全,比如重构代码后,基础包可能会出现错误.
此时可以使用 basePackageClasses
属性, 指定为包中所包含的类或接口.
@Configuration
@ComponentScan(basePackageClasses ={CDPlayer.class,DVDPlayer.class})
public class CDPlayerConfig {
}
也可以创建一个用来进行扫描的空标记接口,例如:
public interface TestInterface {
void play();
}
@Component
public class A implements TestInterface {
......
}
@Component
public class B implements TestInterface {
......
}
@Configuration
@ComponentScan(basePackageClasses ={TestInterface.class})
public class TestConfig {
}
使用注解@Autowired
实现自动装配,例如在 构造器
上添加此注解
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
@Autowired
注解不仅能使用在 构造器
上,也能使用在 属性的Setter
方法上
@Autowired
public void setCompactDisc(CompactDisc cd) {
this.cd = cd;
}
Setter方法并无特殊之处,@Autowired
注解可以用在类的任何方法上,假设CDPlayer 类有一个insertDisc()方法,@Autowired
注解也能发挥相同的作用.
@Autowried
public void insertDisc(CompactDisc cd) {
this.cd = cd;
}
不管是构造器,Setter方法,还是其他方法,spring 都会尝试满足方法参数上所声明的依赖,
若有且只有一个bean
,那么这个bean将会被装配进来.
若没有匹配的bean,那么在应用上下文创建时,spring会抛出一个异常,此时可以设置@Autowried注解的required属性为false
@Autowired(required=false)
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
若将@Autowried注解的required属性设置为false,spring会尝试执行自动装配,但如果没有匹配的bean的话,spring会让这个bean处于未装配状态,如果未在代码中进行null检查,则可能会出现NullPointerException
如果有多个bean都能满足依赖关系,spring将会抛出一个异常,表明没有明确指定要选择哪个bean进行自动装配.(自动装配中的歧义性)
@Autowried
是spring特有注解,也可以使用Java依赖注入规范提供的 @Inject
, 二者存在细微差别(这两个的细微差别,暂时没查阅到),但多数情况下都可以相互替换.
源码: https://gitee.com/jincheng-921/sp_ch2_2.git
最近在看spring in action(第4版) ,边看边写点笔记,整理一下,仅供自己学习记录使用,案例是从书中的源码筛选出来的.