technology-note/java/spring/2.最小化XML配置.md

231 lines
11 KiB
Markdown
Raw Normal View History

2018-11-25 12:37:20 +08:00
---
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设置为false2.将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的配置实现理解也会更加深刻。