【Spring】使用注解存储Bean对象

目录

一、配置扫描路径(使用注解的方式存对象的前提)

二、使用类注解存储Bean对象

1、使用五大类注解存储Bean对象 

2、为什么要这么多的类注解?

2.1、五大类注解之间的关系 

3、获取Bean对象时的默认命名规则

三、使用方法注解来存储Bean对象

1、使用方法注解@Bean需要注意的事项

2、添加@Bean注解的方法的重命名

2.1、需要注意细节


上一篇博客中,说到的存储Bean对象的方式,每创建一个类都需要我们自己手动注入到spring中,这个过程比较繁琐。

Spring还有一种注入对象的方式,也就是使用注解。当我们学习使用Spring时,想要实现一个功能,首先需要考虑的是有没有实现对应功能的注解。下面我们来了解一下如何使用注解的方式来存储Bean对象。

一、配置扫描路径(使用注解的方式存对象的前提)

首先创建一个Spring项目,然后在resources包中创建一个spring-config.xml文件,然后向这个文件中配置下面的代码(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.java.demo"></content:component-scan>
</beans>

上述的代码中<content:component-scan base-package="com.java.demo"></content:component-scan>是用来配置Bean的扫描根路径的。其中的base-package属性的值,就是配置的扫描路径。只有在这些目录中的类才会被扫描是否添加了注解,如果添加了注解,就将这些类存放到IoC容器中。

像下图中设置的扫描路径为com.java.demo,那么在扫描的时候,就会扫描这个目录中的所有类,并且包括这个目录中的子目录中的类也会扫描。所以我们在设置扫描路径的时候,尽可能的设置的详细一点,就好比我们设置的扫描路径为com,那么在扫描时,会将com中的所有类包括com目录中的子目录中的类也扫描一遍,这就会导致在启动类的时候工作量比较大,启动时间比较长。


二、使用类注解存储Bean对象

想要将对象存储在Spring中,有两种注解类型可以实现

  • 类注解:@Controller(控制器)、@Service(服务)、@Repository(仓库)、@Component(组件)、@Configuration(配置)。
  • 方法注解:@Bean

1、使用五大类注解存储Bean对象 

1️⃣我们先使用@Controller来存储Bean对象,代码如下

package com.java.demo;import org.springframework.stereotype.Controller;@Controller
public class User {public void sayHi(){System.out.println("hello User.");}
}

我们类的创建需要在我们设置的扫描路径下,这样启动类获取对象时,可以扫描到这个User类使用了@Controller注解,就会将这个Bean对象存储到spring中

下面是获取IoC容器中的Bean对象,这个写法还是和上一个博客中写的获取Bean对象的方法一样,通过依赖查找来获取对象,默认情况下,Bean对象名称是以大驼峰方式命名类的小写形式(也就是Bean对象名默认为小驼峰形式)

import com.java.demo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {//1、得到容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");//2、得到Bean对象//这里获取Bean对象时,使用的对象名称是以大驼峰形式命名的类的小写形式(小驼峰)User user= context.getBean("user",User.class);//3、调用Bean方法user.sayHi();}
}

 2️⃣使用@Service,@Repository,@Component,@Configuration注解来存储Bean对象,代码如下

package com.java.demo;import org.springframework.stereotype.Service;@Service
public class UserService {public void sayHi(){System.out.println("hi,UserService.");}
}package com.java.demo;import org.springframework.stereotype.Repository;@Repository
public class UserRepository {public void sayHi(){System.out.println("hello UserRepository .");}
}package com.java.demo;import org.springframework.context.annotation.Configuration;@Configuration
public class UserConfiguration {public void sayHi(){System.out.println("hello userConfiguration .");}
}package com.java.demo;import org.springframework.stereotype.Component;@Component
public class UserComponent {public void sayHi(){System.out.println("hello userComponent .");}
}
import com.java.demo.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App2 {public static void main(String[] args) {//1、得到容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");//2、得到Bean对象UserRepository userRepository= context.getBean("userRepository", UserRepository.class);//3、调用Bean方法userRepository.sayHi();UserComponent userComponent= context.getBean("userComponent", UserComponent.class);userComponent.sayHi();UserConfiguration userConfiguration= context.getBean("userConfiguration", UserConfiguration.class);userConfiguration.sayHi();UserService userService= context.getBean("userService", UserService.class);userService.sayHi();}
}

 可以看到只要这些使用了五大类注解的类在我们设置的扫描路径中,那么获取这些Bean对象时都会获取成功。

3️⃣但是如果我们创建的类,没有在扫描路径下,那么获取Bean对象时,就会失败。

这里我们创建一个student类,将其放在com目录下,来观察出现的结果。我们指定的扫描路径为com.java.demo.这里创建的student在com下,但是和Java目录同级,所以没有在扫描路径中。

package com;import org.springframework.stereotype.Component;@Component
public class Student {public void sayHi(){System.out.println("hello Student");}
}

 4️⃣使用五大类注解存储Bean和使用XML格式存储Bean是可以混合使用的

这种使用场景一般是,就像上述的student类,他在com目录下,而扫描路径是com.java.demo。想要使用这个Bean对象,如果使用注解的方式,就需要将扫描路径设置为com。这样扫描范围就非常的大,类启动的时候就会很慢。这种情况我们就可以使用XML格式存储Bean,其他的类还是使用类注解的方式存储Bean,扫描路径不变。


2、为什么要这么多的类注解?

上述进行了五大类注解的使用,最终结果都是将Bean对象存储到spring中,那么为什么需要这么多的注解呢?

这些注解都是通过在类上添加注解来告诉Spring容器它们的作用和职责,从而实现依赖注入和自动装配。它们使得开发人员能够更方便地管理和组织代码,并提供了灵活的配置和扩展性。

  • @Controller:(控制器)将类标记为控制器组件,校验用户请求数据的正确性。
  • @Service:(服务)将类标记为业务逻辑组件,服务、编排和调度具体执行方法,不会直接进行操作,根据请求判断具体调用哪个方法。
  • @Repository:(数据持久层)将类标记为数据访问组件,用于直接与数据库进行交互
  • @Component:(工具类层)将类标记为Spring容器的组件,为整个项目存放一些需要使用的组件,但是又和其他层没有什么实际交互。
  • @Configuration:将类标记为配置类(项目中的一些配置)

 上述的五大类注解也对应这个Java项目标准分层的层级,也就是说我们在写Java项目的时候需要按照这个分层类型来创建包和类,使用不同的注解,表示了这个类的作用,让程序员更好的管理程序代码。

2.1、五大类注解之间的关系 

这些注解的作用都是相同的,都是将对象注入到对象中,所以他们之间一定存在一些共性的,我们来了解一下他们直接的关系。

 可以看到五大类注解中,@Service(服务),@Repository(仓库),@Configuration(配置),@Controller(控制器)是基于@Component(组件)来实现的,这四个注解都是@Component的扩展。也可以理解为@Component是这个四个类注解的父类。


3、获取Bean对象时的默认命名规则

上述的代码中获取Bean对象的操作中,我们使用的对象名为小驼峰的形式,也就是类名的第一个字符小写其他字符不变形式这种形式是使用类注解存储Bean对象之后,在获取对象时的默认形式,但是这个默认形成能够正常使用的前提是,这个类名是按照大驼峰的形式命名的如果出现其他的类的命名方式,这个默认的用法就不能获取到Bean对象了。

下面我们使用一个列子来说明,我们创建一个UConfig类(不是按照大驼峰的形式命名的类),放在com.java.demo扫描路径下。然后来通过小驼峰的方式来命名Bean对象并获取(uConfig)观察结果。

package com.java.demo;import org.springframework.context.annotation.Configuration;@Configuration
public class UConfig {public void sayHi(){System.out.println("hello UConfig");}
}

使用小驼峰的形式命名对象名获取不到Bean对象,但是直接使用类名的方式可以作为对象名获取到Bean对象。

UConfig uConfig = context.getBean("UConfig", UConfig.class);

 这里我们就需要看一下命名规则的源码了

按两下Shift键,idea就会弹出一个全局搜索的框,然后输入BeanName(查找对象的命名规则) ,然后点击圈出来的这个类。

 进入AnnotationBeanNameGenerator类之后,找到下面的方法,他表示的就是生成BeanName

 但是这个方法中并没有明确的写出Bean的命名,他最后调用的方法为创建默认对象名。进入这个方法继续查看。

进入decapitate方法,继续查看。这个方法给出了默认命名的细节实现。

如果类名的长度大于1并且满足第一个与第二个字符为大写,则构造的默认BeanName为原类名,按照大驼峰的规则命名的类名(第一个字符为大写,第二个字符为小写),构造的默认BeanName为原类名的小驼峰形式(第一个字符小写,其他字符不变)。

 我们点击追踪,来查看一下这个默认命名的方法的实现,他是有JDK实现的。

✨ 总结BeanName的规范命名规则

  1. 如果类名不存在或类名为空,BeanName为原类名
  2. 如果类名长度超过1,并且第一个与第二个字符为大写,则BeanName为原类名
  3. 类名按照大驼峰命名,也就是第一个字符大写,第二个字符小写,BeanName为原类名的小驼峰形式。

三、使用方法注解来存储Bean对象

1、使用方法注解@Bean需要注意的事项

我们说到类注解的时候,只需要将类注解加在类上就可以直接使用依赖查找的方式获取Bean对象,但是这里的方法注解的使用和类注解存在较大的差异。

1️⃣使用方法注解@Bean,需要方法的返回值是一个对象。

2️⃣方法注解@Bean不能单独使用,需要和五大类注解搭配使用。

这里我们通过一个示例来了解一下,创建一个普通的ArticleInfo(文章实体)类。给这个类并没有添加五大类注解。

package com.java.demo.model;import java.time.LocalDateTime;public class ArticleInfo {private int id;private String title;private String content;private LocalDateTime createTime;public void setId(int id) {this.id = id;}public void setTitle(String title) {this.title = title;}public void setContent(String content) {this.content = content;}public void setCreateTime(LocalDateTime createTime) {this.createTime = createTime;}@Overridepublic String toString() {return "ArticleInfo{" +"id=" + id +", title='" + title + '\'' +", content='" + content + '\'' +", createTime=" + createTime +'}';}
}

这里写一个伪代码,因为给文章实体类没有添加五大类注解,所以我们没有办法从容器中获取对象,这里我们自己在ArticleInfo方法中实例了一个对象,为其设置属性的值。给ArticleInfo方法添加@Bean注解。

package com.java.demo;import com.java.demo.model.ArticleInfo;
import org.springframework.context.annotation.Bean;import java.time.LocalDateTime;public class Articles {@Beanpublic ArticleInfo ArticleInfo(){//伪代码ArticleInfo articleInfo = new ArticleInfo();articleInfo.setId(1);articleInfo.setTitle("今天周几?");articleInfo.setContent("今天周一");articleInfo.setCreateTime(LocalDateTime.now());return articleInfo;}
}

获取bean对象

import com.java.demo.model.ArticleInfo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {//1、得到容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");//2、得到Bean对象ArticleInfo articleInfo = context.getBean("ArticleInfo", ArticleInfo.class);//3、调用Bean方法System.out.println(articleInfo.toString());}
}

❓❓❓ 为什么方法注解不能和类注解一样,添加了之后就可以直接获取对象?


❗❗❗原因在与在我们创建的扫描路径中,存在很多类,每个类里面又存在很多方法,如果按照类注解的方式对目录中的方法进行扫描,那么程序的执行效率就会非常慢,所以规定在使用@Bean方法注解的时候,需要添加上类注解,这就相当于我们了解五大类注解的时候设置扫描路径是一个道理,精确扫描范围,提高程序的效率。

下面我们对Articles类添加一个类注解,表示扫描这个对象中的方法。再次执行扫描到添加了@Bean注解的方法的返回值就会被保存在容器中了,获取对象就会成功。

package com.java.demo;import com.java.demo.model.ArticleInfo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;@Component
public class Articles {@Beanpublic ArticleInfo ArticleInfo(){//伪代码ArticleInfo articleInfo = new ArticleInfo();articleInfo.setId(1);articleInfo.setTitle("今天周几?");articleInfo.setContent("今天周一");articleInfo.setCreateTime(LocalDateTime.now());return articleInfo;}
}

 3️⃣我们使用方法注解@Bean将对象存入到容器中,我们在获取对象时默认的对象名为方法名。


2、添加@Bean注解的方法的重命名

使用依赖查找获取使用方法注解@Bean存储的对象时,传入的对象名默认的情况下为方法名,虽然说这样获取没有问题,但是在一个项目中的方法起名时有getXXX或者setXXX,用这种默认的方式获取对象,总是看起来不是很舒服。所以这里我们要说到重命名的问题。

@Bean注解是可以设置参数的,设置name属性就可以给存储的对象设置一个别名。

package com.java.demo;import com.java.demo.model.ArticleInfo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;@Component
public class Articles {@Bean("abc")public ArticleInfo ArticleInfo(){//伪代码ArticleInfo articleInfo = new ArticleInfo();articleInfo.setId(1);articleInfo.setTitle("今天周几?");articleInfo.setContent("今天周一");articleInfo.setCreateTime(LocalDateTime.now());return articleInfo;}
}import com.java.demo.model.ArticleInfo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {//1、得到容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");//2、得到Bean对象ArticleInfo articleInfo = context.getBean("abc", ArticleInfo.class);//3、调用Bean方法System.out.println(articleInfo.toString());}
}

 ✨重命名@Bean的几种方式

//方式一
@Bean("abc");
//方式二
@Bean(name="abc")
//方式三
@Bean(value="abc")
//方式四:支持指定多个名称
@Bean(name={"abc","ddd","ccc"})
//方式五
@Bean(value={"aabc","ddd","eee"})
//方式六
@Bean({"aaa","bbb"})

2.1、需要注意细节

1️⃣当我们给Bean重命名了之后,就不能在使用Bean默认的对象名,也就是使用方法名获取对象,这个时候就会报错。

2️⃣在对Bean重命名时,不要使用当前类(以大驼峰形式命名的类)的小驼峰形式给Bean重命名,因为当前类添加了类注解,获取对象的时候默认是当前类名的小驼峰形式。当你获取Bean对象的时候,对象的名称重复了,就会报错。

 3️⃣当我们对同一个类中多个方法返回的对象Bean重命名时,使用了相同的名字,这个时候程序执行的时候,从上往下执行,只会将第一个方法返回的Bean存储到Spring中,之后的相同名称的Bean就不会在存储到Spring中了,容器会忽略掉。

 4️⃣当我们对不同类中的方法返回的对象Bean重命名时,使用了相同的名字,这个时候想要指定某个类中的方法先执行,我们使用@Order注解来控制类加载的顺序,在@Order()中设置的参数越大,表示当前类的加载权重越高,越先被加载,进而影响到类中方法的加载,也就影响到Spring中存放的是那个Bean了。

 5️⃣在Spring中对一个方法添加了@Bean注解,那么这个方法不能设置参数。

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023年新手如何学剪辑视频 想学视频剪辑如何入门

随着短视频、vlog等媒体形式的兴起&#xff0c;视频剪辑已经成为了热门技能。甚至有人说&#xff0c;不会修图可以&#xff0c;但不能不会剪视频。实际上&#xff0c;随着各种智能软件的发展&#xff0c;视频剪辑已经变得越来越简单。接下来&#xff0c;一起来看看新手如何学剪…

以指标驱动,保险、零售、制造企业开启精益敏捷运营的新范式

近日&#xff0c;以“释放数智生产力”为主题的 Kyligence 用户大会在上海前滩香格里拉大酒店成功举行。大会包含上午的主论坛和下午的 4 场平行论坛&#xff0c;并举办了闭门会议、Open Day 等活动。来自金融、零售、制造、医药等行业的客户及合作伙伴带来了超过 23 场主题演讲…

VAE、 EM、KL散度

文章目录 VAEVAE额外的损失函数 EMKL散度 VAE 左图相当于变量x&#xff0c;右图相当于z 假如在AE中&#xff0c;一张满月的图片作为输入&#xff0c;模型得到的输出是一张满月的图片&#xff1b;一张弦月的图片作为输入&#xff0c;模型得到的是一张弦月的图片。当从满月的code…

瞅一眼nginx

目录 &#x1f9ac;什么是nginx? &#x1f9ac;nginx配置官方yum源&#xff1a; &#x1f9ac;nginx优点 &#x1f9ac;nginx 缺点 &#x1f9ac;查看nginx默认模块 &#x1f40c;nginx新版本的配置文件&#xff1a; &#x1f40c;nginx目录索引 &#x1f40c;nginx状态…

网络编程(JavaEE初阶系列10)

目录 前言&#xff1a; 1.网络编程的基础 1.1为什么需要网络编程 1.2什么是网络编程 1.3网络编程中的基本概念 1.3.1发送端和接收端 1.3.2请求和响应 1.3.3客户端和服务端 2.Socket套接字 2.1概念 2.2分类 3.UDP数据报套接字编程 3.1DataGramSocket API 3.2Datagr…

【游戏客户端】制作你玩我猜Like玩法

【游戏客户端】制作你玩我猜Like玩法 大家好&#xff0c;我是Lampard猿奋~~ “你画我猜”相信大家都不陌生&#xff0c;当初这款小游戏可谓茶余饭后必玩之选&#xff0c;风头一时无二。今天要和大家分享如何实现一个你玩我猜Like的玩法。 我们可以简单的把需求拆成两个个部分&…

python实现对图油画、卡通、梦幻、草图、水彩效果

本篇博客将介绍如何使用wxPython模块和OpenCV库来实现对图像进行灰度化、二值化、伽马校正、色彩空间转换和图像反转这5种效果的合并程序。程序可以通过wxPython提供的GUI界面来选择图片路径和效果类型&#xff0c;程序会将处理后的图像保存到指定路径并打开。 步骤一&#xf…

2021年12月 Python(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

一、单选题(共25题,每题2分,共50分) 第1题 昨天的温度是5摄氏度,今天降温7摄氏度,今天的温度是多少摄氏度? A:12 B:7 C:2 D:-2 正确的答案是 D:-2。 解析: 根据题目描述,昨天的温度是 5 摄氏度,今天降温了 7 摄氏度。降温意味着温度变低,所以今天的温度…

k8s之Pod控制器

目录 一、Pod控制器及其功用二、pod控制器的多种类型2.1 pod容器中的有状态和无状态的区别 三、Deployment 控制器四、SatefulSet 控制器4.1 StatefulSet由以下几个部分组成4.2 为什么要有headless&#xff1f;4.3 为什么要有volumeClaimTemplate&#xff1f;4.4 滚动更新4.5 扩…

解决MAC M1处理器运行Android protoc时出现的错误

Protobuf是Google开发的一种新的结构化数据存储格式&#xff0c;一般用于结构化数据的序列化&#xff0c;也就是我们常说的数据序列化。这个序列化协议非常轻量级和高效&#xff0c;并且是跨平台的。目前&#xff0c;它支持多种主流语言&#xff0c;比传统的XML、JSON等方法更具…

性能优化必知必会

系统性能调优 分为四个层次 基础设施网络编解码分布式系统性能整体提升 一&#xff1a;基础设施优化 从提升单机进程的性能入手&#xff0c;包括高效的使用主机的CPU、内存、磁盘等硬件&#xff0c;通过提高并发编程提升吞吐量&#xff0c;根据业务特性选择合适的算法 01 …

CSS—选择器

目录 一、CSS简介 二、HTML页面中常用的元素 三、CSS语法规则 四、常用的选择器 五、选择器参考 一、CSS简介 CSS (Cascading Style Sheets&#xff0c;层叠样式表&#xff09;&#xff0c;是一种用来为结构化文档&#xff08;如 HTML 文档或 XML 应用&#xff09;添加样式…

如何把非1024的采样数放入aac编码器

一. aac对数据规格要求 二、代码实现 1.初始化 2.填入数据 3.取数据 三.图解 一. aac对放入的采样数要求 我们知道aac每次接受的字节数是固定的&#xff0c;在之前的文章里有介绍libfdk_aac音频采样数和编码字节数注意 它支持的采样数和编码字节数分别是&#xff1a; fdk_aac …

Go context.WithCancel()的使用

WithCancel可以将一个Context包装为cancelCtx,并提供一个取消函数,调用这个取消函数,可以Cancel对应的Context Go语言context包-cancelCtx 疑问 context.WithCancel()取消机制的理解 父母5s钟后出门&#xff0c;倒计时&#xff0c;父母在时要学习&#xff0c;父母一走就可以玩 …

【mock安装时报错】

node版本需要大于16 切换>16的版本即可 建议使用 nvm进行 node版本状态管理 可参考另一篇文章 nvm多版本管理方案

windows美化任务栏,不使用软件

1.任务栏透明: 效果图: (1).winr打开命令行 输入regedit回车打开注册表 regedit (2).在注册表中打开 \HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced 这个路径 \HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explore…

爬虫获取电影数据----以沈腾参演电影为例

数据可视化&分析实战 1.1 沈腾参演电影数据获取 文章目录 数据可视化&分析实战前言1. 网页分析2. 构建数据获取函数2.1 网页数据获取函数2.2 网页照片获取函数 3. 获取参演影视作品基本数据4. 电影详细数据获取4.1 导演、演员、描述、类型、投票人数、评分信息、电影海…

【51单片机】晨启科技,7针OLED显示驱动程序,STC89C52RC

文章目录 原理图oled.coled.hmain.c 原理图 sbit OLED_SCLP4^3;//SCL-D0 sbit OLED_SDAP4^1;//SDA-D1 sbit OLED_RES P3^6;//RES sbit OLED_DC P3^7;//DC sbit OLED_CSP2^7; //CS oled.c #include "OLED.h"//******************************说明*******************…

Redis 高频数据类型使用详解

目录 一、前言 二、Redis常用数据类型 2.1 常见的数据类型 三、String 类型 3.1 String 类型简介 3.2 String常用操作命令 3.2.1 String 操作命令实践 3.3 常用业务场景 3.3.1 session共享 3.3.2 登录失败计数器 3.3.3 限流 3.3.4 多线程安全控制 四、Hash类型 4…

android 如何分析应用的内存(十五)——Visual Studio Code 调试Android应用

android 如何分析应用的内存&#xff08;十五&#xff09;——Visual Studio Code 调试Android 应用 在上一篇文章介绍了jdb调试java应用 接下来介绍用UI界面调试java应用&#xff0c;达到同jdb一样的效果。 同样的UI界面有很多选择&#xff0c;如Eclipse&#xff0c;Android …