4.基于注解的bean装配

        对于spring提供传统XML配置方式来说,由于项目的日益扩大,XML配置信息也会随之增加,对于项目的后期管理和维护造成一定的复杂性,为了减少XML配置信息,spring提供了基于注解的配置。

        spring提供了自动装配和自动检测功能,用于减少XML配置信息。

spring的自动装配4种策略

byName

根据名字进行自动装配,改名字为spring配置中的id名,如果没有找到对应的id名则不进行装配

byType 根据bean的属性相同类型的其他bean进行自动装配,如果没有找到相同类型的其他bean,则不进行装配
constructor 把与bean的构造器入参具有相同类型的其他bean自动装配到bean的构造器对应入参中
autodetect 首先尝试constructor进行自动装配,如果失败,则尝试byType进行自动装配

byName实现自动装配:    

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util-3.0.xsd">
         
    <bean id="robbie" class="org.robbie.test.spring.beans.Robbie" autowire="byName" />
    <bean id="flute" class="org.robbie.test.spring.beans.Flute" />
	
</beans>

 在这里配置的两个bean中,由于robbie中的一个属性命名为flute,并且配置了autowire属性为byName,那么则会把当前在spring容器当中id为flute的bean自动装配进来,如果当前多个bean中都有名称为flute的属性,则都会将同一个flute bean装配进来

byType实现自动装配:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util-3.0.xsd">
         
    <bean id="robbie" class="org.robbie.test.spring.beans.Robbie" autowire="byType" />
    <bean id="flute1" class="org.robbie.test.spring.beans.Flute" />
	
</beans>

 当前装配采用byType,因为robbie这个bean里面的属性有一个类型为Instrument的属性,而Flute类也实现了此接口,所以会自动将id为flute1的bean注入到robbie的属性当中。该例中要注意一个问题,如果在装配中在spring容器中发现了有多个相同类型的bean,该注入哪一个bean呢,通常来说spring遇到这种情况会直接抛出异常。为了避免类型相同进行装配的歧义,spring有两种解决方案:

1.自动装配标识一个首选bean;2.取消某个bean的自动装配候选资格

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util-3.0.xsd">
         
    <bean id="robbie" class="org.robbie.test.spring.beans.Robbie" autowire="byType" />
    <bean id="flute" class="org.robbie.test.spring.beans.Flute" />
    <bean id="piano" class="org.robbie.test.spring.beans.Piano" primary="false" />
	
</beans>

 配置primary属性为false(默认为true),降低优先级,当发现两个相同类型的bean时,选择优先级高的进行注入,此例中将会注入flute

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util-3.0.xsd">
         
  <bean id="robbie" class="org.robbie.test.spring.beans.Robbie" autowire="byType" />
  <bean id="flute" class="org.robbie.test.spring.beans.Flute" />
  <bean id="piano" class="org.robbie.test.spring.beans.Piano" autowire-candidate="false" />
	
</beans>

 配置autowire-candidate属性为false(默认为true),取消自动注入候选资格,该例中将会注入id为flute的bean

constructor自动装配:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util-3.0.xsd">
         
    <bean id="robbie" class="org.robbie.test.spring.beans.Robbie" autowire="constructor" />
    <bean id="flute" class="org.robbie.test.spring.beans.Flute" />
	
</beans>

 配置属性autowire为constructor,此例中spring将会检查robbie的构造器,查找构造器当中的参数类型是否与当前容器当中的bean类型相互匹配,如果匹配则通过构造器进行注入,当前例子中因为robbie的构造器中有一个形参为Flute类型的参数,则自动会将flute进行注入

如果配置autowire为autodetect将会进行自动选择进行装配,首先尝试constructor,如果失败尝试byType,此配置在3.0后已经被废弃。

如果需要用到全局配置自动装备属性,则在根元素beans配置defaul-autowire,例如:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		   http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
           http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util-3.0.xsd"
           default-autowire-candidates="false" default-autowire="byType">
	
</beans>

此例中配置了全局的default-autowire-candidates为false,并且default-autowire为byType,意思当前在此配置文件中应用的所有bean取消自动装配候选资格,并且按照byType进行自动装配。对于bean中配置的autowire属性将会覆盖全局默认配置的default-autowire属性的值,从而达到个性化。

注意:当使用constructor自动装配时,不能配置constructor-arg属性,因为spring会自动去填充对应的constructor的参数,手动再去配置constructor-arg会引起冲突,从而报错。但是如果是其他类型的自动装配,可以显示的进行设值,即手工装配和自动装配进行混合使用,首先会使用手动注入的配置

基于注解的装配

        spring默认是禁止了注解的装配的,一旦使用了注解装配,那么在XML上就可以把配置property的地方去掉了,要启动它必须进行显示开启:

