【Spring】基于注解方式存取JavaBean:Spring有几种注入方式?有什么区别?

前言

 Hello,我是小黄。众所周知,Spring是一个开源的Java应用程序框架,其中包括许多通过注解实现依赖注入的功能。Spring提供了多种注入方式,可以满足不同的需求和场景。常见的注入方式包括构造函数注入、Setter方法注入和属性注入。不同的注入方式有不同的适用场景和优缺点。选择正确的注入方式可以提高应用程序的可维护性和性能。 本文以Spring的常见注解以及不同注入方式的优缺点为切入点进行深入讨论。 话不多说,正文开始~
表情1


文章目录

  • 前言
  • *操作准备
  • 1 什么是注解?
  • 2 基于注解存储 Bean 对象
    • 2.1 类注解方式
      • 2.1.1 如何存储 Bean?
      • 2.1.2 如何读取 Bean?
      • 2.1.3 读取时的常见问题及 Bean 命名规则
    • 2.2 方法注解方式
  • 3 基于注解获取 Bean 对象(对象装配)
    • 3.1 属性注入
    • 3.2 Setter 注入
    • 3.3 构造方法注入
    • 3.4 三种不同注入方式的优劣分析
    • 3.5 @Resource 注解
    • 3.6 常见错误:非唯一的 Bean 对象
  • 写在最后


*操作准备

 要想将对象成功存储到 Spring 中,就需要配置存储对象的扫描包路径,只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到 Spring 中。这里采用 xml 文件的形式进行配置(有兴趣的小伙伴可以查阅笔者之前的Spring文章,采用配置类的形式进行纯注解的开发配置):

spring-config.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:content="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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 配置扫描路径 -->
    <content:component-scan base-package="com.hxh"/>
</beans>

 另外,Spring 操作需要的相关依赖,这里采用 Maven 的方式实现,相关依赖的配置如下:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>Spring_demo03</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- Spring相关的配置 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
    </dependencies>
</project>

1 什么是注解?

注解是代码中特殊的标记,格式如下:

@注解名称(属性名称=属性值,属性名称=属性值......)

注解可以作用在类、方法、属性上。Spring针对Bean管理中创建对象提供注解,在 Spring 中,注解可以分为两大类:

  1. 类注解: 以下四个注解都可以用来创建bean实例,只是为了便于开发者清晰区分当前层。@Configuration@Service@Controller@Repository@Component
  2. 方法注解: @Bean

为什么需要这么多个类注解?

对于其余四个类注解,它们本身就是属于 @Component 的“⼦类”。五个类注解的功能是一致的,命名上将其区分,是为了让程序员看到类注解之后,就能直接了解当前类的用途:

  • @Controller:表示的是业务逻辑层;
  • @Servie:服务层;
  • @Repository:持久层;
  • @Configuration:配置层。

程序的工程分层


2 基于注解存储 Bean 对象

2.1 类注解方式

2.1.1 如何存储 Bean?

@Configuration 配置存储

@Configuration   // 将对象存储到 Spring 中
public class UserConfiguration {
    public void sayHello() {
        System.out.println("Hello, I'm userConfiguration !");
    }
}

@Service 服务存储

@Service  // 将对象存储到 Spring 中
public class UserService {
    public void sayHello() {
        System.out.println("Hello, I'm userService!");
    }
}

@Controller 控制器存储

@Controller  // 将对象存储到 Spring 中
public class UserController {
    public void sayHello() {
        System.out.println("Hello, I'm userController!");
    }
}

@Repository 仓库存储

@Repository  // 将对象存储到 Spring 中
public class UserRepository {
    public void sayHello() {
        System.out.println("Hello, I'm userRepository!");
    }
}

@Component 组件存储

@Component  // 将对象存储到 Spring 中
public class UserComponent {
    public void sayHello() {
        System.out.println("Hello, I'm userComponent!");
    }
}

2.1.2 如何读取 Bean?

这里以读取 StudentController 对象为例,由于我们采用的是标准的大驼峰命名法,因此,读取的时候将首字母小写, 就可以读取到相应的 JavaBean 了。
读取JavaBean

