@Resource和@Autowired的区别

307 阅读3分钟

共同点

先说共同点,他们都是用来做自动装配的,当接口只有一个实现类的时候,这两个注解使用的效果一样,可以交替使用,但接口有多个实现类的时候,这两者的使用就有区别了。

不同点

@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的三种方式

  1. 给其中一个实现类添加@Primary注解,意为告诉Spring这个实现类的优先级最高,优先选择这个实现类进行注入

    @Primary
    @Service
    public class UsernameLoginServiceImpl implements LoginService {
        @Override
        public void login() {
            System.out.println("账密登录");
        }
    }
    
  2. 修改待注入属性的名称为指定的实现类

    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class ApplicationTest {
    ​
        @Autowired
        LoginService usernameLoginServiceImpl;
    ​
        @Test
        public void test01() {
            usernameLoginServiceImpl.login();
        }
    }
    
  3. 注入的时候,额外通过@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里面。