@Resource @Autowired @Inject三者区别 @Resource是否已经过时?

前言

在之前的工作中,一直复制着前辈们注入依赖的方法。没有去细细品味这些注入依赖的注解到底有何不同。

今天看到某博客的一个用户提出了一个观点:@Resource只有外包才用,人家国外早淘汰了,都用@Inject。深感惭愧,由于我本人实习以来都一直使用的@Resource,看到这种观点就很诧异,我尼玛我刚工作就落伍了啊,吓得我感觉来了解学习一波。

在了解三者的区别异同之前,先得熟悉他们得来源。下表中表述了本次内容中所涉及到的注解及作用说明。

主要注解及说明
注解名 包名 所属 Java规范 作用
@Resource javax.annotation.Resource Java JSR-250(Spring2.5后支持) 依赖注入
@Inject javax.inject.Inject Java JSR-330(Spring3.0后支持) 依赖注入
@Named javax.inject.Named  Java JSR-330(Spring3.0后支持) 被@Qualifier注解,注入时根据名称匹配bean
@Qualifier javax.inject.Qualifier Java JSR-330(Spring3.0后支持) 只能注解其他注解。用于实现根据名称匹配bean
@Autowired org.springframework.beans.factory.annotation.Autowired Spring   依赖注入
@Qualifier org.springframework.beans.factory.annotation.Qualifier Spring   注入时根据名称匹配bean

 

 

注解的含义和作用

 

 

1. 注解的匹配规则

总的来说,三种注解进行注入时,一般只有根据name和type两种执行路径(execution paths)并以此路径的先后顺序作为注入的匹配规则。当使用@Qualifier注解时,才会根据@Qualifier设置的名称进行匹配这条规则。

Match by Name 通过名称匹配bean

Match by Type 通过类型匹配bean

Match by Qualifier 指定要匹配bean的名字 @Qulifier注解为spring下的注解类,支持JSR-330规范时也可以使用@Named类,效果相同。

一般情况下,三种注解的匹配顺序为下:

    @Resource:当和@Qualifier一起使用时,不会影响匹配的顺序。
        匹配顺序:
            Match by Name
            Match by Type
            Match by Qualifier(当已经通过名称匹配bean时,忽略该匹配)
    @Inject:可以和@Named使用,效果同@Qulifier相同。
        匹配顺序:
            Match by Type
            Match by Qualifier
            Match by Name
    @Autowired:在和@Qualifier(此时,一般有多个同类型的bean,以至于type匹配失效。如果没有多个而使用Qualifier注解是完全没必要的)一起使用时,会匹配Qualifier设置的名称。此时的匹配规则同@Resource相同
        匹配顺序:
            Match by Type
            Match by Qualifier
            Match by Name

 

2. @Resource注解

@Resource注解来自javax.annotation.Resource包,使用CommonAnnotationBeanPostProcessor类注入依赖。属于JSR-250的规范,因此所有支持该规范的框架都可以使用该注解。@Resource有两个主要参数:

name:当@Resource用于注解字段或setter时,name如果不填,会将字段名作为默认值(注解setter时,也是获取该字段的字段名而非传入的形参名)。

type:Resource也可以为需要注入的字段指定具体的类,来进行注入依赖。以此改变匹配顺序。

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {

    /**
     * The JNDI name of the resource.  For field annotations,
     * the default is the field name.  For method annotations,
     * the default is the JavaBeans property name corresponding
     * to the method.  For class annotations, there is no default
     * and this must be specified.
     */
    String name() default "";



    /**
     * The Java type of the resource.  For field annotations,
     * the default is the type of the field.  For method annotations,
     * the default is the type of the JavaBeans property.
     * For class annotations, there is no default and this must be
     * specified.
     */
    Class type() default java.lang.Object.class;

}

 

3. @Inject注解、@Named注解

 

@Inject注解和@Named注解位于同一个包javax.inject下,都属于JSR-330规范。@inject注解的作用与@Autowired注解相同。且@inject注解无参数。

import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;

// 这里的写法是因为引入枚举值时,将其设置为静态字段
@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}

@Inject和@Named同时使用时其作用等同于@Autowired和@Qualifier(Spring)。不过需要注意的是,前者基于Java的JSR-330规范,在任何支持该规范的框架都能使用,而后者仅支持在Spring框架中使用。

@Name注解有着与@Qualifier(Spring)注解相同的作用,就是因为被同包下的@Qualifier(Java)所注解。

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

    /** The name. */
    String value() default "";
}

我们可以通过被@Qualifier(Java)注解,来生成新的自定义名称扫描的注解:

  • 自定义的注解被@Qualifier,@Retention(RUNTIME),以及有可能被@Documented修饰。
  • 自定义注解可以有自己的参数
  • 可以是公共API的一部分,也可以是依赖类型。但不能是一部分公共API的实现类型。
  • 如果被@Target修饰,例如@Target指定为字段或者参数,那么就无法为类、方法、构造器等进行注入配置。
