Spring使用注解存储Bean对象

文章目录

  • 一. 配置扫描路径
  • 二. 使用注解储存Bean对象
    • 1. 使用五大类注解储存Bean
    • 2. 为什么要有五大类注解?
    • 3.4有关获取Bean参数的命名规则
  • 三. 使用方法注解储存Bean对象
    • 1. 方法注解储存对象的用法
    • 2. @Bean的重命名

在前一篇博客中( Spring项目创建与Bean的存储与读取(DL))介绍的是通过配置文件注册对象从而存储到 Spring 中,这种方式其实还是挺繁琐的。

实际上,在使用学习使用 Spring过程中,当我们要实现一个功能的时候,先应该考虑的是有没有相应的注解是实现对应功能的,Spring 中很多功能的配置都是可以依靠注解实现的,而本篇中介绍的是使用注解来存储 Bean 对象。

一. 配置扫描路径

首先还是要创建 Spring 项目,这里有问题还是去看我上一篇博客。当创建好项目后,我们的第一步就是配置扫描路径,这一步骤非常关键的,这里错了,之后的的操作就都不会生效了。

我们在resources目录下创建一个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=""></content:component-scan>
</beans>

其中<content:component-scan base-package=""></content:component-scan>里面 base-package的值设置为你需要扫描对象的根路径,这个路径从java目录开始,比如我在如图中的com.tr.demo目录下创建类:
img
那么这个配置文件中根路径就为com.tr.demo,所以我们将base-package的值设置为com.tr.demo

<content:component-scan base-package="com.tr.demo"></content:component-scan>

二. 使用注解储存Bean对象

想要使用注解,那得先知道能使用哪些注解,在 Spring 中有五大类注解和方法注解,分别为:

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

1. 使用五大类注解储存Bean

首先,我们来了解如何使用五大类注解来储存对象,先以@Controller注解为例,我们有如下的代码:

package com.tr.demo;import org.springframework.stereotype.Controller;@Controller
public class UserController {public void sayHi() {System.out.println("Hi, UserController~");}
}

像这样在扫描路径下创建类,并在类上加上@Controller注解就将Bean存储到容器当中了。

接下来就要从 Spring 中读取出我们的对象,这里还是先使用依赖查找的方式来获取 Bean,使用五大类注解,默认情况下,Bean 的名字就是原类名首字母小写(小驼峰)。

import com.tr.demo.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class APP {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");//获取对象时使用类名的小驼峰形式作为 name 参数UserController userController =  context.getBean("userController", UserController.class);userController.sayHi();}
}

运行结果:
img
要注意,是使用了五大类注解创建的类且类必须要在前面我们配置的扫描路径下(包括子包)才能将 Bean 存储到 Spring 当中,否则是无效的,所以这个扫描路径也叫做根路径。

设置根路径其实也是为了提高程序的性能,因为如果不设置根路径,Spring 就会扫描项目文件中所有的目录,但并不是所有类都需要储存到 Spring当中,这样性能就会比较低,设置了根路径,Spring 就只扫描该根路径下所有的目录就可以了,提高了程序的性能。

上面只使用了 @Controller,那么我们再来验证一下其他四个注解可不可以达到同样的目的,同时为了验证上面的结论,我们在com.tr.demo目录下再创建一个inner目录,在根路径外在创建一个类Student使用类注解。

img

package com.tr.demo.inner;
import org.springframework.stereotype.Component;@Component
public class UserComponent {public void sayHi() {System.out.println("Hi, UserComponent~");}
}package com.tr.demo.inner;
import org.springframework.context.annotation.Configuration;@Configuration
public class UserConfiguration {public void sayHi() {System.out.println("Hi, UserConfiguration~");}
}package com.tr.demo.inner;
import org.springframework.stereotype.Repository;@Repository
public class UserRepository {public void sayHi() {System.out.println("Hi, UserRepository~");}
}package com.tr.demo.inner;
import org.springframework.stereotype.Service;@Service
public class UserService {public void sayHi() {System.out.println("Hi, UserService~");}
}import com.tr.demo.UserController;
import com.tr.demo.inner.UserComponent;
import com.tr.demo.inner.UserConfiguration;
import com.tr.demo.inner.UserRepository;
import com.tr.demo.inner.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class APP {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");//获取对象时使用类名的小驼峰形式作为 name 参数UserController userController =  context.getBean("userController", UserController.class);userController.sayHi();UserService service =  context.getBean("userService", UserService.class);service.sayHi();UserConfiguration configuration =  context.getBean("userConfiguration", UserConfiguration.class);configuration.sayHi();UserComponent component =  context.getBean("userComponent", UserComponent.class);component.sayHi();UserRepository repository =  context.getBean("userRepository", UserRepository.class);repository.sayHi();}
}

