通过源码和实践纠正对@Autowired与@Resource的固有认知

1 缘起

这是一篇耽误了很久的文章。
因为,在初学Spring时,入门级问题:@Resource和@Autowired的区别?
我也查了一些,都说是注入的方式不同,
但是,我实际验证的过程中,发现,注入的方式是相同的,
都是先按照Bean名称注入,无法注入时,
再按照Bean类型注入。
不同,是@Resource是Java原生的通用注解,
@Autowired是Spring基于JSR-330依赖注入规范的另一种实现,
添加了注入Bean时,Bean是否必须存在属性。
纠正我之前查到的,分享如下。


结论:

  • @Resource和@Autowired均是默认通过Bean名称(byName)注入;
  • @Resource和@Autowired匹配不到Bean名称时才会自动切换到匹配Bean类型(byType);
  • @Resource和@Autowired注入多个同类型的Bean时需要为某个Bean添加primary=true保证成功注入。

2 Resource

Resource注解标识在应用程序需要使用的资源上。
该注解可以标注在组件类、组件类的属性或方法上。
标注在属性或方法上:当组件初始化时,容器会注入需要的资源实例到应用组件中。
标注在组件类上:表明资源可以在运行时查找到。
虽然该注解没有标识为Inherited,
但是,部署工具需要检测组件类中所有的父类(超类)是否使用该注解。
所有注解实例都指定了应用程序组件所需要的资源。
位置:javax.annotation.Resource
在这里插入图片描述

2.1 属性

2.1.1 name

资源的JNDI名称。
属性注解默认为属性名称。
方法注解默认为JavaBean属性名名称。
类注解没有默认值,必须指定名称,
我理解是在构建Bean时必须指定id,使用@Resource才可完成注入。
在这里插入图片描述

2.1.2 lookup

引用指向的资源名。通过lookup可以使用全局JNDI名称连接到匹配的资源。
在这里插入图片描述

2.1.3 type

Java资源类型。
属性注解,默认类型为属性类型。
方法注解,默认为JavaBean属性。
类注解,没有默认值,必须指定,
我理解是构建Bean是必须填写class类型(全限定名)。

在这里插入图片描述

2.1.4 AuthenticationType

资源认证类型枚举,两种:CONTAINER和APPLICATION。
当前资源使用的认证类型,默认为CONTAINER,必须使用支持的两种功能类型,不能使用其他类型。
在这里插入图片描述

在这里插入图片描述

2.1.5 shareable

标识位。表明当前资源是否可以在组件之间共享,默认true,可以共享。
在这里插入图片描述

2.1.6 mappedName

资源映射的具体名称。该资源名称(由name元素定义或者默认)是使用本地资源的应用组件名称(JNDI中的名称:java:comp/env namespace)。许多应用服务提供将本地已知资源名称映射到应用服务的功能。映射的名称通常是全局JNDI名称,也可能是其他形式。
在这里插入图片描述

2.1.7 description

资源描述,帮助使用者理解并使用资源。

在这里插入图片描述

2.2 实践

2.2.1 引入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.3.6</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.6</version>
</dependency>

2.2.2 注入Bean

  • User
    用于构建Bean。
    使用@Resource注入(Inject)该类实例化的Bean到需要的地方。
package com.monkey.java_study.annotation.raw_resource;

/**
 * User类.
 *
 * @author xindaqi
 * @date 2022-06-16 10:49
 */
public class User {

    private String uid;

    private String uname;

    public void setUid(String uid) {
        this.uid = uid;
    }

    public String getUid() {
        return uid;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUname() {
        return uname;
    }

    @Override
    public String toString() {
        return "User{" +
                "uid='" + uid + '\'' +
                ", uname='" + uname + '\'' +
                '}';
    }
}
  • OrganizationByResource
    通过@Resource注入(Inject)User实例化的Bean,
    分别通过name和type注入(Inject),即byName,byType。
package com.monkey.java_study.annotation.raw_resource;

import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;

/**
 * 通过@Resource注入Bean.
 *
 * @author xindaqi
 * @date 2022-06-16 11:27
 */
public class OrganizationByResource {

    private String uid;

    private String uname;

    // 通过name注入Bean(byName)
    @Resource(name = "user1")
    private User user1;

