@Resource与@Autowired用法区别和使用场景

使用场景

原文链接:@Resource和@Autowired用谁?

我选了@Resource

  1. 当注入的属性是接口
      1. 在接口只有一个实现类的时候,@Resource和@Autowired 在功能上是没有区别的
      2. 如果接口有多个实现类,在写法上, @Autowired还需要搭配@Qualifier或者@Primary, 而@Resource不需要, 自身属性即可满足

  2. 在使用 @Autowired 时, Idea会出现提示, 强迫症看着很不舒服
    spring Team recommends: “Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies”.

    spring团队推荐用构造函数来注入Bean。

    基于以上,就选择了看起来和写起来都比较简单的@Resource注解

原因

原文链接:@Resource与@Autowired用法区别

spring中,@Resource@Autowired都是做bean的注入时使用。使用过程中,有时候 @Resource@Autowired 可以替换使用;有时,则不可以。

下面,根据自己的学习,整理下这两个注解使用中的共同点和不同点,及用法上的不同。

共同点

@Resource@Autowired都可以作为注入属性的修饰,在接口仅有单一实现类时,两个注解的修饰效果相同,可以互相替换,不影响使用。

不同点

  • @Resource是 Java 自己的注解,@Resource 有两个属性是比较重要的,分是 name 和 type;Spring 将 @Resource 注解的 name 属性解析为 bean 的名字,而 type 属性则解析为 bean 的类型。所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性,这时将通过反射机制使用 byName 自动注入策略。
  • @Autowired 是 spring 的注解,是 spring2.5 版本引入的,Autowired 只根据 type 进行注入,不会去匹配 name。如果涉及到 type 无法辨别注入对象时,那需要依赖 @Qualifier@Primary 注解一起来修饰。

演示

我们创建一个简单的springboot项目demo,

定义一个接口Human.java,里面一个方法 runMarathon,

一个实现类Man.java

一个Controller类HumanController.java,里面注入Human接口的实现

附各Java类源码

Human

package com.example.annotation.service;

/**
 * service接口定义
 * @author Administrator
 */
public interface Human {
    /**
     * 跑马拉松
     * @return
     */
    String runMarathon();
}

Man

package com.example.annotation.service.impl;

import com.example.annotation.service.Human;
import org.springframework.stereotype.Service;

/**
 * service接口第一实现类
 * @author Administrator
 */
@Service
public class Man implements Human {
    public String runMarathon() {
        return "A man run marathon";
    }
}

HumanController

package com.example.annotation.controller;

import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.annotation.service.Human;

/**
 * controller层实现类
 * @author Administrator
 */
@RestController
@RequestMapping("/an")
public class HumanController {

    @Resource
    private Human human;

    @RequestMapping("/run")
    public String runMarathon() {
        return human.runMarathon();
    }
}

至此,代码整理完成,启动 springboot,浏览器地址栏输入 http://localhost:8080/an/run

改动一

将HumanController.java 类中的注解替换为 @Autowired,再次启动,可以正常访问,与上图相同,这里不再贴访问结果图。

改动二

再增加一个实现类 Woman.java

Woman

package com.example.annotation.service.impl;

import com.example.annotation.service.Human;
import org.springframework.stereotype.Service;

/**
 * service接口第二实现类
 * @author Administrator
 */
@Service
public class Woman implements Human {
    public String runMarathon() {
        return "An woman run marathon";
    }
}

HumanController.java 注解使用 @Resource

    @Resource
    private Human human;

启动springboot,控制台会报错,报错信息太多,截取关键信息

2018-09-10 16:07:10.362 WARN 5592 — [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization – cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘humanController’: Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.example.annotation.service.Human’ available: expected single matching bean but found 2: man,woman

找关键信息 expected single matching bean but found 2: man,woman,被期望的单一结果被匹配到两个结果man和woman。

这里,我们需要借助 @Resource 注解的 name 属性或 @Qualifier 来确定一个合格的实现类

代码修改为

    @Resource(name="woman")
    private Human human;

    @Resource
    @Qualifier("woman")
    private Human human;

上面,我们指定了Human接口的实现类是Woman.java,启动springboot,访问 http://localhost:8080/an/run

改动三

在改动二的基础上,将注解替换为 @Autowired,启动报错

Description:

Field human in com.example.annotation.controller.HumanController required a single bean, but 2 were found:
– man: defined in file [D:\DEV_ENV\springbootws\annotation\target\classes\com\example\annotation\service\impl\Man.class]
– woman: defined in file [D:\DEV_ENV\springbootws\annotation\target\classes\com\example\annotation\service\impl\Woman.class]

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

报错信息很明显,HumanController需要一个bean实现,但是找到了两个 man 和woman

解决方案:使用 @Primary 注解,在有多个实现bean时告诉spring首先@Primary修饰的那个;或者使用@Qualifier来标注需要注入的类。

@Qualifier 修改方式与改动二的相同,依然是修改HumanController.java 中间注入的Human上面,这里不再复述

@Primary 是修饰实现类的,告诉spring,如果有多个实现类时,优先注入被@Primary注解修饰的那个。这里,我们希望注入Man.java ,那么修改Man.java为

package com.example.annotation.service.impl;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import com.example.annotation.service.Human;

/**
 * service接口第一实现类
 * @author Administrator
 */
@Service
@Primary
public class Man implements Human {

    public String runMarathon() {
        return "A man run marathon";
    }
}

启动springboot后,可以看到注入的已经是Man.java 了。

发表评论