2.1.3 读取时的常见问题及 Bean 命名规则

倘若我们不按照标准的大驼峰命名法对类进行命名读取Bean时会有区别吗?
首先我们尝试创建类 IService,并通过首字母小写的方式读取 Bean:无法正常读取 Bean!
非标准命名格式下程序运行情况
为了探究异常发生的原因,我们需要深入 Spring 的源码,看看 Bean 的命名规则:AnnotationBeanNameGenerator.class
bean规则1
bean 对象的命名规则使用方法是 JDK Introspector 中的 decapitalize ⽅法,其源码如下:

  public static String decapitalize(String name) {
      if (name == null || name.length() == 0) {
          return name;
      }
      if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                      Character.isUpperCase(name.charAt(0))){
          return name;
      }
      char chars[] = name.toCharArray();
      chars[0] = Character.toLowerCase(chars[0]);
      return new String(chars);
  }

通过阅读源码,可以看出 bean 的命名规则: 如果第⼀个字⺟和第⼆个字⺟都为⼤写的情况,是把 bean 的⾸字⺟也⼤写存储了~ 否则,就将首字母小写~ 这通常意味着将第一个字符从大写转换为小写,但在(不寻常的)特殊情况下,当有多个字符并且第一个和第二个字符都是大写时,我们不处理它。

2.2 方法注解方式

类注解用于标记类为Spring Bean,使用@ComponentScan扫描时,每个注解只会创建一个Bean对象,因此在同一个ApplicationContext容器中,获得的对象是同一个。

那么如果想要获取不同的对象怎么办呢? 下面我们就来聊一聊方法注解~

顾名思义,⽅法注解就是是放到某个⽅法上的,以下是简单的代码实现:需要注意的是 ⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中

@Controller
public class StudentBeans {

    @Bean(name = {"s1", "s2"})  // 可以通过设置 name 属性给 Bean 对象进⾏重命名操作
    public Student student() {
        Student student = new Student();
        student.setId(1);
        student.setName("黄小黄");
        student.setAge(17);
        return student;
    }
    
    @Bean
    public Student student02() {
        Student student = new Student();
        student.setId(2);
        student.setName("蒲小七");
        student.setAge(16);
        return student;
    }
}

读取 Bean 的代码如下:

    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        Student s1 = context.getBean("s1", Student.class);
        Student s2 = context.getBean("s2", Student.class);
        // 打印 s1 和 s2 信息
        System.out.println(s1);
        System.out.println(s2);
        System.out.println("s1 == s2 ? " + (s1 == s2));
        // 尝试获取 student 不同对象
        Student student02 = context.getBean("student02", Student.class);
        System.out.println(student02);
        System.out.println("s1 == student2 ? " + (s1 == student02));
    }

实现结果


3 基于注解获取 Bean 对象(对象装配)

在Spring中,获取 Bean 对象即把对象取出来放到某个类中,有三种方式来实现对象装配:

  1. 属性注入
  2. Setter 注入
  3. 构造方法注入

不同的注入方式有不同的适用场景和优缺点。以下案例则以将 Service 类注⼊到 Controller 类中为切入点,帮助大家了解三种注入方式的优缺点和区别~

3.1 属性注入

属性注入主要通过 @Autowired 实现,相关代码如下:

UserService

@Service
public class UserService {

    public void sayHello() {
        System.out.println("Hello, I'm userService!");
    }
}

UserController

@Controller
public class UserController {

    // 1. 使用属性注入的方式  不能注入不可变对象且违背单一设计原则
    @Autowired
    private UserService userService;

	public void sayHello() {
        // 调用 service 的方法
        userService.sayHello();
    }
}

此时,通过如下的代码,即可通过 userController 的 sayHello() 方法调用 service 中的 sayHello() 方法。其余两种装配实现方式,该部分代码等同,Service 也等同,就不再赘述了。

public class ApplicationTest01 {

    private static final String URL = "spring-config.xml";

    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext(URL);
        UserController userController = context.getBean("userController", UserController.class);
        userController.sayHello();
    }
}