运行结果:

五大类注解效果都是一样的,而不在根路径下的Student是无效的。
img

还需要知道的是使用注解存储的 Bean 和使用XML存储的的 Bean 是可以一同使用的,比如我们将将刚刚有问题的Student重新通过XML的方式进行存储。

img

运行结果:

img

2. 为什么要有五大类注解?

既然都五大类完成的是同样的工作,那为什么要有五大类注解呢?

其实五大类注解主要是为了规范 Java 项目的代码,Java 项目的标准分层如下:

  1. 控制层(Controller)
  2. 服务层(Service)
  3. 数据持久层(Dao)

而五大类注解便是对应着不同的层级别使用的,让程序猿看到某一个注解就可以明确这个了类是做什么的。

  • @Controller:控制器,校验用户请求数据的正确性(安保系统);直接和前端打交道,校验前端发来请求是参数和合法性。

  • @Service:服务,编排和调度具体执行方法的(客服中心);不会直接操作数据库,根据请求判断具体调用哪个方法。

  • @Repository:数据持久层,直接和数据库交互(实际业务的执行),也叫DAO层(data access object)。

  • @Component:组件(工具类层),为整个项目存放一些需要使用的组件,但又和其他层没有什么实际交互。

  • @Configuration 配置项(项目中的一些配置)。

img

包括企业中也是按照这样的结构来将项目分层的,典型的比如阿里,它只是在标准分层在服务层(Service)做了一个扩展,划分的更加细致详细了。

img

五大类注解主要起到的是“见名知意”的作用,代码层面上来看,作用是类似的,我们去查看五大类类注解的源码看一看。

img

img

img

img

img
可以看到五大类的源码中除了 @Component 以外,其他四大类注解中都包含了 @Component 注解的功能,这四大类注解都是基于 @Component 实现的,是 @Component 拓展。

3.4有关获取Bean参数的命名规则

上文中在使用依赖查找的方式获取Bean时,getBean方法的BeanName是使用类名的小驼峰形式(即类名的首字母小写),这是因为使用注解储存对象时,默认会将类名的小驼峰形式设置为 Bean 的名字,但并不是完全依照这个规则的,是有特殊情况的。

比如,我们创建一个类,将它的前两个字母大写,如UConfig,此时来看使用类名的小驼峰形式还能不能获取到 Bean。

package com.tr.demo;import org.springframework.stereotype.Repository;@Repository
public class UConfig {public void sayHi(){System.out.println("Hi, UConfig~");}
}

启动类

import com.tr.demo.UConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class APP2 {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UConfig uConfig=  context.getBean("uConfig", UConfig.class);uConfig.sayHi();}
}

运行结果:
img
可以看到程序报错了,说没有找到beanNameuConfig的对象,那此时的beanName是什么呢?

此时再来试一下原本的类名(大驼峰),看能不能获取:

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

运行结果:
img
此时就获取到了,好像多少有点玄学在里面,我们来翻一翻源码,看一看这是什么原因。

双击Shift进行全局搜索,上面是根于对象名称来找到对象的,所以我们输入beanName,试着搜索一下:

img

我们会发现有AnnotationBeanNameGenerator类与BeanNameGenerator接口,那我们就试着点到AnnotationBeanNameGenerator类源码看一看。

正常点开后看到的应该是 IDEA 将.class文件反编译出来的代码,缺少注释和明确的变量命名。

img

我们点击Download Sources将 Spring 源码下载下来即可,此时我们在在源码中就能看到下面的方法,看名字也知道,用来建立默认的BeanName的。

img

返回值是Introspector.decapitalize方法的返回值,再点进去看看这个方法。

img

此时我们就能分析得出结论,如果类名长度大于1并且满足第一个与第二个字母为大写,则构造的BeanName就为原类名,其他正常情况为类名的小驼峰形式,这就解释了UConfig类的BeanName为什么是原类名了。
而且我们会发现这个方法所在类是来自于jdk的。
img

所以,BeanName的规范命名规则并不是 Spring 独创的,而依照 Java 标准库的规则进行的。

  1. 如果类名不存在或类名为空字符串,BeanName为原类名。
  2. 如果类名字长度大于1,且第一个与第二个字符为大写,BeanName为原类名。
  3. 其他情况,BeanName为原类名的小驼峰形式。

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

