@Autowired和@Resource这两个注解最大的区别:
- @Autowired 根据类型注入
- @Resource 根据名称注入
以上是这两个注解最主要的装配方式,具体使用方式见下方。
以下只针对于一个接口有一个或多个实现类的情况进行讨论。没有实现类的情况不讨论,个人觉得自动注入允许为空的情况是十分不安全的,实际开发中也没有太大意义。
@Autowired
1.接口与实现类一一对应
接口:
package com.example.demo.service;
/**
* @author chenhy
* @date 2021/4/11
*/
public interface IUserService {
void say();
}
实现类01:
package com.example.demo.service.impl;
import com.example.demo.service.IUserService;
import org.springframework.stereotype.Service;
/**
* @author chenhy
* @date 2021/4/11
*/
@Service
public class UserServiceImpl01 implements IUserService {
@Override
public void say() {
System.out.println("I am UserServiceImpl01.................");
}
}
测试类:
package com.example.demo.test;
import com.example.demo.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author chenhy
* @date 2021/4/11
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
@Autowired
IUserService userService;
@Test
public void test() {
userService.say();
}
}
运行测试方法,控制台输出“I am UserServiceImpl01.................”。
此时,Spring容器中只有一个IUserService 的实现类UserServiceImpl01 ,byType进行注入时,会自动找到实现类UserServiceImpl01进行注入。从控制台的打印结果我们也可以看出,调用的是类UserServiceImpl01的方法。验证了@Autowired是根据类型注入的观点。
2.接口与实现类的关系为一对多
新建实现类02:
package com.example.demo.service.impl;
import com.example.demo.service.IUserService;
import org.springframework.stereotype.Service;
/**
* @author chenhy
* @date 2021/4/11
*/
@Service
public class UserServiceImpl02 implements IUserService {
@Override
public void say() {
System.out.println("I am UserServiceImpl02.................");
}
}
运行测试方法,控制台报错如下:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.demo.service.IUserService' available: expected single matching bean but found 2: userServiceImpl01,userServiceImpl02
意思就是有多个实现类,程序不知道到底该注入哪个实现类了。
测试类处理如下:
package com.example.demo.test;
import com.example.demo.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author chenhy
* @date 2021/4/11
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
@Autowired
@Qualifier("userServiceImpl02")
IUserService userService;
@Test
public void test() {
userService.say();
}
}
运行测试方法,控制台输出“I am UserServiceImpl02.................”。
我们新增了@Qualifier注解,该注解与@Autowired结合使用,可以在一个接口有多个实现类的情况下,指定注入的实现类的名称。此处,而我们注入了第二个实现类UserServiceImpl02。(注意,Qualifier注解中的名称首字母为小写)。
@Autowired+@Qualifier的使用,实现了bean自动注入时,先按照类型再按照名称进行注入的功能。
@Resource
1.根据名称注入(byName)
新建实现类:
package com.example.demo.service.impl;
import com.example.demo.service.IUserService;
import org.springframework.stereotype.Service;
/**
* @author chenhy
* @date 2021/4/11
*/
@Service
public class UserService implements IUserService {
@Override
public void say() {
System.out.println("I am UserService.................");
}
}
修改测试类如下:
package com.example.demo.test;
import com.example.demo.service.IUserService;
import com.example.demo.service.impl.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* @author chenhy
* @date 2021/4/11
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
// @Autowired
// @Qualifier("userServiceImpl02")
@Resource
IUserService userService;
@Test
public void test() {
userService.say();
}
}
其他文件不做改动。
现在的整体情况是,IUserService 有3个实现类,如果按类型注入的话肯定是失败的。
运行测试方法,控制台输出“I am UserService.................”。
由此可见,@Resource在进行bean注入时,首先会按照名称(byName)进行装配。
2.根据类型注入
将UserService.java全部注释掉,再次运行测试方法进行测试。
运行测试程序,此时控制台报错如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.example.demo.test.UserTest': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.demo.service.IUserService' available: expected single matching bean but found 2: userServiceImpl01,userServiceImpl02
与之前测试Autowired注解的情形相同,按照类型注入失败了。
测试类处理如下:
package com.example.demo.test;
import com.example.demo.service.IUserService;
import com.example.demo.service.impl.UserServiceImpl01;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* @author chenhy
* @date 2021/4/11
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
// @Autowired
// @Qualifier("userServiceImpl02")
@Resource(type = UserServiceImpl01.class)
IUserService userService;
@Test
public void test() {
userService.say();
}
}
运行测试程序,控制台输出“I am UserServiceImpl01.................”。
通过在@Resource注解中指定注入的实现类来实现bean的注入。
此外,还可以在@Resource注解中指定类名称。
总结
对比项 | @Autowired | @Resource |
---|---|---|
注解来源 | Spring注解 | JDK注解(JSR-250标准注解,属于J2EE) |
装配方式 | 默认byType,其次byName | 默认byName,其次byType |
属性 | required | name、type |
作用范围 | 字段、setter方法、构造器 | 字段、setter方法 |
1.处理这2个注解的BeanPostProcessor不一样
CommonAnnotationBeanPostProcessor是处理@ReSource注解的;
AutoWiredAnnotationBeanPostProcessor是处理@AutoWired注解的。
2.注入方式不同
@Autowired只按照byType注入;
@Resource默认按byName自动注入,也提供按照byType注入。
3.属性不同
@Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。
@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
4.装配顺序不同
@Autowired:首先通过类型来查找bean,如果只找到一个,则直接注入,如果没有找到,则抛出异常;如果找到多个bean也会抛出异常。
解决方法一:可以在配置bean的时候加上@Primary注解,来提高优先级,这样就不会报错;
解决方法二:会默认使用字段名来匹配,如果没有匹配上,抛出异常。
如果需要直接通过bean的id来查找,可以配合@Qualifier来使用,没有找到抛出异常。
这里重点需要指出,使用@Autowired 是有优先级的,@Qualifier > 按类型找(如果找到多个继续使用之后的策略) > @Primary > 按名字找
@Autowired通过设置required=false,在没有找到bean的情况下,不会抛出异常。
@Resource装配顺序:
如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。