自动装配(autowiring):有助于减少甚至消除<property>元素和<constructor-arg>元素,让spring自动识别如何装配Bean的依赖关系。
自动检测(autodiscovery):比自动装配更进一步,让spring能够自动识别哪些类需要被装配成sping Bean ,从而减少对<bean>元素的使用。
3.1 自动装配Bean属性
3.1.1 4种类型的自动装配
byName——把与Bean的属性具有相同的名字(或者ID)的其他Bean自动装配到Bean对应的属性中。如果没有跟属性的名字像匹配的Bean,则该属性不进行装配。
byType——把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中,若果没有跟属性的类型相匹配的bean,则该属性不被装配。
constructor——把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应参数中。
autodetect——首先尝试使用constructor进行自动装配,如果失败,再尝试使用byType进行自动装配。
byName自动装配:

1 <!-- byName自动装配 2 缺点:若是有多个音乐家需要装配instrument属性,则他们就会公用一个Saxopbone(即多个bean的属性被同一个bean赋值) 3 --> 4 <!-- 先在容器中装个乐器对象 --> 5 <bean id="instrument" class="com.springinaction.springidol.Saxophone"></bean> 6 <!--现在为音乐家kenny自动装配上面instrument的乐器--> 7 <bean id="kenny" --> 8 class="com.springinaction.springidol.Instrumentalist" 9 autowire="byName"> 10 <property name="song" value="演员——薛之谦"></property> 11 </bean> 12 13 <bean id="kenny1" 14 class="com.springinaction.springidol.Instrumentalist" 15 autowire="byName"> 16 <property name="song" value="演员1——薛之谦"></property> 17 </bean>
byType自动装配,容器中若是有同个类型的多个bean,自动装配的时候,会出抛出异常(就像选择性综合征,多了不知选哪个了)NoUniqueBeanDefinitionException(不唯一bean定义异常,翻译的可能不准确):

1 <!-- byType装配 2 有两个乐器:kenny bean就不知道要识别那个乐器装配给自己, 3 会抛出异常NoUniqueBeanDefinitionException(非唯一bean定义异常-个人翻译,可能不正确), 4 但是下面会出现提示: 5 org.springframework.beans.factory.UnsatisfiedDependencyException: 6 Error creating bean with name 'kenny' defined in class path resource [spring/springbean.xml]: Unsatisfied dependency expressed through bean property 'instrument': No qualifying bean of type [com.springinaction.springidol.Instrument] is defined: expected single matching bean but found 2: com.springinaction.springidol.Saxophone#0,com.springinaction.springidol.Guitar#0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.springinaction.springidol.Instrument] is defined: expected single matching bean but found 2: com.springinaction.springidol.Saxophone#0,com.springinaction.springidol.Guitar#0 7 Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.springinaction.springidol.Instrument] is defined: expected single matching bean but found 2: com.springinaction.springidol.Saxophone#0,com.springinaction.springidol.Guitar#0 8 --> 9 <!-- Saxophone类型的bean --> 10 <bean class="com.springinaction.springidol.Saxophone"></bean> 11 <!--Guitar类型的bean--> 12 <bean class="com.springinaction.springidol.Guitar"></bean> 13 14 <bean id="kenny" 15 class="com.springinaction.springidol.Instrumentalist" 16 autowire="byType"> 17 <property name="song" value="演员——薛之谦"></property> 18 </bean>
constructor自动装配:

1 <!-- constructor 自动装配 2 这个要求PoeticJuggler类中有一个构造器的参数是Sonnet29(他是--实现Poem的实现类)类型的 3 --> 4 <bean class="com.springinaction.springidol.Sonnet29"></bean> 5 6 <bean id="duke" 7 class="com.springinaction.springidol.PoeticJuggler" 8 autowire="constructor"/>
autodetect混合装配,就不介绍了。
注:在测试代码的时候,遇到了如下的SAXParseException异常:
Caused by: org.xml.sax.SAXParseException; lineNumber: 48; columnNumber: 45; 注释中不允许出现字符串 "--"。
at ;
这个是由于springbean.xml中的注释<!-- 这里是注释 -->除了开头和结尾可以有"--"外,里面不能有第三个"--",不如:<!-- 这里是是--注释 -->就会报上面的错误,这个一看就明白了。
3.1.2 默认自动装配
若果需要为Spring应用上下文中的额每一个Bean(或者其中大多数)配置相同的autowire属性,那么可以要求spring为它所创建的所有Bean引用相同的自动装配策略来简化配置