在这里插入图片描述

3.2 Setter 注入

即,在 setter 方法前加上 @Autowired 注解。

核心代码

    // 2. set 注入 不能注入不可变对象但是更符合单一设计原则
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

3.3 构造方法注入

构造⽅法注⼊是在类的构造⽅法中实现注⼊。在类中添加带有参数的构造方法,Spring会根据参数类型和名称,在容器中找到相应的Bean进行注入。特别地,如果只有⼀个构造⽅法,那么 @Autowired 注解可以省略~

核心代码

    // 3. 构造方法注入
    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

3.4 三种不同注入方式的优劣分析

Spring的三种注入方式分别是属性注入、Setter 注入与构造方法注入,其区别如下:

  1. 属性注入:在类的属性上添加@Autowired注解,Spring会自动在容器中查找符合该属性类型的Bean进行注入。优点是简洁,使⽤⽅便;缺点是只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)。同时,属性注入的方式不可注入不可变对象,且违背单一设计原则:
    在这里插入图片描述

  2. Setter注入:在类中添加setter方法,Spring会根据方法名称和参数类型,在容器中找到相应的Bean进行注入。主要特点是注入后对象的属性是可变的,可以随时修改。虽然在一定程度上相对符合了单一设计原则,但依然不可注入不可变对象:
    在这里插入图片描述

  3. 构造器注入:在类中添加带有参数的构造方法,Spring会根据参数类型和名称,在容器中找到相应的Bean进行注入。主要特点是注入后对象的属性是不可变的,一旦注入完成后就不能再改变,是 Spring 推荐的注⼊⽅式。解决了以上两种方式不可注入不可变对象的问题:
    在这里插入图片描述

📖 补充:单一设计原则

单一职责原则(Single Responsibility Principle)是一种设计原则,它建议一个类或模块只应该负责一项职责。这个原则通常被认为是最基本的设计原则之一。
使用单一职责原则可以带来如下好处:

  1. 降低模块的复杂度,使其更易于理解和修改。
  2. 提高模块的可复用性和可测试性,因为它们只关注一项职责。
  3. 降低系统的耦合性,因为每个模块都是独立的,不会依赖于其他模块。
  4. 更好地支持面向对象设计的原则,例如开放封闭原则。

3.5 @Resource 注解

在进⾏类注⼊时,除了可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊:

@Resource
private UserService userService;

@Resource 与 @Autowired 的区别:

  • @Autowired 来自于 Spring,而 @Resource 来自 JDK,后者相较前者支持更多的参数设置;
  • @Resource 只能用于 Setter 注入和 属性注入。

3.6 常见错误:非唯一的 Bean 对象

当出现以下多个 Bean,返回同⼀对象类型时程序会报错,参考代码如下:

@Component
public class BookBeans {

    @Bean
    public Book book01() {
        Book book = new Book();
        book.setName("计算机网络");
        return book;
    }

    @Bean
    public Book book02() {
        Book book = new Book();
        book.setName("计算机操作系统");
        return book;
    }
}

Controller

@Controller
public class BookController {

    @Autowired
    private Book book;

    public Book getBook() {
        return book;
    }
}

在这里插入图片描述

这是由于 Bean 对象不是唯一的导致的~, 解决方案如下:

  1. 使用 @Resource(name = "xxx") 来指定~
    在这里插入图片描述

  2. 使用 @Qualifier 注解定义名称~
    在这里插入图片描述


写在最后

 本文就如何将对象存储到 Spring 中分类注解与方法注解进行了阐述,同时,对 Bean 对象的命名规则进行了说明。从 Spring 中获取对象有三种方式,不同的注入方式有不同的适用场景,或更加简便,或有独有的特性。总的来说,Spring 的出现的确为我们开发提高了效率,但我们需要明确的是,任何一个工具的出现都要求我们在熟悉的基础上使用,才能事半功倍。
