自动装配(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