---
id: "2018-10-21-10-38-05"
date: "2018/10/21 10:38:05"
title: "spring之最小化XML配置"
tags: ["java", "spring"]
categories:
  - "java"
  - "spring学习"
---

## 一、自动装配

### 1、四种类型的自动装配

| 类型       | 解释                                   | xml 配置                                       |
| ---------- | -------------------------------------- | ---------------------------------------------- |
| byName     | 根据 Bean 的 name 或者 id              | &lt;bean id="bean" class="…" autowire="byName"/&gt; |
| ByType     | 根据 Bean 类型自动装配                 | &lt;bean id="bean" class="…" autowire="byType"/&gt; |
| contructor | 根据 Bean 的构造器入参具有相同类型     | 同上                                           |
| Autodetect | 首先使用 contructor，失败再尝试 byType | 同上                                           |

&emsp;&emsp;byType 在出现多个匹配项时不会自动选择一个然是报错，为避免报错，有两种办法：1.使用&lt;bean&gt;元素的 primary 属性，设置为首选 Bean，但所有 bean 的默认 primary 都是 true，因此我们需要将所有非首选 Bean 设置为**false**；2.将 Bean 的`autowire-candidate`熟悉设置为**false**，取消 这个 Bean 的候选资格，这个 Bean 便不会自动注入了。

&emsp;&emsp;contructor 自动装配和 byType 有一样的局限性，当发现多个 Bean 匹配某个构造器入参时，Spring 不会尝试选择其中一个；此外，如果一个类有多个构造器都满足自动装配的条件，Spring 也不会猜测哪个更合适使用。

<!-- more -->

### 2、默认自动装配

&emsp;&emsp;如果需要为 Spring 应用上下文中的每个 Bean（或者其中的大多数）配置相同的 autowire 属性，可以在根元素&lt;beans&gt;上增加一个 default-autowire 属性，默认该属性设置为 none。该属性只应用于指定配置文件中的所有 Bean，并不是 Spring 上下文中的所有 Bean。

### 3、混合使用自动装配和显式装配

&emsp;&emsp;当我们对某个 Bean 使用了自动装配策略，并不代表我们不能对该 Bean 的某些属性进行显示装配，任然可以为任意一个属性配置&lt;property&gt;元素，显式装配将会覆盖自动装配。**但是**当使用 constructor 自动装配策略时，我们必须让 Spring 自动装配构造器所有入参，不能使用&lt;constructor-arg&gt;元素进行混合。

## 二、注解装配

&emsp;&emsp;从 Spring2.5 开始，可以使用注解自动装配 Bean 的属性，使用注解允许更细粒度的自动装配，可选择性的标注某一个属性来对其应用自动装配。Spring 容器默认禁用注解装配，需要在 Spring 配置中启用，最简单的启用方式是使用 Spring 的 context 命令空间配置中的`<context:annotation-config>`,如下所示：

```xml
<beans ...>
  <context:annotation-config/>
  <!-- bean declarations go here -->
</beans>
```

&emsp;&emsp;Spring3 支持几种不同的用于自动装配的注解：

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

### 1、使用@Autowired

&emsp;&emsp;@Autowired 用于对被注解对象启动 ByType 的自动装配，可用于以下对象:

- 类属性，即使私有属性也能注入
- set 方法
- 构造器
- 任意需要装配 Bean 的方法

在使用@Autowired 时有两种情况会出错：没有匹配的 Bean 和存在多个匹配的 Bean，但是都有对应的解决方法。

- 当没有匹配 Bean 时，自动装配会抛出 NoSuchBeanDefinitionException，如果不想抛出可使用 required 属性，设置为 false 来配置可选的自动装配，即装配失败就不进行装配，不会报错。

  ```java
  @Autowired(required=false)
  ```

  当使用构造器配置时，只有一个构造器可以将 required 属性设置为 true，其他都只能设置为 false。此外，当使用注解标注多个构造器时，Spring 会从所有满足装配条件的构造器中选择入参最多的那个。

- 当存在多个 Bean 满足装配条件时，Spring 也会抛出 NoSuchBeanDefinitionException 错误，为了选择指定的 Bean，我们可以使用@Qualifier 注解进行筛选:

  ```java
  @Autowired
  @Qualifier("name1")//筛选名为name1的Bean
  private TestClass testClass;
  ```

  除了通过 Bean 的 ID 来缩小选择范围，我们还可以通过直接在 Bean 上使用 qualifier 来缩小范围，限制 Bean 的类型，xml 如下：

  ```xml
  <bean class="com.test.xxx">
    <qualifier value="stringed"/>
  </bean>
  ```

  注解如下：

  ```java
  @Qualifier("stringed")
  public class xxx{}
  ```

  还可以创建**自定义限定器（Qualifier）**

  &emsp;&emsp;创建自定义限定器只需要使用@Qualifier 注解作为它的源注解即可，如下创建了一个 Stringed 限定器：

  ```java
  @Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE})
  @Retention(RetentionPolicy.RUNTIME)
  @Qualifier
  public @interface Stringed{}
  ```

  然后使用它注解一个 Bean:

  ```java
  @Stringed
  public class Guitar{}
  ```

  然后就可以进行限定了：

  ```java
  @Autowired
  @Stringed
  private Guitar guitar;
  ```