3.2 使用注解装配
使用注解方式允许更细粒度的自动装配,我们可以选择性标注某一个属性来对其应用自动装配。spring容器默认禁用注解装配,所以,在使用基于注解的自动装配,我们需要在spring配置中启用它。最简单的启用方式是使用spring的context命名空间配置中的<context:annotation-config>元素:
spring的context命名空间: xmlns:context="http://www.springframework.org/schema/context";
xsi:schemaLocation的值中要加:http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd;这两个东西(好像是约束,不知是啥)
spring3支持几种不同的用于自动装配的注解:
- spring自带的@Autowierd注解;
- JSR-330的@Inject注解;
- JSR-250的@Resource注解;
3.2.1 使用@Autowired
@Autowired是:org.springframework.beans.factory.annotation包下的Autowired接口如下:

@Autowired的装配代码,比如,在Instrumentalist类型的setInstrument上标注@Autowired:

1 //注入乐器
2 @Autowired
3 public void setInstrument(Instrument instrument) {
4 this.instrument = instrument;
5 }
在springbean.xml中:

1 <!-- Saxophone类型的bean --> 2 <!-- <bean class="com.springinaction.springidol.Saxophone"></bean> --> 3 <!--Guitar类型的bean(Instrument 乐器)--> 4 <bean class="com.springinaction.springidol.Guitar"></bean> 5 6 <bean id="kenny" 7 class="com.springinaction.springidol.Instrumentalist"> 8 <property name="song" value="演员——薛之谦"></property> 9 </bean>
测试结果,这时容器中只有一个乐器类型的bean:

1 //测试注解@Autowired
2 @Test
3 public void testAutowired() throws Exception {
4
5 Instrumentalist kenny = (Instrumentalist) ac.getBean("kenny");
6 kenny.perform();
7 kenny.getInstrument().play();
8
9 }

当springbean.xml中有两个乐器类型的bean时,会抛异常BeanCreationException,还是bean不唯一的问题:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'kenny': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.springinaction.springidol.Instrumentalist.setInstrument(com.springinaction.springidol.Instrument); nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.springinaction.springidol.Instrument] is defined: expected single matching bean but found 2: com.springinaction.springidol.Saxophone#0,com.springinaction.springidol.Guitar#0:

1 <!-- Saxophone类型的bean,下面两个bean同时存在的时候,会抛异常NoUniqueBeanDefinitionException --> 2 <bean class="com.springinaction.springidol.Saxophone"></bean> 3 <!--Guitar类型的bean(Instrument 乐器)--> 4 <bean class="com.springinaction.springidol.Guitar"></bean> 5 6 <bean id="kenny" 7 class="com.springinaction.springidol.Instrumentalist"> 8 <property name="song" value="演员——薛之谦"></property> 9 </bean>
上面就抛出异常;
@Autowired可以在构造器上面和属性上面(这里标注了就可以把setter方法删掉了)都可以标注;
当容器中没有自动装配的bean时,会抛出 NoSuchBeanDefinitionException(没有这样的bean定义异常):
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.springinaction.springidol.Instrumentalist.setInstrument(com.springinaction.springidol.Instrument); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.springinaction.springidol.Instrument] found for
这时候@Autowired(required=false)这样装配,自动装配可选,容器中没有改类型的bean,instrument值会是空:
@Autowired(required=false)
private Instrument instrument;
这时候抛异常会抛NullPointException;
若是@Autowired注解用在构造器上时,只有一个构造器上required设置为true,其他使用@Autowired注解所标注的构造器只能将required属性设置为false。
为解决上述问题,可以使用注解@Qualifier("guitar"),配置如下,这种配置和第二章中的<property name='instrument' ref='guitar'>原理其实应该差不多(个人觉得):

1 @Autowired(required=false)
2 @Qualifier("guitar")
3 private Instrument instrument;