添加命名空间:

xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"
 增加配置:
<context:annotation-config />
 spring支持3种自动装配的注解:
spring自带的@Autowired注解
JSR-330的@Inject注解
JSR-250的@Resource注解

使用@Autowired

package org.robbie.test.spring.beans;

import org.robbie.test.spring.exception.PerformerException;
import org.robbie.test.spring.inf.Performer;
import org.robbie.test.spring.inf.Poem;
import org.springframework.beans.factory.annotation.Autowired;

public class Juggler implements Performer {
	
	@Autowired
	private Poem poem;

	private int beanBags = 3;
	
	@Autowired
	public void setBeanBags(int beanBags) {
		this.beanBags = beanBags;
	}
	
	
	public static String name = "juggler";
	
	public Juggler() {
	}
	
	public Juggler(int beanBags) {
		this.beanBags = beanBags;
	}

	public Juggler(int beanBags, Poem poem) {
		this.beanBags = beanBags;
		this.poem = poem;
	}
	
	public static String name(){
		return "jugglerMethod";
	}
	
	public String method() {
		return "AA";
	}

	@Override
	public void perform() throws PerformerException {
		System.out.println("Juggling " + beanBags + " Beanbags");
		poem.recite();
	}

	public int getBeanBags() {
		return beanBags;
	}

}
 该例中,@Autowired可以配置在属性上,也可以配置在方法上,都标识着自动装配对应的类型bean或值到属性当中,如果配置在方法上可以标注在构造方法或者任意方法上,如:
@Autowired
public Juggler(Poem poem) {
        this.poem = poem;
}
 
@Autowired
public void hereIsPoem(Poem poem){
        this.poem = poem;
}
@Autowired的注解不会受限于访问修饰符,即便是private依然可以完成注入的工作,并且如果当前容器存在多个类型(没有通过名字区分或者限定器限定)或者找不到匹配的类型来完成注入,就会出现错误,spring用以下两种方式进行解决:
@Autowired(required=false)
private Poem poem;
 标识poem属性的注入并不是必须的,如果找不到匹配的类型,则不进行注入 注意:如果@Autowired配置在多个构造器上,那么只有一个构造器可以配置为required为true,其他必须为false,并且spring会选择形参最多的构造器进行注入

 

 使用@Qualifier来限制歧义:

 

@Autowired()
@Qualifier("robbie")
private Poem poem;
 当spring容器中有两个或以上Poem类型bean时,会了选择特定的一个来进行注入,使用@Qualifier注解进行标注,此例中明确限定了id为robbie的poem进行注入,实际上使用@Qualifer大大的缩小了注入搜寻的范围,进行了一层注入前的过滤。@Qualifier还可以直接配置在类级别上,直接用于字符串标识bean:

 

@Qualifier("robbie")
public class Robbie implements Poem {

}
 同等的XML配置:

 

<bean id="robbie" class="org.robbie.test.spring.beans.Robbie">
        <qualifier value="robbie" />
</bean>
 当spring容器当中有两个以上Poem类型bean时,可以在需要注入的地方使用@Qualifier然后指定已经标识好字符串的bean,用于消除歧义,过滤多个相同类型的bean

 

创建自定义的限定器:

 

package org.robbie.test.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.annotation.Qualifier;