    // 通过type注入Bean(byType)
    @Resource
    private User user2;


    public void setUid(String uid) {
        this.uid = uid;
    }

    public String getUid() {
        return uid;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUname() {
        return uname;
    }

    public void setUser1(User user1) {
        this.user1 = user1;
    }

    public User getUser1() {
        return user1;
    }

    public void setUser2(User user2) {
        this.user2 = user2;
    }

    public User getUser2() {
        return user2;
    }


    @Override
    public String toString() {
        return "OrganizationByResource{" +
                "uid='" + uid + '\'' +
                ", uname='" + uname + '\'' +
                ", user1=" + user1 +
                ", user2=" + user2 +
                '}';
    }
}

2.2.3 构建Bean

分别映射类:User和Organization构建Bean实例。
在resources文件夹下文件配置文件:resource-bean-injection.xml。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- To activate the '@Resource' annotation in the spring framework -->
    <!-- 在Spring framework中启用@Resource注解 -->
    <context:annotation-config />
    <!-- 构建User Bean实例 -->
    <bean id="user1" class="com.monkey.java_study.annotation.raw_resource.User">
        <property name="uid" value="0x0001" />
        <property name="uname" value="xiaohua" />
    </bean>
    <!-- 构建OrganizationByResource Bean实例 -->
    <bean id="organizationByResource" class="com.monkey.java_study.annotation.raw_resource.OrganizationByResource">
        <property name="uid" value="0x0001" />
    </bean>
</beans>

2.2.4 测试

package com.monkey.java_study.annotation.raw_resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 测试@Resource和@Autowired Bean注入.
 *
 * @author xindaqi
 * @date 2022-06-16 11:09
 */
public class ResourceTest {

    private static final Logger logger = LoggerFactory.getLogger(ResourceTest.class);

    public static void main(String[] args) {
        // 通过XML配置文件构建Bean
        ApplicationContext beanFromResource = new ClassPathXmlApplicationContext("bean-test.xml");
        // Bean查询:Lookup方式
        String beanNameByResource = "organizationByResource";
        OrganizationByResource resourceClazz = beanFromResource.getBean(beanNameByResource, OrganizationByResource.class);
        logger.info(">>>>>>>>>Resource injection:{}", resourceClazz);
    }
}

测试结果如下图所示。
有结果可知,
uid直接映射到对应的值;
user1通过@Resource(name=“user1”)指定名称注入到user1;
user1通过@Resource User类型注入到user2。
在这里插入图片描述

3 Autowired

标识构造体、属性、setter方法或者配置方法可由Spring依赖注入工具进行自动装配。
@Autowired默认属性required为true,表明Bean注入时,该Bean必须存在。
@Autowired标注在构造体上,该构造体的Bean必须是唯一确定的,
默认通过Bean名称注入,通过Bean无法注入时,使用Bean类型注入,
但是通过类型注入需要确定Bean,如果存在多个同类型的Bean,
需要为确定的Bean添加primary/default标识。

位置:org.springframework.beans.factory.annotation.Autowired
在这里插入图片描述

3.1 验证@Autowired通过Bean名称注入

通过上面的基础,这里直接构建Bean,
证明@Autowired会通过Bean名称注入。
这里构建两个相同类型(User)的Bean,
名称分别为user1和user2,
如果@Autowired通过名称注入Bean,
会直接查询user1名称的Bean,
查到后,注入给需要的地方,
则不会出现异常。

3.1.1 构建Bean

在resources文件夹下文件配置文件:autowired-bean-injection-name.xml。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- To activate the '@Resource' annotation in the spring framework -->
    <!-- 在Spring framework中启用@Resource注解 -->
    <context:annotation-config />

    <!-- 构建User Bean实例:Bean id为user1 -->
    <bean id="user1" class="com.monkey.java_study.annotation.raw_resource.User">
        <property name="uid" value="0x0001" />
        <property name="uname" value="xiaohua" />
    </bean>
    <!-- 构建User Bean实例:Bean id为user2,
    由于@Autowired先通过name注入,所以,不会冲突,
    不需要添加primary属性 -->
    <bean id="user2" class="com.monkey.java_study.annotation.raw_resource.User">
        <property name="uid" value="0x0002" />
        <property name="uname" value="xiaoli" />
    </bean>
    <!-- 构建OrganizationByAutowired Bean实例 -->
    <bean id="organizationByAutowired" class="com.monkey.java_study.annotation.raw_resource.OrganizationByAutowired">
        <property name="uid" value="0x0003" />
        <property name="uname" value="xiaolan" />
    </bean>
</beans>

3.1.2 注入Bean

package com.monkey.java_study.annotation.raw_resource;

import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;

/**
 * 通过@Autowired注入Bean.
 *
 * @author xindaqi
 * @date 2022-06-16 11:27
 */
public class OrganizationByAutowired {