1 <bean id="saxophone" class="com.springinaction.springidol.Saxophone"></bean> 2 <!--Guitar类型的bean(Instrument 乐器)--> 3 <bean id="guitar" class="com.springinaction.springidol.Guitar"></bean> 4 5 <bean id="kenny" 6 class="com.springinaction.springidol.Instrumentalist"> 7 <property name="song" value="演员——薛之谦"></property> 8 </bean>
还可以如下进行配置:

1 @Autowired(required=false)
2 //@Qualifier("guitar")
3 private Instrument instrument;

1 package com.springinaction.springidol;
2
3 import org.springframework.beans.factory.annotation.Qualifier;
4
5 /**
6 *
7 * @ClassName: Guitar
8 * @Description: 乐器:吉他
9 * @author mao
10 * @date 2017年3月19日 下午8:15:44
11 *
12 */
13 @Qualifier("stringed")
14 public class Guitar implements Instrument {
15
16 public Guitar(){
17
18 }
19
20 public void play() {
21 System.out.println("guitar guitar guitar");
22 }
23
24 }

1 <bean class="com.springinaction.springidol.Guitar"> 2 <qualifier value="stringed"></qualifier> 3 </bean> 4 5 <bean id="kenny" 6 class="com.springinaction.springidol.Instrumentalist"> 7 <property name="song" value="演员——薛之谦"></property> 8 </bean>
测试了一下<qualifier value="stringed"></qualifier>这个有没有都可以哎,我用的spring版本是spring4.2.9的,难道版本高了,功能也自动升级了,不是很明白:
创建自定义的限定器(Qualifiler)(这个感觉好吊的样子,因为看不懂,感觉像是自己创建了个自定义的注解):
首先创建一个接口:

1 package com.springinaction.springidol;
2
3 import java.lang.annotation.ElementType;
4 import java.lang.annotation.Retention;
5 import java.lang.annotation.RetentionPolicy;
6 import java.lang.annotation.Target;
7
8 import org.springframework.beans.factory.annotation.Qualifier;
9
10 @Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE})
11 @Retention(RetentionPolicy.RUNTIME)
12 @Qualifier
13 public @interface StringedInstrument {
14
15 }

1 package com.springinaction.springidol;
2
3
4 /**
5 *
6 * @ClassName: Guitar
7 * @Description: 乐器:吉他
8 * @author mao
9 * @date 2017年3月19日 下午8:15:44
10 *
11 */
12 @StringedInstrument//好嘛?这应该是自定义的注解吧
13 public class Guitar implements Instrument {
14
15 public Guitar(){
16
17 }
18
19 public void play() {
20 System.out.println("guitar guitar guitar");
21 }
22
23 }

1 @Autowired
2 //@Qualifier("guitar")
3 @StringedInstrument
4 private Instrument instrument;

1 <bean class="com.springinaction.springidol.Guitar"> 2 <!-- <qualifier value="stringed"></qualifier> 这一行有没有不影响 --> 3 </bean> 4 5 <bean id="kenny" 6 class="com.springinaction.springidol.Instrumentalist"> 7 <property name="song" value="演员——薛之谦"></property> 8 </bean>

1 //测试注解@Autowired @StringedInstrument
2 @Test
3 public void testAutowired() throws Exception {
4
5 Instrumentalist kenny = (Instrumentalist) ac.getBean("kenny");
6 kenny.perform();
7 kenny.getInstrument().play();
8
9 }
如果有多个乐器类被@StringedInstrument标注了,还需要再进行细粒度的控制,感觉这个太麻烦!要自定义很多个限定器!
3.2.2 借助@Inject实现基于标准的自动装配
JSR-330是一种依赖注入规范,更常见的叫法at inject;
这个要用到新的jar文件,javax.inject,所以pom.xml文件中需要引入该jar依赖;

1 <!-- JSR-330的标准注解 --> 2 <dependency> 3 <groupId>javax.inject</groupId> 4 <artifactId>javax.inject</artifactId> 5 <version>1</version> 6 </dependency>
这个jar内容如下,总共就这些类:

@Inject注解和@Autowired一样,可以用来自动装配属性、方法和构造器;与@Autowired不同的是,@Inject没有required属性。
@Inject他也可以限定,用@Name,@Autowired用的@Qualifier:

1 @Inject
2 @Named("guitar")
3 private Instrument instrument;
基本用法和@Autowired差不多,@Name和@Qualifier区别:前者是通过Bean的ID来表示可选择的Bean,后者是帮助我们缩小匹配Bean的选择范围(目前没有感觉到太大的差一性);
3.2.3 在注解注入中使用表达式
Spring3.0引入了@Value,可以装配String类型的值和基本类型的值,例如。
@Value("Eruption")
private String song;
@Value与SpEL表达式配合,才能显示他的魔力(第二章中的SpEL表达式);
3.3 自动检测Bean
<context:annotation-config/>需要显示定义<bean>,用<context:annotation-scan>允许spring自动检测Bean和定义Bean。为了配置Spring自动检测,需要使用<context:conponent-scan>元素代替<context:annotation-config>元素,元素会扫描指定的包及其所有子包,并查处自动注册的Spring Bean的类。

1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-4.0.xsd"> 10 <context:component-scan 11 base-package="com.springinaction.springidol"> </context:component-scan> 12 </beans>
3.3.1 为自动检测标注Bean
<context:componment-scan>查找使用构造型(stereotype)注解所标注的类:
- @Component——通用的构造型注解,标识该类为Spring组件
- @Constroller——标识将该类定义为SpringMVC controller
- @Repository——标识将该类定义为数据仓库
- @Service——标识将该类定义为服务

1 package com.springinaction.springidol;
2
3 import org.springframework.stereotype.Component;
4
5
6 /**
7 *
8 * @ClassName: Guitar
9 * @Description: 乐器:吉他
10 * @author mao
11 * @date 2017年3月19日 下午8:15:44
12 *
13 */
14 //自动将该类注册为Spring Bean。Bean的ID默认无限定类名。在这种场景下,Guitar Bean的ID为guitar
15 @Component
16 public class Guitar implements Instrument {
17
18 public Guitar(){
19
20 }
21
22 public void play() {
23 System.out.println("guitar guitar guitar");
24 }
25
26 }

1 package com.springinaction.springidol;
2
3 import javax.inject.Inject;
4 import javax.inject.Named;
5
6 import org.springframework.beans.factory.annotation.Value;
7 import org.springframework.stereotype.Component;
8
9
10
11 /**
12 *
13 * @ClassName: Instrumentalist
14 * @Description: 一个有天赋的音乐家
15 * @author mao
16 * @date 2017年3月19日 下午7:15:17
17 *
18 */
19 //将该类注册到Spring容器中,显示的为其命名为eddie。即ID为eddie
20 @Component("eddie")
21 public class Instrumentalist implements Performer {
22
23 @Value("演员--薛之谦")
24 private String song;
25
26 @Inject
27 @Named("guitar")
28 private Instrument instrument;
29
30 //注入乐器
31 public void setInstrument(Instrument instrument) {
32 this.instrument = instrument;
33 }
34 public Instrument getInstrument() {
35 return instrument;
36 }
37 //注入歌曲
38 public void setSong(String song) {
39 this.song = song;
40 }
41 public String getSong() {
42 return song;
43 }
44
45 public Instrumentalist(){
46
47 }
48
49 public void perform() throws Exception {
50 System.out.println("Playing "+song+": ");
51 }
52
53 }

1 //测试注解@Component
2 @Test
3 public void testComponent() throws Exception {
4
5 Instrumentalist eddie = (Instrumentalist) ac.getBean("eddie");
6 eddie.perform();
7 eddie.getInstrument().play();
8
9 }
测试结果:

3.4 使用spring基于java的配置
3.4.1 创建基于Java的配置
即使Spring的java配置可以使用XML就可以编写大多数的Spring配置,但是我们仍然需要极少量的XML来启用Java配置:

1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-4.0.xsd"> 10 <context:component-scan 11 base-package="com.springinaction.springidol*"></context:component-scan> 12 13 </beans>
首先创建一个类,用@Configuration标注该类,使用它标注后,这个类就相当于<beans>容器了:

1 package com.springinaction.springidol;
2
3 import org.springframework.context.annotation.Bean;
4 import org.springframework.context.annotation.Configuration;
5
6 //在基于java的配置里使用@Configuration注解java类,就等价于XML配置中的<beans>
7 //@Configuration注解会作为一个标识告知Spring:这个类将包含一个多个SpringBean的定义。
8 //这些Bean的定义是使用@Bean注解所标注的方法。
9 @Configuration
10 public class SpingIdolConfig {
11
12 /*
13 * 它等价于使用XML所配置的<bean>元素。@Bean告知Sping这个方法返回一个对象,
14 * 该对象应该被注册为Spring应用上下文中的一个Bean。方法名将作为该Bean的ID
15 */
16 @Bean
17 public Performer duke(){
18 return new Juggler();
19 }
20
21 @Bean
22 public Performer kenny(){
23
24 Instrumentalist kenny = new Instrumentalist();
25 kenny.setSong("认真的雪Bean----薛之谦");
26 kenny.setInstrument(guitar());
27 return kenny;
28
29 }
30
31 @Bean
32 public Instrument guitar(){
33 return new Guitar();
34 }
35
36 }
此时Instrumentalist类是这样的,两个属性的注解被注释了:

1 package com.springinaction.springidol;
2
3 import javax.inject.Inject;
4 import javax.inject.Named;
5
6 import org.springframework.beans.factory.annotation.Autowired;
7 import org.springframework.beans.factory.annotation.Qualifier;
8 import org.springframework.beans.factory.annotation.Value;
9 import org.springframework.stereotype.Component;
10
11
12
13 /**
14 *
15 * @ClassName: Instrumentalist
16 * @Description: 一个有天赋的音乐家
17 * @author mao
18 * @date 2017年3月19日 下午7:15:17
19 *
20 */
21 //将该类注册到Spring容器中,显示的为其命名为eddie。即ID为eddie
22
23 public class Instrumentalist implements Performer {
24
25 // @Value("演员---薛之谦")
26 private String song;
27
28 // @Autowired
29 // @Qualifier("saxophone")
30 private Instrument instrument;
31
32 //注入乐器
33 public void setInstrument(Instrument instrument) {
34 this.instrument = instrument;
35 }
36 public Instrument getInstrument() {
37 return instrument;
38 }
39 //注入歌曲
40 public void setSong(String song) {
41 this.song = song;
42 }
43 public String getSong() {
44 return song;
45 }
46
47 public Instrumentalist(){
48
49 }
50
51 public void perform() throws Exception {
52 System.out.println("Playing "+song+": ");
53 }
54
55 }
测试代码:

1 //测试基于java的配置
2 @Test
3 public void testJava() throws Exception {
4
5 Instrumentalist eddie = (Instrumentalist) ac.getBean("kenny");
6 eddie.perform();
7 eddie.getInstrument().play();
8
9 }
结果:

这个基于java的注解,若是将上述的两个注释的注解解封:

1 package com.springinaction.springidol;
2
3 import javax.inject.Inject;
4 import javax.inject.Named;
5
6 import org.springframework.beans.factory.annotation.Autowired;
7 import org.springframework.beans.factory.annotation.Qualifier;
8 import org.springframework.beans.factory.annotation.Value;
9 import org.springframework.stereotype.Component;
10
11
12
13 /**
14 *
15 * @ClassName: Instrumentalist
16 * @Description: 一个有天赋的音乐家
17 * @author mao
18 * @date 2017年3月19日 下午7:15:17
19 *
20 */
21 //将该类注册到Spring容器中,显示的为其命名为eddie。即ID为eddie
22
23 public class Instrumentalist implements Performer {
24
25 @Value("演员---薛之谦")
26 private String song;
27
28 @Autowired
29 @Qualifier("saxophone")
30 private Instrument instrument;
31
32 //注入乐器
33 public void setInstrument(Instrument instrument) {
34 this.instrument = instrument;
35 }
36 public Instrument getInstrument() {
37 return instrument;
38 }
39 //注入歌曲
40 public void setSong(String song) {
41 this.song = song;
42 }
43 public String getSong() {
44 return song;
45 }
46
47 public Instrumentalist(){
48
49 }
50
51 public void perform() throws Exception {
52 System.out.println("Playing "+song+": ");
53 }
54
55 }
结果就是:

这个感觉像是就近原则啊(xml的注解在属性上方),要么是先是执行了基于java的配置,后又执行了基于xml的配置,xml的配置覆盖了前者的结果。
来源:https://www.cnblogs.com/huaxueyihao/p/6594501.html
