《Spring实战》学习笔记 :自动装配, 监测

在Spring中,对象无需自己查找或者创建与其相关联的其他对象, 相反,容器负责把需要相互协作的对象引用赋予各个对象.
创建应用对象之间协作关系的行为通常称为”装配”. 这也是依赖注入(DI)的本质.
Spring提供了三种主要的装配机制:
1. 在XML中进行显示配置
2. 在JAVA中进行显示配置.
3. 隐式的Bean发现机制和自动装配.

Spring提供了几种技巧,可以减少XML的配置数量:
1. 自动装配(autowiring):可以减少和元素,让Spring自动识别如何装配Bean的依赖关系;
2. 自动检测(autodiscovery):Spring能够自动识别哪些类需要被装配成Spring Bean,从而减少对的使用。


自动装配

自动装配用于配置Bean的属性, 构造器,依赖关系等.

4种自动装配
  1. byName:把与Bean属性具有相同名字(或id)的其他Bean自动装配到Bean的对应属性中;
  2. byType:把与Bean属性具有相同类型的其他Bean自动装配到Bean的对应属性中;
  3. constructor:把与Bean的构造函数的入参具有相同类型的其他Bean自动装配到Bean的构造函数对应的入参中;
  4. autodetect:先尝试使用constructor,失败后再使用byType。

Demo 类

public class Instrumentalist implements Performer {
    private String song;
    private Instrument instrument;
    public Instrumentalist() {
    }
    public void perform() throws PerformanceException {
        System.out.print("Playing " + song + " : ");
        instrument.play();
    }
    public void setSong(String song) { // 注入歌曲
        this.song = song;
    }
    public String getSong() {
        return song;
    }
    public String screamSong() {
        return song;
    }
    public void setInstrument(Instrument instrument) { // 注入乐器
        this.instrument = instrument;
    }
}

1. XML自动装配
1.1 byName

为属性自动装配id与该属性的名字相同的Bean。使用方法:

<bean id="kenny" class="com.springinaction.springidol.Instrumentalist" autowire="byName">
    <property name="song" value="Happy" />
</bean>

通过配置Bean Kenny的autowire=”byName”属性,Spring就可以利用此信息自动装配Kenny的instrument属性了。

缺点:需要假设Bean的名字(如instrument)与其他Bean的属性的名字一样,若其他多个Bean的属性都是instrument,那么让他们将使用同一个instrument。

1.2 byType

类似byName ,但匹配属性是检查属性的类型,
限制:当Spring根据类型匹配到多个Bean时,会抛出异常,为了解决这个问题,

Spring提供了两种方案:可以自动装配标识一个首选Bean,或者可以取消某个Bean的自动装配的候选资格。

标识首选Bean:primary=”true”
可以使用primary属性将Bean设置为首选Bean,那么它将会得到优选被选择权:

<bean id="saxphone" class="com.springinaction.springidol.Saxophone" />
<bean id="guitar" class="com.springinaction.springidol.Guitar" primary="true"/>

<bean id="kenny" class="com.springinaction.springidol.Instrumentalist"
    autowire="byType">
    <property name="song" value="Happy" />
</bean>

有两个Bean类型满足kenny的instrument属性,但是guitar设置了primary=”true”,因此会注入guitar。

排除其他Bean: autowire-candidate=”false”

<bean id="saxphone" class="com.springinaction.springidol.Saxophone" autowire-candidate="false"/>
<bean id="guitar" class="com.springinaction.springidol.Guitar"/>
<bean id="kenny" class="com.springinaction.springidol.Instrumentalist"
    autowire="byType">
    <property name="song" value="Happy" />
</bean>

通过排除其他Bean的候选资格来达到和上面设置primary同样的效果。

1.3 constructor

当有多个Bean匹配某个构造函数的入参时,Spring同样会抛出异常。

 <bean id="duke" class="com.springinaction.springidol.PoeticJuggler" 
     autowire="constructor">
  </bean>

当使用constructor自动装配时,就不能混合使用constructor自动装配和标签了。

1.4 autodetect

如果想自动装配,但又不能决定使用哪一种类型的自动装配,可以把autowire属性设置为autodetect,由Spring来决定,如:

 <bean id="duke" class="com.springinaction.springidol.PoeticJuggler" 
     autowire="autodetect">
  </bean>

Spring将首先尝试使用constructor自动装配,如果没有发现与构造器相匹配的Bean时,Spring将尝试使用ByType自动装配。

1.5 默认自动装配

可以在根元素上添加default-autowire属性来设置该配置文件中的的自动装配方式。
【default-autowire=”byName”】

<beans xmlns="http://www.springframework.org/schema/beans"
       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://www.springframework.org/schema/mvc"
       default-lazy-init="true" default-autowire="byName">
</beans>
1.6 混合使用显示装配和自动装配

显示装配会覆盖掉自动装配:

<bean id="saxphone" class="com.springinaction.springidol.Saxophone" autowire-candidate="false"/>
<bean id="guitar" class="com.springinaction.springidol.Guitar"/>
<bean id="kenny" class="com.springinaction.springidol.Instrumentalist"
    autowire="byType">
    <property name="song" value="Happy" />
    <property name="instrument" ref="saxphone"></property>
</bean>

虽然取消了saxphone的候选资格,但最终仍是saxphone注入到了kenny的属性中。


2.注解自动装配

启用注解装配:

<context:annotation-config />

Spring 支持几种不同的用于自动装配的注解

Spring自带的 @Autowired注解
JSR-330的 @Inject注解
JSR-250的 @Resource注解

2.1 @Autowired
// 1、可以标注setter
@Autowired
public void setInstrument(Instrument instrument) {
this.instrument = instrument;
}

