@Autowired和@Resource这两个注解最大的区别:
@Autowired 根据类型注入
@Resource 根据名称注入
以上是这两个注解最主要的装配方式,具体使用方式见下方。 以下只针对于一个接口有一个或多个实现类的情况进行讨论。没有实现类的情况不讨论,个人觉得自动注入允许为空的情况是十分不安全的,实际开发中也没有太大意义。
@Autowired 1.接口与实现类一一对应 接口:
1 2 3 4 5 6 7 8 9 package com.example.demo.service; /** * @author chenhy * @date 2021/4/11 */ public interface IUserService { void say(); }
实现类01:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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................."); } }
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 意思就是有多个实现类,程序不知道到底该注入哪个实现类了。
测试类处理如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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) 新建实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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................."); } }
修改测试类如下:
.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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注解的情形相同,按照类型注入失败了。 测试类处理如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。