### 2、使用@Inject 自动注入

&emsp;&emsp;为统一各种依赖注入框架的编程模型，JCP（Java Community Process）发布的 Java 依赖注入规范，被称为 JSR-330，从 Spring3 开始，Spring 已经开始兼容该依赖注入模型。

&emsp;&emsp;和@Autowired 一样，@Inject 可以用来自动装配属性、方法和构造器。但是@Inject 没有 required 属性，因此依赖关系必须存在，如不存在将抛出异常。

&emsp;&emsp;JSR-330 还提供另一种注入技巧，注入一个 Provider。Provider 接口可以实现 Bean 引用的延迟注入以及注入 Bean 的多个实例等功能。

&emsp;&emsp;例如我们有一个 KnifeJuggler 类需要注入一个或多个 Knife 实例，假设 Knife Bean 的作用域声明为 prototype，下面的 KnifeJuggler 的构造器将获得多个 Knife Bean:

```java
private Set<Knife> knifes;

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

&emsp;&emsp;相对于@Autowired 所对应的@Qualifier，@Inject 对应的是@Named 注解。事实上 JSR-330 中也有@Qualifier 注解，不过不建议直接使用，建议通过该注解来创建自定义的限定注解，和 Spring 的@Qualifier 创建过程类似。

### 3、注解中使用表达式

&emsp;&emsp;Spring3 中引入的`@Value`属性可用来装配 String 类型的值和基本类型的值。借助 SpEL 表达式，@Value 不光可以装配硬编码值还可以在运行期动态计算表达式并装配，例如下面的：

```java
@Value("#{systemProperties.name}")
private String name;
```

## 三、自动检测 Bean

&emsp;&emsp;在 Spring 中使用上面说到的`<context:annotation-config>`，可以做到自动装配，但还是要在 xml 中申明 Bean。Spring 还有另一个元素`<context:component-scan>`,元素除了完成自动装配的功能，还允许 Spring 自动检测 Bean 和定义 Bean ，用法如下：

```xml
<beans ...>
  <context:component-scan base-package="com.springtest">
  </context:component-scan>
</beans>
```

开启后支持如下注解：

| 注解        | 解释                                   |
| ----------- | -------------------------------------- |
| @Component  | 通用的构造型注解，标识类为 Spring 组件 |
| @Controller | 标识该类定义为 Spring MVC controller   |
| @Repository | 标识该类定义为数据仓库                 |
| @Service    | 标识该类定义为服务                     |

&emsp;&emsp;使用上述注解是 Bean 的 ID 默认为无限定类名。使用`@Component("name")`指定 ID。

### 1、过滤组建扫描

&emsp;&emsp;通过为\<context:component-scan>配置&lt;context:include-filter&gt;和&lt;context:exclude-filter&gt;子元素，我们可以随意调整扫描行为。下面的配置自动注册所有的 TestInterface 实现类：

```xml
<context:component-scan base-package="com.fxb.springtest">
  <context:include-filter type="assignable"
      expression="com.fxb.springTest.TestInterface"/>
</context:component-scan>
```

其中的 type 和 expression 属性一起协作来定义组件扫描策略。type 有以下值可选择：

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

&emsp;&emsp;exclude-filter 使用和 include-filter 类似，只是效果相反。

## 四、使用 Spring 基于 Java 的配置

&emsp;&emsp;在 Spring3.0 中几乎可以不使用 XML 而使用纯粹的 Java 代码来配置 Spring 应用。

- 首先还是需要极少量的 XML 来启用 Java 配置，就是上面说到的`<context:component-scan>`,该标签还会自动加载使用`@Configuration`注解所标识的类

- @Configuration 注解相当于 XML 配置中的\<beans>元素，这个注解将会告知 Spring：这个类包含一个或多个 Spring Bean 的定义，这些定义是使用@Bean 注解所标注的方法

- 申明一个简单的 Bean 代码如下：

  ```java
  @Configuration
  public class TestConfig{
      @Bean
      public Animal duck(){
          return new Ducker();
      }
  }
  ```

  @Bean 告知 Spring 这个方法将返回一个对象，该对象应该被注册为 Spring 应用上下文中的一个 Bean，方法名作为该 Bean 的 ID 。想要使用另一个 Bean 的引用也很简单，如下：

  ```java
  @Bean
  public Food duckFood(){
    return new DuckFood();
  }

  @Bean //通过方法名引用一个Bean，并不会创建一个新的实例
  public Animal duck(){
      return new Ducker(DuckFood());
  }
  ```

## 五、小结

&emsp;&emsp;终于写完了 spring 的最小化配置，对 spring 的各种注解也有了一些了解，再不是之前看到注解一脸莫名其妙了，虽然现在 Springboot 已经帮我们做了零 XML 配置，但觉得还是有必要了解下 XML 配置实现，这样对 Java 的配置实现理解也会更加深刻。