1. 方法注解储存对象的用法

五大类注解是添加到某个类上的,而方法注解是放到方法上的,当一个方法返回的是一个具体的实例对象时,我们就可以使用方法注解@Bean来将对象储存到 Spring,但是单单使用一个@Bean是不能够成功储存对象的,还需要在方法所在类上使用五大类注解才行,比如搭配一 个@Component 注解,方法注解是不能够单独使用的,@Bean注解必须要搭配五大类注解一起使用(Spring为了提升性能所做的规定,毕竟造方法的成本太低了,不能去扫描整个项目的方法吧)。

还是要注意使用必须是在根路径下。

比如我们有一个普通文章的实体类ArticleInfo

package com.tr.demo.model;import java.time.LocalDateTime;/*** 普通的文章实体类*/
public class ArticleInfo {private int aid;private LocalDateTime createtime;private String title;private String author;private String content;public void setAid(int aid) {this.aid = aid;}public void setCreatetime(LocalDateTime createtime) {this.createtime = createtime;}public void setTitle(String title) {this.title = title;}public void setAuthor(String author) {this.author = author;}public void setContent(String content) {this.content = content;}@Overridepublic String toString() {return "ArticleInfo{" +"aid=" + aid +", createtime=" + createtime + "\n" +", title='" + title + '\'' +", author='" + author + '\'' + "\n" +", content='" + content + '\'' +'}';}
}

下面演示使用@Bean方法注解储存对象

package com.tr.demo;import com.tr.demo.model.ArticleInfo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import java.time.LocalDateTime;@Controller
public class Articles {@Bean// 将当前方法返回的对象存储到 IoC 容器public ArticleInfo getArt(){// 伪代码(实际上这里的 Bean 不是 new 出来的)ArticleInfo articleInfo = new ArticleInfo();articleInfo.setAid(1);articleInfo.setCreatetime(LocalDateTime.now());articleInfo.setTitle("夏日绝句");articleInfo.setAuthor("李清照");articleInfo.setContent("生当做人杰,死亦为鬼雄。至今思项羽,不肯过江东。");return articleInfo;}public void sayHi(){System.out.println("Hi, Articles~");}
}

获取方法注解储存的对象时,传入的BeanName参数值默认值就是方法名,我上面的代码中方法名为getArt,所以获取时,就使用getArt作为参数来进行获取。

import com.tr.demo.model.ArticleInfo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class APP3 {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");ArticleInfo article =  context.getBean("getArt", ArticleInfo.class);System.out.println(article);}
}

运行结果:
img

2. @Bean的重命名

获取方法注解储存的对象时,传入的BeanName参数值默值为方法名,但像上面那样返回对象的方法名称往往是getXXX这样式取名的,虽然在语法与实现上是没有问题的,但实际开发写出这样的代码,看起来还是比较别扭的。

实际上注解 @Bean 是可以加参数的,给储存的对象起别名,像下面这个样子。

@Controller
public class Articles {@Bean("article")// 将当前方法返回的对象存储到 IoC 容器public ArticleInfo getArt(){// 伪代码(实际上这里的 Bean 不是 new 出来的)ArticleInfo articleInfo = new ArticleInfo();articleInfo.setAid(1);articleInfo.setCreatetime(LocalDateTime.now());articleInfo.setTitle("夏日绝句");articleInfo.setAuthor("李清照");articleInfo.setContent("生当做人杰,死亦为鬼雄。至今思项羽,不肯过江东。");return articleInfo;}public void sayHi(){System.out.println("Hi, Articles~");}
}

也可以给 Bean 设置多个别名,总结起来有如下几种方式:

//方式一(省略参数名的情况下默认是name)
@Bean("article1")
//方式二
@Bean(name = "article2")
//方式三
@Bean(value = "article3")
//起多个别名
@Bean(name = {"article4", "article5"})
@Bean(value = {"article6", "article7"})
@Bean({"article8", "article9", "article10"})

我们按照第 9 行的方式设置,此时获取方法注解储存的对象就能够使用别名来进行获取。

img

import com.tr.demo.model.ArticleInfo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class APP4 {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");ArticleInfo article1 =  context.getBean("article6", ArticleInfo.class);System.out.println(article1);System.out.println("-----------------------------------------------------");ArticleInfo article2 =  context.getBean("article7", ArticleInfo.class);System.out.println(article2);System.out.println("-----------------------------------------------------");}
}