/**
 * Identifies qualifier annotations. Anyone can define a new qualifier. A
 * qualifier annotation:
 *
 * 
    *
  • is annotated with {@code @Qualifier}, {@code @Retention(RUNTIME)}, * and typically {@code @Documented}.
  • *
  • can have attributes.
  • *
  • may be part of the public API, much like the dependency type, but * unlike implementation types which needn't be part of the public * API.
  • *
  • may have restricted usage if annotated with {@code @Target}. While * this specification covers applying qualifiers to fields and * parameters only, some injector configurations might use qualifier * annotations in other places (on methods or classes for example).
  • *
* *

For example: * *

 *   @java.lang.annotation.Documented
 *   @java.lang.annotation.Retention(RUNTIME)
 *   @javax.inject.Qualifier
 *   public @interface Leather {
 *     Color color() default Color.TAN;
 *     public enum Color { RED, BLACK, TAN }
 *   }
* * @see javax.inject.Named @Named */ @Target(ANNOTATION_TYPE) @Retention(RUNTIME) @Documented public @interface Qualifier {}

 

4. @Autowired注解和@Qualifier注解

 

@Autowired注解与@Inject注解功能相同,都是使用AutowiredAnnotationBeanPostProcessor类注入依赖。仅有一个注解标明该依赖是否为必需。

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER
, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

	/**
	 * Declares whether the annotated dependency is required.
	 * 

Defaults to {@code true}. */ boolean required() default true; }

 

@Qualifier注解与@Autowired一起使用时可以用来消除歧义。使用@Qualifier的场景,一般是有多个同类型的bean导致@Autowired根据type匹配失效,这时就可以通过@Qualifier精准定位到我们需要注入的bean。其实在多个同类型的bean的情况下,我们也可以不使用@Qualifier,使用@Autowired的name匹配规则一样可以达成目的。

 

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER
, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {

	String value() default "";

}

 

参考:

Wiring in Spring: @Autowired, @Resource and @Inject

The Spring @Qualifier Annotation

 

 

几个简单的实例

 

1. 新建测试类

新建一个接口类MatchService,两个实现类DogServiceImpl和CatServiceImpl。之后会分别演示三种注解的实现方式。

// 匹配接口
public interface MatchService {

  String getName();

}



// 狗
@Service(value = "dog")
public class DogServiceImpl implements MatchService {
  @Override
  public String getName() {
    return "Dog";
  }
}



// 猫
@Service(value = "cat")
public class CatServiceImpl implements MatchService {
  @Override
  public String getName() {
    return "Cat";
  }
}

2. Match by Name


  // 由于有多个同类型的bean (dog,cat都属于MatchService类)
  // 这里type匹配失效,qualifier没有设置,到最后还是会根据名字匹配
  @Autowired
  MatchService dog;

  // 同上
  @Inject
  MatchService dog;

  @Resource
  MatchService dog;

 

3. Match by Type

三种注解根据类型匹配时,只有在该类型再容器中只有一个时才会生效,有多个同类型的bean会抛出异常org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type

这里我们可以把cat类先注释掉,用例如下:

  // 这里的字段名是为了区分bean名,
  @Autowired
  MatchService matchService;  



  @Inject
  MatchService matchService;



  // 此时由于设置了type参数,使得type优先级提到了最前
  @Resource(type = MatchService.class)
  MatchService matchService;

 

4. Match by Qualifier

当Resource同时使用Named和Qualifier时,如何指定得名称相同,则正常运行,否则程序将会启动失败。

下面得例子中,都不满足type和name匹配,确保用例是由Named和Qualifier匹配。

  @Inject
  @Named(value = "dog")
  MatchService matchService;


  @Autowired
  @Qualifier(value = "cat")
  MatchService matchService;



  // 在之前说过得条件满足下两种使用都可以
  @Resource
  @Named(value = "dog")
  // @Qualifier(value = "cat")
  MatchService matchService;

Named和Qualifier名称不同时,resource报错信息

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-09-03 15:58:05.999 ERROR 20704 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

A component required a bean of type 'com.training.spring.service.MatchService' that could not be found.

The following candidates were found but could not be injected:
	- User-defined bean
	- User-defined bean


Action:

Consider revisiting the entries above or defining a bean of type 'com.training.spring.service.MatchService' in your configuration.

 

分析与总结

 

@Resource @Autowired @Inject三者异同:

相同点:

1. 相同点在于三者都是用于依赖注入(DI),其功能相同。

不同点:

2. 所属框架和版本不同。@Resource和@Inject都来自Java源代码,但是@Inject版本更高,二者都可以用于支持其规范的框架中。而@Autowired来自Spring框架,且只用于Spring框架。

3. 匹配规则不同。@Resource优先匹配名称,@Autowired、@Inject优先匹配类型。

4. @Qualifier或@Named影响范围不同。@Resource中即使配置了@Qualifier,如果已经通过名称匹配到了bean,那么还是会根据名称匹配来进行注入。而在多类型中,@Autowired和@Inject则是会先进行@Qualifier匹配,匹配失败才会根据名称匹配来完成注入。

 

至此解惑了,前文提到的@Resource过时的观点有一定道理但是也不全对,@Resource对于老项目老说还是中流砥柱般的存在的,因为老项目框架太杂,啥玩意都有,甚至存在多个版本同时导入的可能,因此用Resource是比较稳定的。当然我觉得还是根据使用场景,用自己喜欢用的注解就好。

 

你可能感兴趣的:(Spring,spring,java,bean)