Spring装配Bean之自动化装配(一)

自动化装配

spring从两个角度来实现自动化装配

  • 组件扫描: spring会自动发现应用上下文中所创建的bean
  • 自动装配: spring自动满足bean之间的依赖

创建能被spring发现的bean

在需要被发现的类上添加注解@Component

public interface CompactDisc {
	void play();
}
@Component
public class SgtPeppers implements CompactDisc {

	public void play() {
		...
	}
}

使用@Component注解表明该类会作为组件类,并告知spring要为此类创建bean

启用组件扫描@ComponentScan

@Configuration
@ComponentScan
public class CDPlayerConfig {
}

若没有其他配置,@ComponentScan 默认会扫描与配置类相同的包,即spring将会扫描此包以及此包下的所有子包,查找带有@Component注解的类,并自动为其创建一个bean.
当使用XML启用组件扫描,可以使用spring context命名空间的元素

为组件扫描的bean命名

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 注解,没有为其设置任何属性,那么,它会按照默认规则,以配置类所在的包作为基础包进行组件扫描.

如果我们将配置类 放在单独的包中,是其与其他应用代码分开,此时默认的基础包就不能满足我们的需求了.

这时可以使用 @ComponentScanvalue 属性,指明包的名称

@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 {
}

为bean添加注解实现自动装配

使用注解@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版) ,边看边写点笔记,整理一下,仅供自己学习记录使用,案例是从书中的源码筛选出来的.

你可能感兴趣的:(spring)