本文被 JavaEE编程之路 收录点击订阅专栏 , 持续更新中。
 以上便是本文的全部内容啦!创作不易,如果你有任何问题,欢迎私信,感谢您的支持!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/33964.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于卷积神经网络的狗猫数据集分类实验

目录 一、环境配置1、anaconda安装2、配置TensorFlow、Keras 二、数据集分类1、分类源码2、训练流程 三、模型调整1、图像增强2、网络模型添加dropout层 四、使用VGG19优化提高猫狗图像分类五、总结六、参考资料 一、环境配置 1、anaconda安装 下载链接&#xff1a;anaconda …

Appium安装部署

目录 一、检查Java环境 二、安装android SDK 一、检查Java环境 Android SDK依赖ava环境&#xff0c;因此需要先安装jdk。在CMD中输入java -version 出现下图的结果&#xff0c;说明当前环境已安装jdk 如果提示java命令无效&#xff0c;请安装后进行下一步。 二、安装androi…

iOS App的上架和版本更新流程

一、前言&#xff1a; 作为一名iOSDeveloper&#xff0c;把开发出来的App上传到App Store是必要的。下面就来详细讲解一下具体流程步骤。 二、准备&#xff1a; 一个已付费的开发者账号&#xff08;账号类型分为个人&#xff08;Individual&#xff09;、公司&#xff08;Com…

单片机-串口通信

1.串口向电脑发送数据 1.配置串口 T1定时器&#xff0c;方式二8位重装 void UartInit(void) //4800bps11.0592MHz {PCON & 0x7F; //波特率不倍速SCON 0x50; //8位数据,可变波特率TMOD & 0x0F; //清除定时器1模式位TMOD | 0x20; //设定定时器1为8位自动重装方式…

【论文笔记】FASTER SEGMENT ANYTHING:TOWARDS LIGHTWEIGHT SAM FOR MOBILE APPLICATIONS

前脚fast SAM刚发完&#xff0c;后脚mobile SAM就发了 &#xff0c;之前的论文笔记中我一直就认为fast SAM其实应该算是yolo的扩展工作&#xff0c;和原生的SAM架构相去甚远&#xff0c;而且在简介上直接就对&#xff08;gong&#xff09;比&#xff08;ji&#xff09;了FastSA…

ElasticSearch 8.0+ 版本Windows系统启动

下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases/winlogbeat-8-8-1 解压\elasticsearch\elasticsearch-8.5.1 进入bin目录&#xff0c;启动elasticsearch.bat 问题1&#xff1a; warning: ignoring JAVA_HOMED:\jdk1.8.0_271; using bundled JDK J…

【FPGA】Verilog:时序电路设计 | 自循环移位寄存器 | 环形计数 | 扭环计数 | 约翰逊计数器

前言&#xff1a;本章内容主要是演示Vivado下利用Verilog语言进行电路设计、仿真、综合和下载 示例&#xff1a;计数器 ​​ 功能特性&#xff1a; 采用 Xilinx Artix-7 XC7A35T芯片 配置方式&#xff1a;USB-JTAG/SPI Flash 高达100MHz 的内部时钟速度 存储器&#xff1a;2Mb…

简单认识Tomcat的部署和优化

文章目录 一、简单认识Tomcat1、简介2、构成3、Tomcat 功能组件结构4、Tomcat 请求过程&#xff1a; 二、Tomcat部署1.关闭防火墙&#xff0c;将安装 Tomcat 所需软件包传到/opt目录下2.安装JDK3.设置JDK环境变量4.测试java环境5.安装Tomcat6.启动和关闭Tomcat7.优化 tomcat 启…

【每日一题】2. 两数相加

【每日一题】2. 两数相加 2. 两数相加题目描述解题思路 2. 两数相加 题目描述 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一…

会网络爬虫能干什么?

网络爬虫是一种自动化程序&#xff0c;用于浏览互联网并从网页中获取数据。它可以执行以下任务&#xff1a; 数据采集&#xff1a;网络爬虫可以访问网站&#xff0c;并从中提取所需的数据&#xff0c;例如新闻文章、产品信息、用户评论等。这些数据可以用于各种目的&#xff0…