    private String uid;

    private String uname;

    // 通过type注入Bean(byType)
    @Autowired
    private User user1;


    public void setUid(String uid) {
        this.uid = uid;
    }

    public String getUid() {
        return uid;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUname() {
        return uname;
    }

    public void setUser1(User user1) {
        this.user1 = user1;
    }

    public User getUser1() {
        return user1;
    }

    @Override
    public String toString() {
        return "OrganizationByResource{" +
                "uid='" + uid + '\'' +
                ", uname='" + uname + '\'' +
                ", user1=" + user1 +
                '}';
    }
}

3.1.3 测试

package com.monkey.java_study.annotation.raw_resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 测试@Resource和@Autowired Bean注入.
 *
 * @author xindaqi
 * @date 2022-06-16 11:09
 */
public class AutowiredTest {

    private static final Logger logger = LoggerFactory.getLogger(AutowiredTest.class);

    private static void autowiredInjectTest(String beanConfig, String beanName) {
        // 通过XML配置文件构建Bean
        ApplicationContext beanFromResource = new ClassPathXmlApplicationContext(beanConfig);
        // Bean查询:Lookup方式
        OrganizationByAutowired autowiredClazz = beanFromResource.getBean(beanName, OrganizationByAutowired.class);
        logger.info(">>>>>>>>>Autowired injection:{}", autowiredClazz);

    }

    public static void main(String[] args) {
        // 按照Bean名称(byName)注入
        String beanConfig1 = "autowired-bean-injection-name.xml";
        // 构建的Bean
        String beanNameByAutowired = "organizationByAutowired";
        autowiredInjectTest(beanConfig1, beanNameByAutowired);
    }
}

user1会注入名称为user1的Bean。
在这里插入图片描述

3.2 验证@Autowired通过Bean类型注入

上面验证了@Autowired首先会通过Bean名称注入,
接下来,验证@Autowired通过Bean类型注入,
当创建同类型(User)的Bean:userA和userB,
此时,@Autowired标识的类名称为user1,
这时无法查到user1这个Bean,所以,按照Bean类型注入,
但是,类型相同,@Autowired无法决定注入哪一个,
此时会报错。
要解决此问题,需要为某个Bean添加primary=true,
保证,按照类型注入时,确定一个Bean。

3.2.1 构建Bean

在resources文件夹下文件配置文件:autowired-bean-injection-type.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 在Spring framework中启用@Resource注解 -->
    <context:annotation-config />

    <!-- 构建User Bean实例:Bean id为user1 -->
    <bean id="userA" class="com.monkey.java_study.annotation.raw_resource.User">
        <property name="uid" value="0x0001" />
        <property name="uname" value="xiaohua" />
    </bean>
    <!-- 构建User Bean实例:Bean id为userB,
    由于@Autowired先通过name注入,
    当无法通过Bean名称找到Bean时,
    再通过Bean类型找Bean
    所以,这里相同User类型的Bean会冲突,
    需要添加primary属性 -->
    <bean id="userB" class="com.monkey.java_study.annotation.raw_resource.User">
        <property name="uid" value="0x0002" />
        <property name="uname" value="xiaoli" />
    </bean>
    <!-- 构建OrganizationByAutowired Bean实例 -->
    <bean id="organizationByAutowired" class="com.monkey.java_study.annotation.raw_resource.OrganizationByAutowired">
        <property name="uid" value="0x0003" />
        <property name="uname" value="xiaolan" />
    </bean>
</beans>

3.2.2 注入Bean

通过@Autowired注入类型为User的Bean,
此时,通过xml创建了两个类型为User的Bean,名称分别为userA和userB,
@Autowired无法通过Bean名称注入,
只能通过Bean类型注入,但是,@Autowired不知哪个Bean是主要的Bean,
导致冲突。

package com.monkey.java_study.annotation.raw_resource;

import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;

/**
 * 通过@Autowired注入Bean.
 *
 * @author xindaqi
 * @date 2022-06-16 11:27
 */
public class OrganizationByAutowired {

