自动装配(autowiring):有助于减少甚至消除配置<property>元素和<constructor-arg>元素,让Spring自动识别如何装配Bean的依赖关系。
自动检测(autodiscovery):比自动装配更进一步,让Spring能够自动识别哪些类需要被配置成Spring Bean,从而减少对<bean>元素的使用。
byName——把与Bean的属性具有相同名字(或者ID)的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的名字相匹配的Bean,则该属性不进行装配。
byType——把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的名字相匹配的Bean,则该属性不进行装配。
constructor——把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应入参中。
autodetect——首先尝试使用constructor进行自动装配。如果失败,再尝试使用byType进行自动装配。
byName自动装配
——为属性自动装配ID与该属性同名的Bean
例:
假设存在一个Instrumentalist类包含属性song、instrument,以下是手动装配Bean的方式。
<bean id="saxophone" class="com.roger.spring.beans.Saxophone" /> <bean id="kenny" class="com.roger.spring.beans.Instrumentalist"> <property name="song" value="Jingle Bells" /> <property name="instrument" ref="saxophone" /> </bean>
如果要利用byName自动装配,就需要在使用<bean>元素定义Saxophone的时候,将Bean的id设置为I和nstrumentalist属性instrument同名的字面值。 通过配置autowire属性就可以利用此信息自动装配 kenny 的 instrument 属性了。
<bean id="instrument" class="com.roger.spring.beans.Saxophone" /> <bean id="kenny" class="com.roger.spring.beans.Instrumentalist" autowire="byName"> <property name="song" value="Jingle Bells" /> </bean>
byType自动装配
——Spring会寻找哪一个Bean的类型与属性的类型相同。
局限:如果Spring寻找到多个Bean,它们的类型与需要自动装配的属性的类型都相匹配,这时候,Spring不会猜测哪一个Bean更适合自动装配,而是选择抛出异常。所以,应用只允许存在一个Bean与需要自动装配的属性类型相匹配。
解决装配歧义的问题:可以为自动装配标识一个首选,或者取消某个Bean自动装配的候选资格。
标识首选
利用<bean>的primary属性, 默认为true,因此要达到标识首选的目的,需要将其他候选的primary属性都设为false。
取消候选
将<bean>的autowire-candidate属性设为false。
constructor自动装配
——在Bean声明中,不需要<constructor-arg>元素了
局限:a. 具有和byType自动装配相同的局限,当发现多个Bean匹配某个构造器入参时,Spring不会猜测哪个更适合;
b. 如果一个类有多个构造器,它们都满足自动装配的条件时,Spring也会猜测哪个更适合。
默认自动装配
利用<beans>属性default-autowire进行设置,默认为none,表示都不使用自动装配。可以利用<bean>元素的autowire属性来覆盖<beans>的默认自动装配策略。
使用注解方式允许更细粒度的自动装配,可以选择性地标注某一个属性来对其应用自动装配。
Spring容器默认禁用注解装配
xml配置中启用注解,利用context命名空间中<context:annotation-config>元素,如下所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context:"http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http:..www.springframework.org/schema/context http:..www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config /> </beans>
Spring支持集中不同的用于自动装配的注解:
Spring自带的@Autowired注解
JSR-330的@Inject注解
JSR-250的@Resource注解
@Autowired 默认是通过byType方式进行自动装配的,通过结合@Qualifier注解可以通过匹配Bean的id进行自动装配。
如:
通过@Qualifier注解将范围缩小,尝试将id为"guita"的bean注入。
@Autowired @Qualifier("guita") private Instrument instrument;
a、@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入;
b、@Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用;
c、@Resource注解是由J2EE提供,而@Autowired是由Spring提供,故减少系统对spring的依赖建议使用
@Resource的方式;
d、@Resource和@Autowired都可以书写标注在字段或者该字段的setter方法之上
即使<context:annotation-config>元素有助于完全消除Spring配置中的<property>和<constructor-arg>元素,但是仍要使用<bean>元素显示定义Bean。
因此,借助<context:component-scan>元素,除了完成与<context:annotation-config>一样的工作,还允许Spring自动检测Bean和定义Bean。中意味着不使用<bean>元素,Spring应用中的大多数(或所有)Bean都能够实现定义和装配。
还可以通过<context:component-scan>元素的base-package属性标识<context:component-scan>元素会扫描指定的包及其所有子包。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context:"http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http:..www.springframework.org/schema/context http:..www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.roger.spring.beans"> </context:component-scan> </beans>
默认情况下,<context:component-scan>查找使用构造型(stereotype)注解所标注的类。
这些特殊注解如下:
@Component——通过的构造型注解,标识该类为Spring组件
@Controller——标识将该类定义为Spring MVC controller
@Repository——标识将该类定义为数据仓库
@Service——标识将该类定义为服务
使用@Component标注的任意自定义注解
Spring扫描发现@Component注解所标注的类,并自动地将它注册为Spring Bean,Bean的ID默认为无限定类名(如:Person Bean的id为person)。
也可以使用@Component("Roger")方式来指定Bean的id。
通过为<context:component-config>配置子元素<context:include-filter>或者<context:exclude-filter>来调整扫描行为。
<context:include-filter>的type和expression属性一起来定义组件扫描策略。
过滤器类型 | 描述 |
annotation |
过滤器扫描使用指定注解所标注的那些类,通过expression属性指定要扫描的注解 |
assignable | 过滤器扫描派生于expression属性所指定类型的那些类 |
aspectj | 过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类 |
custom | 使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定 |
regex | 过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的那些类 |
例如:
<context:component-scan base-package="com.roger.spring.beans"> <context:include-filter type="assignable" expression="com.roger.spring.Address" /> </context:component-scan>
用极少量的xml来启用java配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context:"http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http:..www.springframework.org/schema/context http:..www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.roger.spring.beans"> </context:component-scan> </beans>
<context:component-scan>自动注册那些使用某种构造型(stereotype)注解所标注的Bean。
同时,也会自动加载使用@Configuration注解所标注的类。
在基于java的配置里使用@Configuration注解的java类,等价于xml配置中的<beans>元素。
@Configuration注解作为一个标识告知Spring:这个类将包含一个或多个Spring Bean的定义。
这些Bean的定义是使用@Bean注解所标注的方法。
例如:
有一个Person基类,Teacher类继承Person类,以下,声明一个简单的id为roger的Teacher Bean。
import org.springframework.context.annotation.Configuration; @Configuration public class SpringConfig { //Bean declaration methods go here //声明一个简单的Bean @Bean public Person roger() { return new Teacher(); } }
@Bean告知Spring这个方法将返回一个对象,该对象应该被注册为Spring应用上下文中的一个Bean,方法名作为Bean的id。
例如:
注入一个引用
import org.springframework.context.annotation.Configuration; @Configuration public class SpringConfig { //Bean declaration methods go here @Bean public Address address() { return new Address(); } @Bean public Person roger() { return new Teacher(address()); } }
注意:在Spring的java配置中,通过声明方法引用一个Bean并不等同于调用该方法。通过@Bean注解标注address()方法,要在其他Bean的声明方法中引用这个方法时,Spring都会拦截方法的调用,并尝试在应用上下文中查找该Bean,而不是让方法创建一个新的实例。