写在前面:在Spring整理系列(02)——spring依赖注入,组装对象之间的依赖关系文中,第三部分简单介绍了使用@Autowired注解自动装配bean的情况,此处引用一下。
一、使用@Autowired注解,实现自动装配
在使用注解装配之前,首先要开启注解装配的方式,那就是在配置文件中加上下面这句话<context:annotation-config></context:annotation-config>
,也可以使用<context:component-scan>
标签(两者的区别在Spring整理系列(01)——spring概念简介、bean扫描与注册实现方式中有提到)。
使用@Autowired注解,可以用在属性、setter方法、constructor构造方法上,实现自动装配。
spring的xml配置文件:
<!-- 开启注解配置 -->
<context:annotation-config></context:annotation-config>
<!-- 注册bean -->
<bean id="userDao" class="com.jsun.test.springDemo.ioc.User.UserDaoImpl"></bean>
<!-- 注册bean -->
<bean id="userService" class="com.jsun.test.springDemo.ioc.User.UserServiceImpl"></bean>
UserServiceImpl:
1、注解在setter方法上:
private UserDao userDao;
//添加注解,匹配注入与参数类型有关,即byType,与参数名称、setter方法名称、成员变量属性名称无关
@Autowired
public void setUserDao(UserDao userDao) {
System.out.println("注解自动装配setUserDao注入");
this.userDao = userDao;
}
2、constructor构造方法上:
private UserDao userDao;
//添加注解,匹配注入与参数类型有关,即byType,与参数名称、成员变量属性名称无关
@Autowired
public UserServiceImpl(UserDao userDao){
System.out.println("注解自动装配构造器UserServiceImpl注入");
this.userDao = userDao;
}
3、声明的属性上:
//添加注解,先按照属性名称匹配注入,如果未找到则按照属性类型匹配注入,即byName-->byType
@Autowired
private UserDao userDao;
二、使用@Autowired抛出异常情况及与@Qualifier注解结合使用情况
抛出异常情况:
@Autowired使用的时候必须只有一个Bean适合装配,否则spring会抛出异常;
1、NoSuchBeanDefinitionException异常:如果在应用上下文当中找不到相应的bean去自动装配,那么spring也会抛出异常。
。。。
Caused by: org.springframework.beans.factory.BeanCreationException: 。。。
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type。。。
要避免这种情况发生,而且需要装配的属性也不是必须要装配的话,可以使用@Autowired(required=false)注解:
//在注入bean的时候该属性不是必须的,即使找不到对应bean注入,也不会报异常
@Autowired(required=false)
private UserDao userDao;
2、NoUniqueBeanDefinitionException异常:如果在同一个spring容器中出现了两个或多个都必须存在的bean,然而自动装配只需要装配期中一个,也会抛出异常。
父类接口:
public interface TestBeanInterface {
public void sayHello();
}
两个实现子类:
@Component("testBean1")
public class TestBean1 implements TestBeanInterface{
public void sayHello(){
System.out.println("TestBean1 sayHello...");
}
}
@Component("testBean2")
public class TestBean2 implements TestBeanInterface{
public void sayHello(){
System.out.println("TestBean2 sayHello...");
}
}
测试类中:
//以接口父类声明的成员变量属性,在spring容器中有它两个子类的bean
private TestBeanInterface testBean;
//自动装配,按照参数类型匹配注入bean,此时spring容器中TestBeanInterface类型有两个bean
@Autowired
public void setTestBean(TestBeanInterface t){
this.testBean = t;
}
执行测试的异常:
。。。
Caused by: org.springframework.beans.factory.BeanCreationException:。。。
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:。。。
这个时候@Autowired可以结合使用@Qualifier注解指定一个Bean来装配:
@Autowired
public void setTestBean(@Qualifier("testBean2")TestBeanInterface t){
this.testBean = t;
}
注:@Qualifier注解其实就是先按照bean的name过滤装配指定类型bean,把@Autowired放在成员变量属性上,也是先按照属性name查找,再按类型注入,所以把@Autowired放在成员变量属性上报NoUniqueBeanDefinitionException异常的概率比较低,并且不需要setter或构造器,推荐使用。
三、@Autowired可以装配的spring内部bean,不可以装配的bean
1、@Autowired可以注解的spring内部类型:
可以用@Autowired注解那些解析依赖性接口,比如BeanFactory、ApplicationContext、Environment、ResourceLoader、ApplicationEventPublisher、MessageSource等
@Autowired
private ApplicationContext context;
@Test
public void testGetBean(){
TestBeanInterface tb = (TestBeanInterface)context.getBean("testBean1");
System.out.println(tb.getClass().getName());
}
2、@Autowired不可以注解的类型:
因为@Autowired是由Spring BeanPostProcessor处理的,所以不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型应用@Autowired注解,这些类型必须通过xml或者@Bean注解形式加载。
四、@Autowired与JSR-250提供的@Resource注解的区别
1、先说@Autowired注解:
@Autowired是Spring提供的注解,需导入Package:org.springframework.beans.factory.annotation.Autowired;
@Autowired用在setter和构造器上只按照byType注入,用在成员变量field上,先按照byName注入,找不到则按照byType注入;
结合@Qualifier注解,可让setter和构造器实现byName匹配注入。
2、再说@Resource注解:
@Resource由J2EE提供,需导入javax.annotation.Resource;
@Resource有两个中重要的属性:name和type ,Spring将name属性解析为bean的名字,type属性解析为bean的类型。所以如果使用name属性,则按照byName自动注入,使用type属性则按照byType自动注入。如果既不指定name也不指定type属性,这时将通过反射机制默认使用byName自动注入。@Resource自动装配注入顺序如下:
(1). 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常;
(2). 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常;
(3). 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常;
(4). 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
@Resource分别可以用在field、setter上,无法应用在constructor构造器上
//用于field字段
@Resource
private TestBeanInterface testBean;
//用于setter
@Resource(name = "testBean1")
public void setTestBean(TestBeanInterface t){
this.testBean = t;
}
注:@Resource放在setter方法上,setter方法的参数必须是一个参数,如果是多参数,则抛出如下异常:
...
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ...
...
Caused by: java.lang.IllegalStateException: @Resource annotation requires a single-arg method:...
如果需要多参数setter或者构造器注入,还需要替换为@Autowired结合@Qualifier实现。
3、补充:集合或Map类型bean,无法通过@Autowired注解注入,因为没有类型匹配到这样的bean,必须通过@Resource注解注入,通过唯一名称引用集合或Map类型bean
五、JSR-330注解@Inject、@Name使用介绍
1、spring3.0开始支持JSR330标注注解,使用JSR330需要引入javax.inject包,使用maven引入:
<!-- 引入对JSR330的注解支持包 -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
2、@Inject:
等效于@Autowired,可以用于成员变量属性、setter方法、构造器,包括匹配注入情况和抛出异常情况,可以互换使用,代码实例参照上面@Autowired注解。
3、@Named:
如果想使用特定名称进行依赖注入,可以使用@Named,此时与@Quarlifier注解作用相同。
@Inject
private UserDao userDao1;
@Autowired
@Named("userDao2")
private UserDao userDao2;
@Inject
@Named("userDao2")
public void setUserDao2(UserDao ud2){
this.userDao2 = ud2;
}
@Inject
public UserServiceImpl(@Named("userDao1")UserDao ud1){
this.userDao1 = ud1;
}
用在类上,与@Component注解作用是等效的。
@Named("tb")
public class TestBean {
@PostConstruct
public void start(){
System.out.println("TestBean 初始化。。。");
}
@PreDestroy
public void cleanUp(){
System.out.println("TestBean 销毁。。。");
}
}