Spring中如何用注解方式存取JavaBean?有几种注入方式?

 

  • 博主简介:想进大厂的打工人
  • 博主主页:@xyk:
  • 所属专栏: JavaEE进阶 

本篇文章将讲解如何在spring中使用注解的方式来存取Bean对象,spring提供了多种注入对象的方式,常见的注入方式包括 构造函数注入,Setter 方法注入和属性注入,不同的注入方式都有优缺点,下面我们来讲解一下~~


目录

文章目录

一、使用注解方式的前提

1.1 前置工作

1.2 什么是注解?

二、spring基于注解存储Bean对象

2.1 类注解方式

2.2 如何读取Bean对象?

2.3 读取Bean对象时的命名规则

2.4 方法注解方式

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

3.1 属性注入

3.2 Setter方法注入

3.3 构造方法注入

3.4 三种注入方式的优缺点:

缺点3:设计原则问题

优点3:完全初始化

优点4:通用性更好

四、@Resource另⼀种注⼊关键字

4.1 @Autowired 和 @Resource 的区别

4.2 同⼀类型多个 Bean 报错处理


一、使用注解方式的前提

1.1 前置工作

在我们使用注解方式来存储 Bean对象 的前提,我们要先将配置文件写好,因为在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.demo"/>
    
</beans>

其中 base-package 中的路径对应你项目中的包名即可 

也就是说,即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到 Spring 中的

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-demo1</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>


    <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.2 什么是注解?

注解就是代码中的特殊标记,无需在xml中配置繁琐的Bean对象代码,注解可以作用在类、方法、属性上。spring 针对 Bean对象的管理提供了注解方式,在 spring中,注解可以分为两大类:

  1. 类注解: 以下四个注解都可以用来创建bean实例,只是为了便于开发者清晰区分当前层。
  2. 方法注解:@Bean
  • @Controller:表示的是业务逻辑层;
  • @Service:服务层;
  • @Repository:持久层;
  • @Configuration:配置层;
  • @Component:组件层

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

这是因为让程序猿看到类注解之后,就能直接了解当前类的用途,也是为了开发更加方便~

 在我们查看上面类注解的源码中,可以发现:

 其实这些注解⾥⾯都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“⼦类”

二、spring基于注解存储Bean对象

2.1 类注解方式

@Controller 控制器存储

@Controller
public class BController {
    public String sayHi() {
        return "Hi,Controler.";
    }
}

@Service 服务存储

@Service
public class StudentService {
    public void sayHi(){
        System.out.println("Hi,Service");
    }
}

@Repository 仓库存储

@Repository
public class UserRepository {
    public String sayHi(){
        return "Hi,@Repository";
    }
}

@Component 组件存储

@Component
public class UserComponent {
    public String sayHi() {
        return "Hi,@Component.";
    }
}

@Configuration 配置存储

@Configuration
public class UserConfiguration {
    public String sayHi(){
        return "Hi,@Configuration";
    }
}

2.2 如何读取Bean对象?

这里以读取 BController 对象为例, 由于前两个首字母都是大写的,所以我们使用原类名就可以读取到相应的 JavaBean 了。

那如果我们的首字母不大写,或者只有一个字母大写会发生什么?

2.3 读取Bean对象时的命名规则

这个时候,我们就要查询 Spring 关于 bean 存储时⽣成的命名规则了。我们可以在 Idea 中使⽤搜索关键字“beanName”可以看到以下内容。

 它使⽤的是 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);
    }
  1. 如果第⼀个字⺟和第⼆个字⺟都为⼤写的情况,是把 bean 的⾸字⺟也⼤写存储了
  2. 其他的命名规则都是首字母小写即可(默认)

如果不遵守这个规则的话,可是会报错的,如下:找不到此Bean对象

2.4 方法注解方式

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

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

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

@Component
public class StudentBeans {

    //    @Bean(name = {"s1", "s2"})
    @Bean
    public Student student1() {
        // 伪代码,构建对象
        Student stu = new Student();
        stu.setId(1);
        stu.setName("张三");
        stu.setAge(18);
        return stu;
    }

    @Bean
    public Student student2() {
        // 伪代码,构建对象
        Student stu = new Student();
        stu.setId(2);
        stu.setName("李四");
        stu.setAge(20);
        return stu;
    }

}
public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        Student student = context.getBean("student1",Student.class);
        System.out.println(student);

        Student student2 = context.getBean("student2",Student.class);
        System.out.println(student2);
    }
}

@Bean默认情况下,Bean name = 方法名

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

获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊。

对象装配(对象注⼊)的实现⽅法以下 3 种:

  1. 属性注⼊
  2. 构造⽅法注⼊
  3. Setter 注⼊

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

3.1 属性注入

Controller

@Controller
public class BController {
    
