technology-note/java/spring/2.最小化XML配置.md
2019-01-07 10:31:40 +08:00

233 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id="2018-10-21-10-38-05"
title="spring之最小化XML配置"
headWord="spring是为了解决企业级应用开发的复杂性而创建的但是最初的Spring在随着应用程序的规模越来越大的情况下xml配置文件也随之膨胀变得不便于阅读与管理随后就有了一系列的手段来减少xml配置直到一行都没有"
tags=["java", "spring"]
category="java"
serie="spring学习"
---
## 一、自动装配
### 1、四种类型的自动装配
| 类型 | 解释 | xml 配置 |
| ---------- | -------------------------------------- | ---------------------------------------------- |
| byName | 根据 Bean 的 name 或者 id | \<bean id="bean" class="…" autowire="byName"/> |
| ByType | 根据 Bean 类型自动装配 | \<bean id="bean" class="…" autowire="byType"/> |
| contructor | 根据 Bean 的构造器入参具有相同类型 | 同上 |
| Autodetect | 首先使用 contructor失败再尝试 byType | 同上 |
&emsp;&emsp;byType 在出现多个匹配项时不会自动选择一个然是报错为避免报错有两种办法1.使用\<bean>元素的 primary 属性,设置为首选 Bean但所有 bean 的默认 primary 都是 true因此我们需要将所有非首选 Bean 设置为**false**2.将 Bean 的`autowire-candidate`熟悉设置为**false**,取消 这个 Bean 的候选资格,这个 Bean 便不会自动注入了。
&emsp;&emsp;contructor 自动装配和 byType 有一样的局限性,当发现多个 Bean 匹配某个构造器入参时Spring 不会尝试选择其中一个此外如果一个类有多个构造器都满足自动装配的条件Spring 也不会猜测哪个更合适使用。
### 2、默认自动装配
&emsp;&emsp;如果需要为 Spring 应用上下文中的每个 Bean或者其中的大多数配置相同的 autowire 属性,可以在根元素\<beans>上增加一个 default-autowire 属性,默认该属性设置为 none。该属性只应用于指定配置文件中的所有 Bean并不是 Spring 上下文中的所有 Bean。
### 3、混合使用自动装配和显式装配
&emsp;&emsp;当我们对某个 Bean 使用了自动装配策略,并不代表我们不能对该 Bean 的某些属性进行显示装配,任然可以为任意一个属性配置\<property>元素,显式装配将会覆盖自动装配。**但是**当使用 constructor 自动装配策略时,我们必须让 Spring 自动装配构造器所有入参,不能使用\<constructor-arg>元素进行混合。
## 二、注解装配
&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;为统一各种依赖注入框架的编程模型JCPJava 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 的配置实现理解也会更加深刻。