MySQL复合查询

目录 一、多表查询 二、自连接 三、子查询 3.1 单行子查询 3.2 多行子查询 3.3 多列子查询 3.4 在from子句中使用子查询 四、合并查询 一、多表查询 实际开发中往往需要将多张表关联起来进行查询&#xff0c;即多表查询在进行多表查询时&#xff0c;只需将多张表的表名…

python机器学习——机器学习相关概念 特征工程

目录 机器学习特征工程1.特征抽取2.特征处理2.1 归一化&#xff1a;传统精确小数据2.2 标准化&#xff1a;大多数情况 3.数据降维3.1特征选择3.2主成分分析PCA 案例&#xff1a;超市订单分析 机器学习 监督学习&#xff1a;输入数据有特征有标签&#xff0c;即有标准答案 分类&…

5.4.1 虚拟专用网VPN

5.4.1 虚拟专用网VPN 我们已经学习了因特网的路由协议&#xff08;5.3.1 因特网的路由协议&#xff08;一&#xff09;、5.3.2 因特网的路由协议&#xff08;二&#xff09;基于距离向量算法的RIP协议、5.3.3 因特网的路由协议&#xff08;三&#xff09;OSPF协议、5.3.4 因特…

无锡斑目信息技术有限公司与无锡漫途科技有限公司签署战略伙伴合作协议!

2023年6月21日无锡斑目信息技术有限公司与无锡漫途科技有限公司签署战略伙伴合作协议。双方将在数字工厂、智慧城市等领域凭借各自的优势进行全方面的合作。 漫途传感科技总经理田吉成、无锡艾森汇智科技总经理钱小伟、无锡数字城市建设发展工业互联网事业部部长王威共同参加签…

kafka安装(包含Zookeeper 安装)

kafka 依赖于 Zookeeper 1. Zookeeper 本地模式安装 修改配置文件 解压后的目录中的 conf 路径下&#xff0c;将文件 zoo_sample.cfg 修改为 zoo.cfg。 mv zoo_sample.cfg zoo.cfg打开 zoo.cfg 文件&#xff0c;修改 dataDir 路径。 dataDir 路径 默认在 /tmp 下&#xff0…

尚硅谷微信小程序开发 仿网易云音乐App 小程序 后端接口服务器搭建

小程序学习 尚硅谷微信小程序开发 项目网易云小程序学习地址&#xff1a; 01-尚硅谷-小程序-课程介绍_哔哩哔哩_bilibili 视频相关的教程文档与笔记分享 链接&#xff1a;https://pan.baidu.com/s/1aq7ks8B3fJ1Wahge17YYUw?pwd7oqm 提取码&#xff1a;7oqm 配套服务器 老师…

Redis的高可用与持久化

目录 一、Redis 高可用1. 持久化2. 主从复制3. 哨兵4. 集群(cluster) 二、Redis 持久化方式1. 持久化的功能2. 持久化的方式 三、RDB 持久化1. 触发条件2.执行流程3. 启动时加载 四、AOF持久化1.开启 AOF2. 执行流程2.1 命令追加2.2 文件写入&#xff08;write&#xff09;和文…

路由基础静态路由

路由基础&静态路由 一、路由器基本原理1.1、路由器基本概述1.2、LAN和广播域1.3、路由选路1.3.1、路由器转发数据包1.3.2、IP路由表1.3.3、建立路由表1.3.4、最长匹配原则1.3.5、路由优先级1.3.6、路由度量1.3.7、等价路由 1.4、总结 二、静态路由基础2.1、静态路由配置2.2…

《计算机系统与网络安全》 第九章 访问控制技术

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

自动刷新工具--可以自动编辑安居客房源信息

本工具可以自动刷新安居客的房源信息&#xff0c;不是爬虫&#xff0c;就是一款解放劳动力的RPA工具 使用方法&#xff1a; 1. 首先输入要自动刷新的房源id 2.点击 开始执行 如果需要免密登陆&#xff0c;需要在个人中心填上anjuke的账密 定时执行 声明&#xff1a;此工具只是…