运行结果:
img

再想一下,当一个 Bean 有别名了,那使用之前那个方法名还能够获取到对象吗?尝试一下:

import com.tr.demo.model.ArticleInfo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class APP5 {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");ArticleInfo article =  context.getBean("getArt", ArticleInfo.class);System.out.println(article);}
}

运行结果:

此时就能发现是获取不到的
img
所以使用 @Bean 存储对象的beanName命名规则是,当没有设置name/value属性时,此时 Bean 的默认名字就是方法名,一旦添加了别名name/value属性后,就只能通过重命名的别名来获取 Bean 了,默认的使用方法名获取 Bean 对象就不能使用了。

还要简单注意一下,@Bean 使用时,同一类如果多个 Bean 使用相同的名称,此时程序执行是不会报错的,他会根据类加载顺序和类中代码从上至下的的顺序,将第一个 Bean 存放到 Spring 中,但第一个之后的对象就不会被存放到容器中了,也就是只有在第一次创建 Bean 的时候会将对象和 Bean 名称关联起来,后续再有相同名称的Bean存储时候,容器会自动忽略。

还可以通过类注解 @Order 注解控制类加载顺序(值越小,优先级越高),进而影响 Bean 的存放的先后顺序,这些也比较简单,就不做演示了。

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

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

相关文章

nginx 配置 wss加密访问 mqtt