    @Autowired
    private StudentService studentService;
    
    public void sayHi() {
        studentService.sayHi();
    }
}

Service 

@Service
public class StudentService {
    public void sayHi(){
        System.out.println("Hi,Service");
    }
}

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

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        BController controller = context.getBean("BController",BController.class);
        controller.sayHi();
    }
}

3.2 Setter方法注入

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

@Controller
public class BController {
    
    private StudentService studentService;

    @Autowired
    public void setStudentService(StudentService studentService){
        this.studentService = studentService;
    }

    public void sayHi() {
        studentService.sayHi();
    }
}

3.3 构造方法注入

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

@Controller
public class BController {

    private StudentService studentService;

    @Autowired
    public BController(StudentService studentService){
        this.studentService = studentService;
    }

    public void sayHi() {
        studentService.sayHi();
    }
}

3.4 三种注入方式的优缺点:

1.属性注入最大的优点就是实现简单、使用简单

   属性注入的缺点主要包含以下 3 个:

  1. 功能性问题:无法注入一个不可变的对象(final 修饰的对象);
  2. 通用性问题:只能适应于 IoC 容器;
  3. 设计原则问题:更容易违背单一设计原则。

缺点1:功能性问题

使用属性注入无法注入一个不可变的对象(final 修饰的对象),如下图所示:

原因也很简单:在 Java 中 final 对象(不可变)要么直接赋值,要么在构造方法中赋值,所以当使用属性注入 final 对象时,它不符合 Java 中 final 的使用规范,所以就不能注入成功了。

如果要注入一个不可变的对象,要怎么实现呢?使用下面的构造方法注入即可。

缺点2:通用性问题

使用属性注入的方式只适用于 IoC 框架(容器),如果将属性注入的代码移植到其他非 IoC 的框架中,那么代码就无效了,所以属性注入的通用性不是很好。

缺点3:设计原则问题

单一设计原则 定义: 就一个类而言, 应该仅有一个引起它变化的原因。单一设计原则就是自己只负责自己的事,不需要去关心别人的事。

使用属性注入的方式,因为使用起来很简单,所以开发者很容易在一个类中同时注入多个对象,而这些对象的注入是否有必要?是否符合程序设计中的单一职责原则?就变成了一个问题。 但可以肯定的是,注入实现越简单,那么滥用它的概率也越大,所以出现违背单一设计原则的概率也越大。 注意:这里强调的是违背设计原则(单一职责)的可能性,而不是一定会违背设计原则此处针对对象是类。

2.Setter 注入的优缺点

要说 Setter 注入有什么优点的话,那么首当其冲的就是它完全符合单一职责的设计原则,因为每一个 Setter 只针对一个对象

它的缺点主要体现在以下 2 点:

  1. 不能注入不可变对象(final 修饰的对象);
  2. 注入的对象可被修改。

缺点2:注入对象可被修改

Setter 注入提供了 setXXX 的方法,意味着你可以在任何时候、在任何地方,通过调用 setXXX 的方法来改变注入对象,所以 Setter 注入的问题是,被注入的对象可能随时被修改

3. 构造方法注入的优缺点

构造方法注入是 Spring 官方从 4.x 之后推荐的注入方式。

构造方法注入相比于前两种注入方法,它可以注入不可变对象,并且它只会执行一次,也不存在像 Setter 注入那样,被注入的对象随时被修改的情况,它的优点有以下 4 个:

  1. 可注入不可变对象;
  2. 注入对象不会被修改;
  3. 注入对象会被完全初始化;
  4. 通用性更好。

优点2:注入对象不会被修改

因为加了final关键字,而且构造方法注入不会像 Setter 注入那样,构造方法在对象创建时只会执行一次,因此它不存在注入对象被随时(调用)修改的情况。

优点3:完全初始化

因为依赖对象是在构造方法中执行的,而构造方法是在对象创建之初执行的,因此被注入的对象在使用之前,会被完全初始化,这也是构造方法注入的优点之一。

优点4:通用性更好

构造方法和属性注入不同,构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架,构造方法注入的代码都是通用的,所以它的通用性更好。

四、@Resource另⼀种注⼊关键字

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

@Controller
public class BController {

    @Resource
    private StudentService studentService;

    public void sayHi() {
        studentService.sayHi();
    }
}

4.1 @Autowired 和 @Resource 的区别

  1. 出身不同:@Autowired 来⾃于 Spring,⽽ @Resource 来⾃于 JDK 的注解;
  2. 使⽤时设置的参数不同:相⽐于 @Autowired 来说它只支持required,@Resource ⽀持更多的参数设置,例如name 设置,根据名称获取 Bean。
  3. @Autowired 可⽤于 Setter 注⼊、构造函数注⼊和属性注⼊,⽽ @Resource 只能⽤于 Setter 注⼊和属性注⼊,不能⽤于构造函数注⼊

