《Spring实战》读书笔记_装配bean

依赖注入

未使用依赖注入

  • 这个骑士只能使用剑,不能使用其他武器
  • 需要使用其他武器时,只能创建另一个骑士类
  • 无法进行单元测试
public class Knight implements Solider{
	private class Weapon weapon;
	public Knight() {
		weapon = new Sword();
	}
	public void executeTask() {
		weapon.attack();
	}
}

使用依赖注入

  • 骑士使用的武器从外部获取,骑士没有与任何特定的武器发生耦合
  • 骑士在执行任务的时候可以灵活地使用各种武器
  • 可以轻松地进行单元测试,只需要传入一个mock weapon即可
public class Knight implements Solider {
	private class Weapon weapon;
	public Knight(Weapon weapon) {
		this.weapon = weapon;
	}
	public void executeTask() {
		weapon.attack();
	}
}

装配bean

自动装配

自动扫描

  • 定义一个CD接口
// CD
public interface CompactDisc {
	void play();
}
  • @component注解表明该类会作为组件类,并告知Spring要为这个类创建bean,所以没有必要显式的配置SgtPeppersBean
    • @Component注解创建的bean的默认ID是类名开头字母小写,也就是sgtPeppers
    • 可以自定义beanID,用法:@Component(“beanID”)
@component
public class SgtPeppers implements CompactDisc {
	public void play() {	
		System.out.println("I'm sgtPeppers");
	}	
}
  • 组件扫描默认不启用,我们需要显式配置一下启用组件扫描
  • 创建一个配置类,使用@ComponentScan注解
    • 配置类会扫描所在包以及子包下所有带有@Component注解的类,并为这些类创建一个bean
    • 如果你想将配置类单独放在一个包里面,默认的基础包机制失效,可以手动指定扫描的包,给该注解的basePackages传递想要扫描的包名即可
    • basePackageClasses传入具体class对象,会扫描这些类所在的包,可以避免上面的String(String是类型不安全的,写代码容易出bug)
  • 问题:SpringBoot是如何开启组件扫描的呢?
// 方式一
@Configuration
@ComponentScan
public class CDPlayerConfig {
}

// 方式二
@Configuration
@ComponentScan(basePackages={"soundsystem", "video"})
public class CDPlayerConfig {
}

// 方式三
@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
public class CDPlayerConfig {
}

自动装配

用法
  • 使用@Autowired注解来实现自动装配
  • @Autowired可以用在任意方法之上,比如构造器方法,setter方法或者普通方法
  • 自动装配也能用在属性上
// 构造函数
@Autowired
public CDPlayer(CompactDisc cd) {
	this.cd = cd;
}
// setter方法
@Autowired
public void setCompactDisc(CompactDisc cd) {
	this.cd = cd;
}
// 普通方法
@Autowired
public void insertDisc(CompactDisc cd) {
	this.cd = cd;
}
原理
  • 不管是什么方法,Spring都会尝试满足方法参数上所声明的依赖
  • @Autowired先按类型查找bean
    • 如果找不到则报错,可以声明required=false,这样找不到时,Spring会让这个bean处于未装配状态,容易报空指针异常
    • 如果找到多个bean,需要借助其他注解来解决自动装配中的歧义性

通过Java代码装配Bean

  • 使用场景:将第三方库的组件装配到你的应用中时,你是没办法在它的类上添加@Component和@Autowired注解的
  • 声明简单的bean,如下:
    • @Configuration注解表明这是一个配置类
    • @Bean会告诉Spring该方法会返回一个对象,该对象要注册为Spring容器中的bean
    • 默认情况下beanID与带有@Bean注解的方法名时一样的,也可以用@Bean注解中的name属性指定一个不同的beanID
