共同点
先说共同点,他们都是用来做自动装配的,当接口只有一个实现类的时候,这两个注解使用的效果一样,可以交替使用,但接口有多个实现类的时候,这两者的使用就有区别了。
不同点
@Resource
- @Resource是JDK原生的注解,有两个属性name和type,默认是按照名称(byName)来注入
- Spring会将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以使用name属性则使用byName的注入策略;使用type属性则使用byType的注入策略;如果两者都不指定,会通过反射机制使用byName自动注入策略
@Autowired
- Spring的注解,依赖于Spring的环境,即脱离了Spring后就无法使用
- 按照类型(byType)来装配依赖对象,默认情况下要求依赖对象必须存在,除非设置required为false。如果想按照名称(byName)来装配,可以结合@Qualifier注解一起使用
示例说明
假设使用场景:
登录接口,一开始我们只有默认的账密登录方式,后来我们新增了支持微信登录的方式
声明一个登录的接口service:
public interface LoginService {
void login();
}
然后是账密登录方式的实现类:
@Service
public class UsernameLoginServiceImpl implements LoginService {
@Override
public void login() {
System.out.println("账密登录");
}
}
这时候我们不管使用@Autowired还是@Resource注解来装配,效果都是一样的,在测试类中测试看看:
@SpringBootTest
@RunWith(SpringRunner.class)
public class ApplicationTest {
// @Autowired
// LoginService loginService;
@Resource
LoginService loginService;
@Test
public void test01() {
loginService.login();
}
}
输出内容:
账密登录方式
好了,现在我们新增一个继承于LoginService的微信登录接口:
@Service
public class WechatLoginServiceImpl implements LoginService {
@Override
public void login() {
System.out.println("微信登录");
}
}
这时候使用@Autowired或@Resource都会报错:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.cc.ApplicationTest': Unsatisfied dependency expressed through field 'loginService'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.cc.LoginService' available: expected single matching bean but found 2: usernameLoginServiceImpl,wechatLoginServiceImpl
意思是找到了两个符合条件的bean,Spring不知道应该注入哪一个,所以在这种接口有多个实现类的情况下,@Autowired和@Resource这两个注解的使用上会有一些区别。
多实现类场景下使用@Autowired的三种方式
-
给其中一个实现类添加@Primary注解,意为告诉Spring这个实现类的优先级最高,优先选择这个实现类进行注入
@Primary @Service public class UsernameLoginServiceImpl implements LoginService { @Override public void login() { System.out.println("账密登录"); } } -
修改待注入属性的名称为指定的实现类
@SpringBootTest @RunWith(SpringRunner.class) public class ApplicationTest { @Autowired LoginService usernameLoginServiceImpl; @Test public void test01() { usernameLoginServiceImpl.login(); } } -
注入的时候,额外通过@Qualifier来标识需要注入的实现类的名字
@SpringBootTest @RunWith(SpringRunner.class) public class ApplicationTest { @Qualifier("usernameLoginServiceImpl") @Autowired LoginService loginService; @Test public void test01() { loginService.login(); } }
多实现类场景下使用@Resource的方式
上面@Autowired的三种解决办法同样适用于@Resource,并且还可以指定@Resource注解的name值、type值来指定类型:
@SpringBootTest
@RunWith(SpringRunner.class)
public class ApplicationTest {
@Resource(name = "usernameLoginServiceImpl", type = UsernameLoginServiceImpl.class)
LoginService loginService;
@Test
public void test01() {
loginService.login();
}
}
可以单独指定name或者type属性来查找bean。
建议使用方式
如果性能上有所追求,希望加快运行速度,可以使用@Resource并指定name值,这是最快的自动装配方式,因为byName策略比起byType策略,会更快的找到对应的依赖,byType策略找到指定类型时(有多个实现类的情况),还要去找是哪一个实现类。
使用@Resource还有一个好处,是在脱离Spring环境的情况下仍然支持兼容绝大多数框架,因为他是JDK原生的注解,而@Autowired是Spring框架的。
顺带一提,在Service中注入对象的时候,如果滥用@Autowired或@Resource注解,容易破坏软件设计中的单一职责原则,即一个类应该只做一件事情。比如我们可以在userService中注入一个userMapper对象来做用户的增删改查工作,但我们不应该把类似roleMapper、menuMapper也注入到userService里面。