4.2 同⼀类型多个 Bean 报错处理

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

@Component
class UserComponent {
    @Bean
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java");
        return user;
    }
    @Bean
    public User user2() {
        User user = new User();
        user.setId(2);
        user.setName("MySQL");
        return user;
    }
}

在另⼀个类中获取 User 对象,如下代码如下:

@Controller
public class UserController {
    // 注⼊
    @Resource
    private User user;
    public User getUser() {
        return user;
    }
}

解决同⼀个类型,多个 bean 的解决⽅案有以下两个:

  1. 使⽤ @Resource(name="user1") 定义。
  2. 使⽤ @Qualifier 注解定义名称,结合@Autowired

使⽤ @Resource(name="XXX")

@Controller
class UserController {
    // 注⼊
    @Resource(name = "user1")
    private User user;

    public User getUser() {
        return user;
    }
}

使⽤ @Qualifier

@Controller
public class UserController {
    // 注⼊
    @Autowired
    @Qualifier(value = "user2")
    private User user;
    public User getUser() {
        return user;
    }
}

@Qualifier中value可以省略~


 创作不易,欢迎大家私信我,一起探讨问题~ 

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

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

相关文章

字节跳动 EB 级 Iceberg 数据湖的机器学习应用与优化

深度学习的模型规模越来越庞大&#xff0c;其训练数据量级也成倍增长&#xff0c;这对海量训练数据的存储方案也提出了更高的要求&#xff1a;怎样更高性能地读取训练样本、不使数据读取成为模型训练的瓶颈&#xff0c;怎样更高效地支持特征工程、更便捷地增删和回填特征。本文…

[OnWork.Tools]系列 02-安装

下载地址 百度网盘 历史版本连接各种版本都有,请下载版本号最高的版本 链接&#xff1a;https://pan.baidu.com/s/1aOT0oUhiRO_L8sBCGomXdQ?pwdn159提取码&#xff1a;n159 个人链接 http://on8.top:5000/share.cgi?ssiddb2012fa6b224cd1b7f87ff5f5214910 软件安装 双…

Rust之泛型、特性和生命期(四):验证有生存期的引用

开发环境 Windows 10Rust 1.71.0 VS Code 1.80.1 项目工程 这里继续沿用上次工程rust-demo 验证具有生存期的引用 生存期是我们已经在使用的另一种泛型。生存期不是确保一个类型具有我们想要的行为&#xff0c;而是确保引用在我们需要时有效。 我们在第4章“引用和借用”一…

SpringCloud学习路线(13)——分布式搜索ElasticSeach集群

前言 单机ES做数据存储&#xff0c;必然面临两个问题&#xff1a;海量数据的存储&#xff0c;单点故障。 如何解决这两个问题&#xff1f; 海量数据的存储问题&#xff1a; 将索引库从逻辑上拆分为N个分片&#xff08;shard&#xff09;&#xff0c;存储到多个节点。单点故障…

新增WebDB和ChatGPT组件,支持对ChatGPT资产进行纳管,JumpServer堡垒机v3.5.0发布

2023年7月24日&#xff0c;JumpServer开源堡垒机正式发布v3.5.0版本。在这一版本中&#xff0c;新生代数据库连接组件——问题终结者Chen强势来袭&#xff0c;替代原有的OmniDB组件&#xff0c;在兼容旧版本的同时&#xff0c;解决了旧组件性能不足的问题&#xff0c;为用户提供…

微信小程序开发之配置菜单跳转到自定义页面

需求: 用户点击公众号菜单跳转到自定义带引流码的链接 公众号相关文档: 网页授权 | 微信开放文档 大致流程: 1.在公众号菜单配置链接: https://open.weixin.qq.com/connect/oauth2/authorize?appidXXXXXXXXXXXX&redirect_urihttps%3A%2F%2F测试域名%2Fws_dabai%2Fwe…

NoSQL-Redis持久化

NoSQL-Redis持久化 一、Redis 高可用&#xff1a;1.概述&#xff1a; 二、Redis持久化&#xff1a;1.持久化的功能&#xff1a;2.Redis 提供两种方式进行持久化&#xff1a; 三、RDB 持久化&#xff1a;1.定义&#xff1a;2.触发条件&#xff1a;3.执行流程&#xff1a;4.启动时…

HDFS的设计目标和重要特性

HDFS的设计目标和重要特性 设计目标HDFS重要特性主从架构分块存储机制副本机制namespace元数据管理数据块存储 设计目标 硬件故障(Hardware Failure)是常态&#xff0c;HDFS可能有成百上千的服务器组成&#xff0c;每一个组件都有可能出现故障。因此古见检测和自动快速恢复的H…

