@Autowired、@Resource和@Inject注解的区别(最详细)

java1234

共 11130字,需浏览 23分钟

 ·

2021-01-06 15:05

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

76套java从入门到精通实战课程分享

 在Spring中依赖注入可以使用@Autowired、@Resource和@Inject来完成,并且在一般的使用中是可以相互替换的(注意是一般),不过三者还是有区别,今天来介绍一下他们的区别:

     如果你想更加深入的了解@Autowired、@Resource的区别,请移步:Spring源码分析@Autowired、@Resource注解的区别,这里面可能是你见过最详细的关于@Autowired、@Resource的区别,看完之后,面试你懂得。

     @Autowired注解:

        1.Spring本身替换的注解(org.springframework.beans.factory.annotation.Autowired),需要导入Spring相应的jar包才能使用

        2.可以标注的位置:构造器、方法、方法参数、变量域和注解上面

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

       3.在Spring容器解析@Autowired注解时,使用的后置处理器为AutowiredAnnotationBeanPostProcessor,在这个后置处理的注释中有这么一段:

{@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessor}
* implementation that autowires annotated fields, setter methods, and arbitrary
* config methods. Such members to be injected are detected through annotations:
* by default, Spring's {@link Autowired @Autowired} and {@link Value @Value}
* annotations.
*

Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation,
if available, as a direct alternative to Spring's own {@code @Autowired}.

        4. @Autowired注解有一个required属性,当指定required属性为false时,意味着在容器中找相应类型的bean,如果找不到则忽略,而不报错(这一条是两个注解所没有的功能)。

       5. 默认优先按照类型去容器中找对应的组件,找到就赋值,如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找,如果组件id对象的bean不存在,而且required属性为true,就报错。

       6. 支持@Primary注解,让Spring进行自动装配的时候,默认使用首选的bean;

      @Resource

         1.JSR250规范提供的注解(javax.annotation.Resource),不需要导入格外的包,这个注解在JDK的rt.jar包中

         2.可以标注的位置:TYPE(表示可以标注在接口、类、枚举),FIELD(变量域)和METHOD(方法)上面。

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
    String name() default "";
 
    String lookup() default "";
 
    Class type() default java.lang.Object.class;
 
    enum AuthenticationType {
            CONTAINER,
            APPLICATION
    }
 
    AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
 
    boolean shareable() default true;
 
    String mappedName() default "";
 
    String description() default "";
}


      3.在Spring容器解析@Resource注解时,使用的后置处理器为CommonAnnotationBeanPostProcessor,在这个后置处理的注释中有这么一段: 

* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
* that supports common Java annotations out of the box, in particular the JSR-250
* annotations in the {@code javax.annotation} package. These common Java
* annotations are supported in many Java EE 5 technologies (e.g. JSF 1.2),
* as well as in Java 6's JAX-WS.
*

This post-processor includes support for the {@link javax.annotation.PostConstruct}
* and {@link javax.annotation.PreDestroy} annotations - as init annotation
* and destroy annotation, respectively - through inheriting from
* {@link InitDestroyAnnotationBeanPostProcessor} with pre-configured annotation types.
*

The central element is the {@link javax.annotation.Resource} annotation
* for annotation-driven injection of named beans, by default from the containing
* Spring BeanFactory, with only {@code mappedName} references resolved in JNDI.
* The {@link #setAlwaysUseJndiLookup "alwaysUseJndiLookup" flag} enforces JNDI lookups
* equivalent to standard Java EE 5 resource injection for {@code name} references
* and default names as well. The target beans can be simple POJOs, with no special
* requirements other than the type having to match.

      4. 默认是按照组件名称进行装配的

         这样说大家可能没有很深的印象,为啥@Autowired是根据类型,@Resource是根据组件名称,下面使用代码来进行解释:         

@Component
public class Student {
    private String num = "1";
    public String getNum() {
        return num;
    }
    public void setNum(String num) {
        this.num = num;
    }
}
@Configuration
@ComponentScan(basePackages = {"it.cast.autowired"})
public class Config {
 
    @Bean
    public Student student1(){
        Student student = new Student();
        student.setNum("2");
        return student;
    }
}
@Component
public class Teacher {
 
    @Resource   //注意这里使用的@Resource注解,Spring支持注入Map、Conllent类型的属性变量
    private Map student;
    public Map getStudent() {
        return student;
    }
 
    public void setStudent(Map student) {
        this.student = student;
    }
 
}
public class Test01 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        Teacher teacher = context.getBean(Teacher.class);
        System.out.println(teacher.getStudent());
    }
}
 
//打印结果:
//Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'student' is expected to be of type 'java.util.Map' but was actually of type 'it.cast.autowired.Student'
// at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:392)
// at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
// at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeanByName(AbstractAutowireCapableBeanFactory.java:452)
// at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:527)
// at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:497)
// at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:637)
// at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:180)
// at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
// at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:322)
// ... 12 more
 

       可以看到使用@Resource标注的Map类型的时报错,为什么报错?因为Map类型的变量名为student,容器中已经存在了名称为student的bean,其类型为Student,所以会报错,大致步骤为:根据组件名称student去Spring容器中获取相应的Bean,在获取之后,会将获取到的Bean赋值给相应的属性。

      如果此时我们将@Resource换成@Autowired时,其打印结果又会如何?

@Component
public class Student {
    private String num = "1";
    public String getNum() {
        return num;
    }
    public void setNum(String num) {
        this.num = num;
    }
}
@Configuration
@ComponentScan(basePackages = {"it.cast.autowired"})
public class Config {
 