// 2、标注其他方法
@Autowired
public void heresYourInstrument(Instrument instrument) {
this.instrument = instrument;
}

// 3、标注构造器
@Autowired
public Instrumentalist(Instrument instrument) {
this.instrument = instrument;
}

// 4、直接标注属性
@Autowired
private Instrument instrument;

@Autowired 存在两个问题: 没有找到合适的Bean,或者找到多个合适的bean,解决这个问题要用到可选的自动装配

@Autowired(required=false)
private Instrument instrument;

注意:
1. 当使用构造器装配时,只有一个构造器可以将@Autowired的required属性设置为true,其他使用@Autowired注解的required属性必须设置为false。
2. 当使用@Autowired标注多个构造器时,Spring会从所有满足装配条件的构造器中选择入参最多的那个。

限制歧义性的依赖
当有多个Bean满足装配条件时,可以配合使用@Qualifier注解。

@Autowired
@Qualifier("guitar")
private Instrument instrument;

注:用户也可以创建自定义的限定器,限定器的最终目的是【缩小了自动装配候选Bean的范围】

2.2 @Inject

Spring的@Autowired注解会引入对Spring的特定依赖,幸运的是Spring还提供了标准的JAVA注解来代替@Autowired,
和@Autowired一样,@Inject可以装配 属性、方法和构造器;
但是@Inject没有required属性,因此@Inject注解所依赖的bean是必须存在的,如果不存在就会抛出异常。

除了@Inject注解,JSR-330还提供了另一种技巧,与其直接注入一个引用, 不如要求@Inject注入一个Provider。Provider接口可以实现Bean引用的延迟注入以及注入多个Bean实例的功能。

private Set<Knife> knives;

@Inject
public KnifeJuggler(Provider<Knife> knifeProvider) {
    knives = new HashSet<Knife>();
    for (int i = 0; i < 5; i++) {
        knives.add(knifeProvider.get());
    }
}

KnifeJuggler类需要注入多个Knife实例,假设Knife Bean的作用域是prototype的,那么KnifeJuggler将获得一个Provider,这时只有provider被注入;在调用provider的get()方法之前,实际的Knife对象没有被注入。

限定注入的属性:@Named

@Inject
@Named("guitar")
private Instrument instrument;

用户也可以创建自定义的JSR-330 Qualifier

在注解中使用表达式:@Value
可以使用@Value装配简单值:String类型和基本类型,如:

@Value("Happy")
private String song;

@Value可以配合SpEL使用:

@Value("#{systemProperties.myFavoriteSong}")
private String song;

自动监测

自动监测用于使Spring自动发现应用中的Bean,解放手动配置Bean(xml或使用注解) 的繁琐流程.

<context:annotation-config> 可以减少<property>和<constructor-arg> 的使用,但仍需配置bean。
使用<context:component-scan> 除了可以完成上述工作,还可以自动检测Bean和定义Bean,
它会扫描指定的包及其所有子包,并查找出能够自动注册为Spring Bean的类。

正确配置:
<context:component-scan base-package="com.springinaction.springidol"/>
1.注解自动监测Bean

默认情况下, scan 会查找使用如下注解的类,

@Component:通用的构造型注解,标识该类为Spring组件;
@Controller:标识该类为Spring MVC controller;
@Repository:标识为数据仓库;
@Service:标识为服务;
@Component:标注为自定义注解。

如:

//Guitar对应的Bean的ID为guitar,
//注意首字母变为小写
@Component
public class Guitar implements Instrument {
    public void play() {
        System.out.println("Strum strum strum");
    }
}

//Strum 对应的Bean的ID为eddie
@Component("eddie")
public class Strum implements Instrument {
    public void play() {
        System.out.println("Strum strum strum");
    }
}
2.注解过滤组件扫描

自动检测注解的不足:必须手动用@Component标注所有Bean,对于第三方的代码,无法标注,所以Spring添加了一些过滤器组件【context:exclude-filter】,其Type和expression属性一起协作来定义组件扫描策略:

过滤器类型描述
annotation扫描使用指定注解所标注的类,通过expression属性指定要扫描的注解
assignable扫描派生于expression属性所指定类型的那些类
aspectj扫描与expression属性所指定的AspectJ表达式多匹配的那些类
custom使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定
regex扫描类名称与expression属性所指定的正则表达式所匹配的类

以下配置实现了:自动注册所有实现了Instrument接口的类,并且排除使用自定义@SkipIt注解的类。

<context:component-scan base-package="com.springinaction.springidol">
         <!--所有实现Instrument的类都将被自动注册为bean-->
    <context:include-filter type="assignable"
        expression="com.springinaction.springidol.Instrument" />

         <!--排除使用自定义@SkipIt注解的类-->
    <context:exclude-filter type="annotation"
        expression="com.springinaction.springidol.SkipIt" />
</context:component-scan>

注:应用过滤器时,可以有无限的过滤可能,但我们会发现默认的基于注解的过滤策略是经常使用到的。

3.基于JAVA配置bean

component-can会自动加载base-package下使用@Configuration注解所标注的类,此类即为配置类,
等价于XML配置中的’< beans>元素, 该注解告诉Spring:这个类将包含一个或者多个Spring Bean的定义,这行Bean的定义是使用@Bean注解所标注的方法。

//定义一个配置类
@Configuration
class Employee {
    public Car myCar;

    public Employee(Car _car) {
        this.myCar = _car;
    }

    //@Bean 告诉Spring这个方法将返回一个对象,该对象会被注册为Spring应用上下文中的一个Bean
    @Bean 
    public Car newCar() {
        return new Car(1, "white");
    }

    //使用Bean装配另一个Bean引用。
    @Bean
    public Employee newEmployeeWithCar() {
        return new Employee(newCar());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值