选择合适的图表,高效展现数据魅力

随着大数据时代的来临&#xff0c;数据的重要性愈发凸显&#xff0c;数据分析和可视化成为了决策和传递信息的重要手段。在数据可视化中&#xff0c;选择合适的图表是至关重要的一环&#xff0c;它能让数据更加生动、直观地呈现&#xff0c;为观众提供更有说服力的信息。本文将…

JavaScript 练手小技巧:音乐播放器的歌词显示

暑假了&#xff0c;还是不能让自己闲着&#xff0c;学点自己感兴趣的知识&#xff0c;写点自己喜欢的代码。 今天写了一个播放器的雏形&#xff0c;带歌词显示。 没去自定义播放器&#xff0c;主要是写歌词显示效果。效果图如下&#xff1a; 首先当然是要准备一个 mp3 文件。 …

RocketMQ基本概念与入门

文章目录 MQ基本结构依赖案例:productConsumer 核心概念1.nameserver2.broker3.主题队列4.queue队列5. 生产者6.消费者分组和生产者分组7.消费点位 MQ基本结构 message: 消息数据对象product: 程序代码,生成消息,发送消息到队列consumer: 程序代码,监听(绑定)队列,获取消息,执行…

分布式锁:Redis、Zookeeper

1.基于Redis实现分布式锁&#xfeff; Redis分布式锁原理如上图所示&#xff0c;当有多个Set命令发送到Redis时&#xff0c;Redis会串行处理&#xff0c;最终只有一个Set命令执行成功&#xff0c;从而只有一个线程加锁成功 2.SetNx命令加锁 利用Redis的setNx命令在Redis数据库…

数据结构【绪论】

数据结构入门级 第一章绪论 什么是数据结构&#xff1f;什么是数据类型&#xff1f; 程序数据结构算法 一、基本概念&#xff1a; 数据&#xff1a;指所有能被计算机处理的&#xff0c;无论图、文字、符号等。数据元素&#xff1a;数据的基本单位&#xff0c;通常作为整体考…

Unity TMP (TextMeshPro) 创建字体材质

1 TMP 简介 完整名称&#xff1a;Text Mesh Pro &#xff0c;unity新一代主流字体插件 1.1 组件变化 内置的Text组件以及与内置Text组件绑定的Button、DropDown、InputField均被替换为使用TextMeshPro的版本 内置的Text组件以及与内置Text组件绑定的Button、DropDown、Input…

tinymce插件tinymce-powerpaste-plugin——将word中内容(文字图片等)直接粘贴至tinymce编辑器中

TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有&#xff1a;UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。 TinyMCE的优势&#xff1a; 开源可商用&#xff0c;基于LGPL2.1 插件丰富&#xff0c;自带插件基本涵盖日常…

【项目设计】基于负载均衡的在线oj平台

目录 一、项目介绍 二、开发环境以及技术 三、概要设计 四、关键算法 五、项目演示 六、代码实现 一、项目介绍 该项目是基于负载均衡的在线oj&#xff0c;模拟平时刷题网站&#xff08;leetcode和牛客&#xff09;写的一个在线判题系统 项目主要分为五个模块&#xff…

OpenAI重磅官宣ChatGPT安卓版本周发布,现已开启下载预约,附详细预约教程

7月22号&#xff0c;OpenAI 突然宣布&#xff0c;安卓版 ChatGPT 将在下周发布&#xff01;换句话说&#xff0c;本周安卓版 ChatGPT正式上线&#xff01; 最早&#xff0c;ChatGPT仅有网页版。 今年5月&#xff0c;iOS版ChatGPT正式发布&#xff0c;当时OpenAI表示Android版将…

Docker—— consul的容器服务更新与发现

Docker—— consul的容器服务更新与发现 一、Consul概述1.什么是服务注册与发现2.什么是consul 二、consul 部署1.consul服务器①. 建立 Consul 服务②. 查看集群信息③. 通过 http api 获取集群信息 2.registrator服务器①. 安装 Gliderlabs/Registrator②. 测试服务发现功能是…

智能小说文本字幕生成器

分享一个免费的&#xff0c;智能小说文本字幕生成器 智能分句。短词。 链接&#xff1a;https://pan.baidu.com/s/15xGlQg01LmbHHuGFZbgaiw?pwd0gjv 提取码&#xff1a;0gjv

分类评估指标

文章目录 1. 混淆矩阵2. Precision(精准率)3. Recall(召回率)4. F1-score5. ROC曲线和AUC指标5.1 ROC 曲线5.2 绘制 ROC 曲线5.3 AUC 值6. API介绍6.1 **分类评估报告api**6.2 **AUC计算API**练习-电信客户流失预测1. 数据集介绍2. 处理流程3. 案例实现4. 小结1. 混淆矩阵 …