1. 在服务器上部署mqtt服务 2.在宝塔上配置域名证书 3.nginx配置websocket server {listen 80;listen 443 ssl http2;server_name ws-xx.example.com;index index.php index.html index.htm default.php default.htm default.html;root /www/wwwroot/ws-xx.example.com;loca…

服务器量化训练操作说明

Freespace服务器预训练主要步骤&#xff1a; 首先登录堡垒机&#xff0c;命令如下&#xff1a; ssh xxxrelay.baidu-int.com &#xff08;xxx为个人邮箱前缀&#xff09; 密码为个人邮箱密码 登录工作机&#xff0c;命令如下&#xff1a; ssh l3yq01-gpu-255-122-22-00.e…

如何在小程序首页隐藏商品分类

​因为在分类页面有显示分类&#xff0c;所以有的商家希望在首页就不显示分类啦。下面就介绍一下如何在首页隐藏商品分类。 在小程序管理员后台->页面设置->首页&#xff0c;显示商品分类设置为否。设置后&#xff0c;在小程序首页商品分类就不会显示啦。注意&#xff1a…

django rest_framework 框架动态设置序列化返回的字段

动态修改字段可以使Django rest框架API像graphQL端点一样&#xff0c;只从模型中检索所需的字段。 一旦序列化器被初始化&#xff0c;就可以使用.fields属性访问序列化器上设置的字段字典。访问和修改此属性允许您动态修改序列化器。 显式地修改fields参数可以帮助您做一些奇…

关于Integer类的一个有趣的面试问题

相信很多人觉得答案是false&#xff0c;false&#xff0c;因为Integer是一个类&#xff0c;把int类型的数据传给Integer类型的数据会创建一个对象&#xff0c;而a,b,c,d作为引用指向的是不同的地址&#xff0c;所以判断相同得到的结果应该是false 但这个想法就正中下怀了&#…

【Spring篇】初识 Spring IoC 与 DI

目录 一. Spring 是什么 ? 二. 何为 IoC ? 三. 如何理解 Spring IoC ? 四. IoC 与 DI 五 . 总结 一. Spring 是什么 ? 我们通常所说的 Spring 指的是 Spring Framework&#xff08;Spring 框架&#xff09;&#xff0c;它是⼀个开源框架&#xff0c;有着活跃⽽ 庞⼤…

探秘Spring中Bean的注解宝典:解读存取Bean的相关注解及用法

目录 存储Bean对象Controller(控制器存储)Service(服务存储)Repository(仓库存储)Component(组件存储)Configuration(配置存储)Bean重命名Bean 获取Bean对象属性注入构造方法注入Setter注入Resource(注入关键字) 存储Bean对象 将对象存储在 Spring 中&#xff0c;有两种注解类…

【Linux】信号保存信号处理

前言&#xff1a;对信号产生的思考 上一篇博客所说的信号产生&#xff0c;最终都要有OS来进行执行&#xff0c;为什么&#xff1f;OS是进程的管理者&#xff01;信号的处理是否是立即处理的&#xff1f;在合适的时候 -》那什么是合适的时候&#xff1f;信号如图不是被立即处理…

Windows安装RabbitMQ

Windows安装RabbitMQ 前言配置erlang环境下载配置环境变量验证 安装RabbitMQ验证 参考 前言 本文并不涉及到RabbitMQ的底层原理&#xff0c;或者别的一些特性说明&#xff0c;仅仅只是安装。 配置erlang环境 因为RabbitMQ是使用该语言开发的。 下载 下载地址&#xff1a; …

《qt quick核心编程》笔记一

1.基础HelloWorld代码 import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15Window {width: 400height: 300visible: truetitle: qsTr("Hello 1World")Rectangle {width: parent.widthheight: parent.heightcolor: "gray"Text {…

【数据挖掘】bytewax 与 ydata工具可实时了解您的数据

一、说明 在这篇博文中&#xff0c;我们将介绍如何将开源流式处理解决方案 bytewax 与 ydata 分析相结合并加以利用&#xff0c;以提高流式处理流的质量。 STream 处理支持在传输中和存储之前对数据进行实时分析&#xff0c;并且可以是有状态的&#xff0c;也可以是无状态的。 …

【网络编程】网络套接字udp通用服务器和客户端

1.预备知识 认识端口号 端口号(port)是传输层协议的内容&#xff1a; 端口号是一个2字节16位的整数(uint16)端口号用来标识主机上的一个进程IP地址port能够标识网络上的某一台主机和某一个进程一个端口号只能被一个进程占用 认识TCP协议 此处我们先对TCP(Transmission Con…

Spring MVC异步上传、跨服务器上传和文件下载

一、异步上传 之前的上传方案&#xff0c;在上传成功后都会跳转页面。而在实际开发中&#xff0c;很多情况下上传后不进行跳转&#xff0c;而是进行页面的局部刷新&#xff0c;比如&#xff1a;上传头像成功后将头像显示在网页中。这时候就需要使用异步文件上传。 1.1 JSP页面 …

[golang gin框架] 41.Gin商城项目-微服务实战之后台Rbac微服务(用户登录 、Gorm数据库配置单独抽离、 Consul配置单独抽离)

上一节抽离了captcha验证码功能,集成了验证码微服务功能,这一节来看看后台Rbac功能,并抽离其中的用户登录,管理员管理,角色管理,权限管理等功能作为微服务来调用 一.引入 后台操作从登录到后台首页,然后其中的管理员管理,角色管理,权限管理等功能可以抽离出来作为 一个Rbac微服…

Python实战

官方文档 请点击下面工程名称&#xff0c;跳转到代码的仓库页面&#xff0c;将工程 下载下来 Demo Code 里有详细的注释 LearnPythonPython 实现功能点demo

OpenCV for Python 实战(一):获取图片拍摄GPS地址并自动添加水印

Hello 我们在OpenCV每天的基础博客当中已经更新了很多了&#xff0c;那么今天我们就来结合前几天的内容。做一个获取属性然后添加对应属性的水印。那让我们赶快开始吧~ 文章目录 图片EXIFPython 获取EXIFexifread库使用方法转换成文字地址 添加水印cv2.putText() 每日总结 图片…

【001 操作系统】什么是线程、进程?线程进程的区别是什么?

一、什么是线程、进程&#xff1f; 进程&#xff1a;进程是资源分配的基本单位&#xff0c;它是程序执行时的一个实例&#xff0c;在程序运行时创建。 在Linux环境下&#xff0c;每个进程有自己各自独立的 4G 地址空间&#xff0c;大家互不干扰对方&#xff0c;如果两个进程之间…

基于大模型的Text2SQL微调的实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

Homography单应性矩阵

1. Homography 单应性概念 考虑 同一个平面(比如书皮)的两张图片&#xff0c;红点表示同一个物理坐标点在两张图片上的各自位置。在 CV 术语中&#xff0c;我们称之为对应点。 Homography 就是将一张图像上的点映射到另一张图像上对应点的3x3变换矩阵. 因为 Homography 是一个 …

Python模块requests基本用法

简介 Python 的 requests 模块是一个流行的第三方库&#xff0c;用于发送HTTP请求。它提供了一组简洁且易于使用的API&#xff0c;使得在Python中进行网络通信变得更加简单和灵活。 目录 1. 基本概念 1.1. HTTP 协议 1.2. GET 请求 1.3. POST 请求 1.4. get 和 post 的区别…