@Configuration
public class CDPlayerConfig{
	@Bean
	public CompactDisc sgtPeppers() {
		return new SgtPeppers();
	}
}
  • 声明复杂的Bean:CDPlayer,CDPlayer依赖sgtPeppers
    • 方式一:因为sgtPeppers()方法上添加了@Bean,Spring会拦截所有对它的调用,并确保直接返回该方法所创建的Bean,这种方式的限制是sgtPeppers()方法和cdPlayer()方法必须放在同一个配置类中
    • 方式二:当Spring调用cdPlayer()方法创建一个bean时,它会自动装配一个CompactDisc,这种方式的好处是CompactDisc这个bean如何被注入到Spring的没有限制
    • 方式三:使用setter方法注入,可以看到使用带有@Bean注解的方法注入bean时,可以采用任何的java代码来创建bean,方法参数中需要的bean,Spring都会自动传递进来
@Configuration
public class CDPlayerConfig{
	@Bean
	public CompactDisc sgtPeppers() {
		return new SgtPeppers();
	}
	
	//方式一
	@Bean
	public CDPlayer cdPlayer() {
		return new CDPlayer(sgtPeppers());
	}
	
	//方式二
	@Bean
	public CDPlayer cdPlayer(CompactDisc compactDisc) {
		return new CDPlayer(compactDisc);
	}
	// 方式三
	@Bean
	public CDPlayer cdPlayer(CompactDisc compactDisc) {
		CDPlayer cdPlayer = new CDPlayer();
		cdPlayer.setCompactDisc(compactDisc);
		return cdPlayer;
	}
}

通过XML装配Bean(省略)

自动装配的歧义性

演示歧义性

演示一:实现

  • 自动装配bean时,容器中如果找到多个类型相同的bean会阻碍Spring的自动装配,代码如下:
public interface Dessert {}
@Component
public class Cookies implements Dessert {
}
@Component
public class Cake implements Dessert {
}
@Component
public class IceCream implements Dessert {
}
  • 依赖注入
    • 方式一:注入的Dessert类型的Bean在容器中可以找到三个:Cookies, IceCream, Cake,自动装配失败
    • 方式二:如果把注入的类型改成Dessert的子类,自动装配成功
@Component
public class Kitchen {

	private Dessert dessert;

	// 方式一
    @Autowired
    public void dessert(Dessert dessert) {
        this.dessert = dessert;
    }
    
	// 方式二
    public void dessert(Cake dessert) {
        this.dessert = dessert;
    }
}

演示二:继承

  • 把实现改成继承,方式一的注入依然报错;方式二的注入依然成功
  • 如果在父类中也加上@Component
    • 上述方式一自动装配成功
    • 但是,通过ApplicationContext.getBean(Dessert.class)方法获取bean报错,提示有四个相同类型的bean:Dessert,Cookies,Cake和IceCream
// 演示二
@Component
public class Dessert {}
@Component
public class Cookies extends Dessert {
}
@Component
public class Cake extends Dessert {
}
@Component
public class IceCream extends Dessert {
}

解决歧义性

标示首选的bean

  • 有效配置
    • @Primary能够与@Component组合用在组件扫描的bean上
    • @Primary也能够与@Bean组合用在java配置的bean声明中
    • 在xml配置中,<bean>标签有一个primary属性用来指定首选的bean,设置为true即可
  • 无效配置
    • 在多个bean上配置了@Primary

限定自动装配的bean

  • @Primary只是标示了一个优先的可选方案,当优先可选方案失效(同上),装配依旧失败
  • @Qualifier(“beanId”),默认的限定符是beanId
    • @Component默认beanId是类名小写,也可指定beanId:@Component(“beanId”)
    • @Bean默认beanId是方法名小写,也可指定beanId:@Bean(“beanId”)
  • @Qualifier可以为bean自定义限定符,这样在重构代码(修改类名)的时候,就不会报错
// 注入bean时声明限定符
@Component
@Qualifier("cold")
public class IceCream implements Dessert {}
// 或者:
@Bean
@Qualifier("cold")
pubic Dessert iceCream() {
	return new IceCream();
}
// 装配bean时指定限定符
@Autowired
@Qualifier("cold")
public void setDessert(Dessert dessert) {
	this.dessert = dessert;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值