Spring:自动装配的疑惑,@Autowired和@Resource梳理

在研究自动装配@Resource和@Autowired的时候,@Autowired注解的装配方式有点疑惑,琢磨了下

首先@Resource是默认是byName,,而@Autowired是默认按byType

那么首先setter方法添加一个@Autowired注解

package com.lihuia.ioc;

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

/**
* Copyright (C), lihuia.com
* FileName: Demo
* Author: lihui
* Date: 2018/7/29
*/

public class Demo {

private Girl girl;

@Autowired
public void setGirl(Girl girl) {
this.girl = girl;
}

public void sayHello() {
System.out.println(girl.getName());
}
}

此时XML里配置如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.xsd">

<bean id="girl" class="com.lihuia.ioc.Girl"></bean>
<bean id="girl1" class="com.lihuia.ioc.Girl"></bean>
<bean id="demo" class="com.lihuia.ioc.Demo"></bean>
</beans>

由于@Autowired是byType的,但是我配置了两个同Type的bean,先以为会报错,因为装配的时候不清楚要选择哪个bean,但是结果却是正常执行,说明了还是能够正常选择某一个bean装配

这时可以做一个简单的测试,修改下@Autowired注解对应的Name

package com.lihuia.ioc;

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

/**
* Copyright (C), lihuia.com
* FileName: Demo
* Author: lihui
* Date: 2018/7/29
*/

public class Demo {

private Girl girl;

@Autowired
public void setGirl(Girl girl1) {
this.girl = girl1;
}

public void sayHello() {
System.out.println(girl.getName());
}
}

这时候还是能够正常执行,继续修改Name

@Autowired
public void setGirl(Girl girl2) {
this.girl = girl2;
}

此时IDEA里girl2变红了,说明有问题了,根据这种测试可以得出:默认@Autowired先匹配byType,假如匹配了多个bean id,那么还会继续匹配byName

这里可以继续做一个简单测试,加一个注解让@Autowired按byName来加载

package com.lihuia.ioc;

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

/**
* Copyright (C), lihuia.com
* FileName: Demo
* Author: lihui
* Date: 2018/7/29
*/

public class Demo {

private Girl girl;

@Autowired
@Qualifier(value = "girl")
public void setGirl(Girl girl2) {
this.girl = girl2;
}

public void sayHello() {
System.out.println(girl.getName());
}
}

此时运行时正常的,说明假如设置了匹配byName,那么只要Name匹配上了,就直接装配bean

如果将value修改为一个没法匹配上的Name,那么匹配byName无法匹配上,就直接报错不会继续匹配byType了

换成@Resource注解

package com.lihuia.ioc;

import javax.annotation.Resource;

/**
* Copyright (C), lihuia.com
* FileName: Demo
* Author: lihui
* Date: 2018/7/29
*/

public class Demo {

private Girl girl;

@Resource
public void setGirl(Girl girl) {
this.girl = girl;
}

public void sayHello() {
System.out.println(girl.getName());
}
}

由于它默认就是byName,因此直接可以找到bean id,匹配上对应的bean

下面一种情况就比较好玩了,修改Name,使得匹配不上

package com.lihuia.ioc;

import javax.annotation.Resource;

/**
* Copyright (C), lihuia.com
* FileName: Demo
* Author: lihui
* Date: 2018/7/29
*/

public class Demo {

private Girl girl;

@Resource
public void setGirl2(Girl girl2) {
this.girl = girl2;
}

public void sayHello() {
System.out.println(girl.getName());
}
}

此时是没有girl2这个Name的,因此byName是匹配不上的,执行的结果报了一个错误

七月 29, 2018 11:54:50 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getTestExecutionListeners
信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@551aa95a, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@35d176f7, org.springframework.test.context.support.DirtiesContextTestExecutionListener@1dfe2924]
七月 29, 2018 11:54:51 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [demo.xml]
七月 29, 2018 11:54:51 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.GenericApplicationContext@52f759d7: startup date [Sun Jul 29 23:54:51 CST 2018]; root of context hierarchy
七月 29, 2018 11:54:51 下午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'demo': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lihuia.ioc.Girl' available: expected single matching bean but found 2: girl,girl1
七月 29, 2018 11:54:51 下午 org.springframework.test.context.TestContextManager prepareTestInstance
严重: Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@35d176f7] to prepare test instance [com.lihuia.ioc.DemoTest@64729b1e]

倒数第三行,错误信息,匹配byType=Girl的时候,发现了两个bean的类型是Girl导致不知道应该装配哪一个

也就是说默认@Resource注解,如果byName没匹配上,那么就byType来匹配,但是假如有多个bean是目标Type,还是会失败,只有唯一一个Type类型的bean存在,才能装配成功

如果在@Resource注解里指定了name

package com.lihuia.ioc;

import javax.annotation.Resource;

/**
* Copyright (C), lihuia.com
* FileName: Demo
* Author: lihui
* Date: 2018/7/29
*/

public class Demo {

private Girl girl;

@Resource(name = "girl")
public void setGirl(Girl girl) {
this.girl = girl;
}

public void sayHello() {
System.out.println(girl.getName());
}
}

只要是byName匹配上了,就直接根据该bean来装配;假如byName没有匹配上,就没法装配,也不会再按byType来匹配

如果在@Resource注解里指定了type

package com.lihuia.ioc;

import javax.annotation.Resource;

/**
* Copyright (C), lihuia.com
* FileName: Demo
* Author: lihui
* Date: 2018/7/29
*/

public class Demo {

private Girl girl;

@Resource(type = Girl.class)
public void setGirl(Girl girl) {
this.girl = girl;
}

public void sayHello() {
System.out.println(girl.getName());
}
}

如果只指定了Type,那么久只匹配byType,匹配上了就直接装配;假如指定的Type没有匹配上,就没法装配,不会再按byName来匹配

最后如果同时指定Name和Type

package com.lihuia.ioc;

import javax.annotation.Resource;

/**
* Copyright (C), lihuia.com
* FileName: Demo
* Author: lihui
* Date: 2018/7/29
*/

public class Demo {

private Girl girl;

@Resource(name = "girl", type = Girl.class)
public void setGirl(Girl girl) {
this.girl = girl;
}

public void sayHello() {
System.out.println(girl.getName());
}
}

根据上面的例子也能够知道逻辑

byName和byType会同时匹配,必须同时匹配上,才能装配bean,有一个没匹配上就会报错无法装配

综上所述,总结一下:

@Autowired:

默认注解,先匹配byType,如果匹配上了唯一的bean,直接装配;如果匹配上了多个bean,那么继续匹配byName,找到唯一的bean,装配

注解设置name,先匹配byName,如果匹配上了,直接装配;如果byName没匹配上,直接报错,不会继续匹配了

@Resource

默认注解,先匹配byName,如果匹配上了,直接装配;如果byName没匹配上,继续匹配byType,假如有唯一的bean,直接装配;如果byName没匹配上,继续匹配byType,假如有多个bean,失败报错

注解设置name,先匹配byName,如果匹配上了,直接装配;如果byName没匹配上,直接报错,不会继续匹配了

注解设置type,先匹配byType,如果匹配上了,直接装配;如果byType没匹配上,直接报错,不会继续匹配了

注解设置了name和type,同时匹配byName和byType,只有都匹配上了,才能装配;有任意一个没匹配上,直接报错,失败

发表评论