    @Bean
    public Student student1(){
        Student student = new Student();
        student.setNum("2");
        return student;
    }
}
@Component
public class Teacher {
 
    @Autowired  //注意这里使用的@Autowired注解,Spring支持注入Map、Conllent类型的属性变量
    private Map student;
    public Map getStudent() {
        return student;
    }
 
    public void setStudent(Map student) {
        this.student = student;
    }
 
}
public class Test01 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        Teacher teacher = context.getBean(Teacher.class);
        System.out.println(teacher.getStudent());
    }
}
//其打印结果:
//{student=it.cast.autowired.Student@61009542, student1=it.cast.autowired.Student@77e9807f}


      可以看到系统并没有报错,根据上面两组代码的对比,可以得出以下结论:当使用@Resource注解时,是根据组件名称进行查找,当使用@Autowired注解时,是根据类型进行查找的。

      5. 支持@Primary注解,不过首先按照会按照名称进行注入bean,如果Spring IOC容器中没有该Bean,则按照@Primary注解标注的bean进行装配(这条是我自己总结的,别人都说不支持,但是代码是不会骗人的,给出验证代码,如有错误,请多指教,这个代码的逻辑其实可以看一下CommonAnnotationBeanPostProcessor是怎么处理的,有时间我来看看源码

     下面验证@Resource默认是按照组件名称进行装配的和持支@Primary注解的:

@Configuration
@ComponentScan({"it.cast.resouce"})
public class ResourceConfig {
 
    @Primary   //标有Primary注解,使用@Autowired@Inject注解注解时,优先被加载
    @Bean
    public Y y1(){
        Y y = new Y();
        y.setI(0);
        return y;
    }
 
}
 
@Component
public class X {
    @Resource   //这里使用的是@Resource注解,该注解默认按照组件名称进行装配的,所以会优先加载id为y的bean
    private Y y;
 
    public Y getY() {
        return y;
    }
 
    public void setY(Y y) {
        this.y = y;
    }
}
 
@Component
public class Y {
    private int i = 2;
 
    public int getI() {
        return i;
    }
 
    public void setI(int i) {
        this.i = i;
    }
}


      测试一下使用,使用@Resource注解的打印结果:

    @Test
    public void ResourceConfigTest(){
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(ResourceConfig.class);
 
        X bean = context.getBean(X.class);
        System.out.println(bean.getY().getI());
    }
    //输出结果为:
    //     2
    //从而验证了@Resource默认按照名称进行加载


     此时,将@Resource注解的属性名称换成y12,这个bean在容器里面没有的

@Configuration
@ComponentScan({"it.cast.resouce"})
public class ResourceConfig {
 
    @Primary   //标有Primary注解,使用@Autowired@Inject注解注解时,优先被加载
    @Bean
    public Y y1(){
        Y y = new Y();
        y.setI(0);
        return y;
    }
 
}
 
@Component
public class X {
    @Resource       //这里使用的是@Resource注解,该注解默认按照组件名称进行装配的,所以会优先加载id为y12的bean,
    private Y y12;  //如果找不到则按Primary注解标注的bean进行注入
 
    public Y getY() {
        return y12;
    }
 
    public void setY(Y y) {
        this.y12 = y;
    }
}
 
@Component
public class Y {
    private int i = 2;
 
    public int getI() {
        return i;
    }
 
    public void setI(int i) {
        this.i = i;
    }
}


      测试一下使用,使用@Resource注解的打印结果: 

    @Test
    public void ResourceConfigTest(){
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(ResourceConfig.class);
 
        X bean = context.getBean(X.class);
        System.out.println(bean.getY().getI());
    }
    //输出结果为:
    //     0
    //由于没有找到id为y12的bean,所以注入了使用@Primary标注的bean,
    //而且整个程序没有报错,所以验证了@Resource支持@Primary注解

      此时,将@Resource注解换成@Autowired注解的打印结果:

@Configuration
@ComponentScan({"it.cast.resouce"})
public class ResourceConfig {
 
    @Primary   //标有Primary注解,使用@Autowired@Inject注解注解时,优先被加载
    @Bean
    public Y y1(){
        Y y = new Y();
        y.setI(0);
        return y;
    }
 
}
 
@Component
public class X {
    @Autowired   
    private Y y;  //此时不管名称是y还是y12,都会使用标有Primary注解的bean
 
    public Y getY() {
        return y;
    }
 
    public void setY(Y y) {
        this.y = y;
    }
}
 
@Component
public class Y {
    private int i = 2;
 
    public int getI() {
        return i;
    }
 
    public void setI(int i) {
        this.i = i;
    }
}


    测试一下使用,使用@Autowired注解的打印结果: 

    @Test
    public void ResourceConfigTest(){
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(ResourceConfig.class);
 
        X bean = context.getBean(X.class);
        System.out.println(bean.getY().getI());
    }
    //输出结果为:
    //     0
    //从而验证了@Autowired支持@Primary注解

      @Inject

          1.JSR330规范提供的注解(javax.inject.Inject),主要导入javax.inject包才能使用

          2.可以标注的位置:方法、构造器和变量域中

@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}  //该注解中没有任何属性

         3.在Spring容器解析@Inject注解时,使用的后置处理器和@Autowired是一样的,都是AutowiredAnnotationBeanPostProcessor。

         4.由于@Inject注解没有属性,在加载所需bean失败时,会报错

         除了上面的不同点之后,@Inject和@Autowired完全等价。


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:

https://blog.csdn.net/qq_35634181/article/details/104276056





粉丝福利:Java从入门到入土学习路线图

???

?长按上方微信二维码 2 秒


感谢点赞支持下哈 

浏览 32
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报