@Target({ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MyQualifier {

}
 将自定义的限定器用于自己建立的类上:
@MyQualifier
public class Robbie implements Poem {

}
 在注入的时候通过自定义的限定器进行过滤,缩小注入搜寻范围:

 

@Autowired
@MyQualifier
private Poem poem;
 此例中如果有多个Poem类型bean,那么注入的时候将会使用通过自定义限定器注解后的bean

 

总结:使用@Autowired注解来进行自动装配,在一定程度会上依赖spring框架(个人认为不是什么问题,开发中必须采用spring已经成为铁打的事实),所以也可以通过@Inject这个java标准自动装配注解来完成相同的工作。

 

使用@Inject:

        为了统一依赖注入的编程模型,JCP发布了依赖注入的规范,即JSR-330,spring已经兼容了此模型,@Inject可以完全替代@Autowired,但是也存在着一些区别,使用@Inject没有required属性,说明了在使用该注解时,必须要求注入,如果没有找到对应的类型bean或值,则会出现错误。

使用Provider进行延迟注入:

 

@Inject
public Juggler(Provider<Poem> provider){
        Poem poem = provider.get();
        poem.recite();
}
 该例中,Juggler的构造器并没有注入一个Poem,而是注入的Provider,使用Provider的get()方法,可以取得Poem对象,自行进行处理后,最后自行进行注入。

 

使用@Named限定@Inject的注入,@Named的用法跟@Qualifier的用法一致,自定义限定器也与@Qualifier一致:

 

@Inject
@Named("robbie")
private Poem poem;
 

在注解中应用表达式:

spring3.0后引入了@Value注解,可以装配基本类型和String类型的值,并且还能够在Value中使用表达式进行运算:

 

@Value("my name is robbie")
private String name; 

使用@Value装配硬编码的值其实并不是特别的有意义,该注解配合SpEL共同使用才能够真正起到实质性的作用,从而满足某些项目的依赖注入需求:

 

@Value("#{juggler.method()}")
private String name;
该例中会调动spring容器中juggler bean的method方法,将返回值注入到name属性当中。

 

@Resource注解使用:

使用@Resource注解跟前两者相同,不同的是自动装配时候的策略,该注解首先会根据名称进行匹配,如果没有找到再根据类型进行匹配,其他用法不再详述(@Autowired是先ByType查找,然后ByName)。

 

总结:自动装配能够让spring自动识别如何将bean装配到一起,从而减少XML配置,在bean的声明真正的独立起来(因为只关注如何声明bean,不会关注属性的注入问题),让解耦达到了一个新的高度。

 

bean的自动检测:

之前所配置的:

<context:annotation-config />
 消除了property和constructor-arg的XML配置,但是仍然需要在XML上定义bean,为了完全消除bean的定义,我们可以使用context:component-scan,该配置不仅能够完成context:annotation-config的工作,还能够自动检测定义的bean:
<context:component-scan base-package="org.robbie.test" />
 该例中会自动扫描org.robbie.test包下被注解应用的类,并将这些类放入spring容器形成bean

spring会扫描特定注解的类,这些注解如下:

@Component 通用注解,标识为spring组件
@Controller 标识定义为spring MVC的controller
@Repository

标识该类定义为数据仓库,一般为持久层

@Service 标识为服务
@Component
public class Robbie implements Poem {

}

也可以指定具体的名称,如果没有指定名称采用类名(首字母小写),来进行注册

@Component("name")
public class Robbie implements Poem {

}

过滤组件扫描:

<context:component-scan base-package="org.robbie.test" >
  <context:include-filter type="regex" expression="*Service"/>
  <context:exclude-filter type="annotation" expression="org.robbie.test.spring.MyQualifier"/>
</context:component-scan>

 过滤组件扫描的核心在于include-filter,exclude-filter配置,并且通过type和expression两个属性共同来指定扫描策略,include-filter是需要扫描的类,exclude-filter是不需要扫描的类。type为过滤器类型和expression为描述,说明如下:

过滤器类型 描述
annotation 过滤器扫描使用指定注解所标注的那些类,通过expression属性指定要扫描的注解
assignable 过滤器扫描继承于expression属性所指定类型的那些类
aspectj 过滤器扫描与expression属性所指定的Aspectj表达式所匹配的那些类
custom 使用自定义的org.springframework.core.type.TypeFilter的实现类,该类由expression属性指定
regex 过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的那些类

该例中扫描名称为Service结尾的类(通过正则表达式进行匹配),并排除了使用MyQualifier注解的类

 

基于JAVA的配置:

定义配置类:

 

package org.robbie.test.spring.beans;

import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfiguration {

}

 @Configuration会告知spring,此类是一个配置类,这个类将包含一个或者多个spring bean的定义,这些bean的定义是使用@Bean注解所标注的方法:

package org.robbie.test.spring.beans;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfiguration {

	
	@Bean
	public Object getObject(){
		return new Object();
	}
	
}

 该例中getObject方法返回的值将会作为spring的bean,通过基于JAVA的spring配置的好处是编译时期的检查,如果是在XML上都是通过字符串进行定义和描述,难免会出现错误,而JAVA的方法名为spring的ID名称,并且所有配置采用JAVA代码编写,在编译时期可以进行检测,降低出现错误的几率

在基于java配置的类中进行装配:

package org.robbie.test.spring.beans;

import org.robbie.test.spring.inf.Performer;
import org.robbie.test.spring.inf.Poem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfiguration {
	@Bean
	public Poem poem(){
		return new Robbie();
	}
	
	@Bean
	public Performer performer(){
		return new Juggler(poem());
	}
	
}

 注意:在该例中,配置类MyConfiguration里定义了两个bean,performer依赖poem,把poem作为bean通过构造器进行注入,实际上这里调用poem方法并不是每次都会去产生一个poem实例,spring在此做了拦截,它会去容器中查找poem实例,如果找到直接返回作为参数传递给performer,最后完成注入。

 

 

猜你喜欢

转载自dynamicman.iteye.com/blog/2061784
今日推荐