    private String uid;

    private String uname;

    // 通过type注入Bean(byType)
    @Autowired
    private User user1;


    public void setUid(String uid) {
        this.uid = uid;
    }

    public String getUid() {
        return uid;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUname() {
        return uname;
    }

    public void setUser1(User user1) {
        this.user1 = user1;
    }

    public User getUser1() {
        return user1;
    }

    @Override
    public String toString() {
        return "OrganizationByResource{" +
                "uid='" + uid + '\'' +
                ", uname='" + uname + '\'' +
                ", user1=" + user1 +
                '}';
    }
}

3.2.3 测试

package com.monkey.java_study.annotation.raw_resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 测试@Resource和@Autowired Bean注入.
 *
 * @author xindaqi
 * @date 2022-06-16 11:09
 */
public class AutowiredTest {

    private static final Logger logger = LoggerFactory.getLogger(AutowiredTest.class);

    private static void autowiredInjectTest(String beanConfig, String beanName) {
        // 通过XML配置文件构建Bean
        ApplicationContext beanFromResource = new ClassPathXmlApplicationContext(beanConfig);
        // Bean查询:Lookup方式
        OrganizationByAutowired autowiredClazz = beanFromResource.getBean(beanName, OrganizationByAutowired.class);
        logger.info(">>>>>>>>>Autowired injection:{}", autowiredClazz);

    }

    public static void main(String[] args) {
        // 构建的Bean
        String beanNameByAutowired = "organizationByAutowired";
        // 按照Bean类型(byType)注入
        String beanConfig2 = "autowired-bean-injection-type.xml";
        autowiredInjectTest(beanConfig2, beanNameByAutowired);
    }
}

异常结果如下图所示,
由图可知,没有匹配的Bean,因为需要一个Bean,但是发现两个,
说明,多个同类型的Bean使用@Autowired注入时,无法正常注入。
在这里插入图片描述

3.3.4 添加primary属性

由@Autowired注释可知,注入多个同类型的Bean时,
需要添加primary或者default,让@Autowired确定注入哪个Bean,
通过xml文件的primary属性为true,配置如下。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- To activate the '@Resource' annotation in the spring framework -->
    <!-- 在Spring framework中启用@Resource注解 -->
    <context:annotation-config />

    <!-- 构建User Bean实例:Bean id为user1 -->
    <bean id="userA" class="com.monkey.java_study.annotation.raw_resource.User">
        <property name="uid" value="0x0001" />
        <property name="uname" value="xiaohua" />
    </bean>
    <!-- 构建User Bean实例:Bean id为userB,
    由于@Autowired先通过name注入,
    当无法通过Bean名称找到Bean时,
    再通过Bean类型找Bean
    所以,这里相同User类型的Bean会冲突,
    需要添加primary属性 -->
    <bean id="userB" class="com.monkey.java_study.annotation.raw_resource.User" primary="true">
        <property name="uid" value="0x0002" />
        <property name="uname" value="xiaoli" />
    </bean>
    <!-- 构建OrganizationByAutowired Bean实例 -->
    <bean id="organizationByAutowired" class="com.monkey.java_study.annotation.raw_resource.OrganizationByAutowired">
        <property name="uid" value="0x0003" />
        <property name="uname" value="xiaolan" />
    </bean>
</beans>

注入结果如下图所示,
由结果可知,@Autowired注入了使用primary=true的Bean。
在这里插入图片描述

4 小结

核心:
(1)@Resource和@Autowired均是默认通过Bean名称(byName)注入;
(2)@Resource和@Autowired均是匹配不到Bean名称时才会自动切换到匹配Bean类型(byType);
(3)@Resource和@Autowired注入多个同类型的Bean时需要为某个Bean添加primary=true或者@Primary保证成功注入;
(4)不同点:@Resource是Java原生的通用注解,@Autowired是Spring基于JSR-330依赖注入规范的另一种实现,
并添加了注入Bean时,判断Bean是否必须存在属性。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天然玩家

坚持才能做到极致

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值