Spring的基础用法

news2024/12/23 15:19:10

前言

上面的目录内容本文章不一定都会写到,本次的文章将非常简略,主写主要内容,次要内容需要读者有一定的学习基础才可以看懂以及掌握,本文章比较偏向于理论复习,注意是理论,本文章代码不多,代码比较多的是高效率复习文章系列,该文章后续也会有Spring高效率复习文章。

Spring简介

简介

Spring是一个非常实用的框架,它提取了大量的再实际开发中需要重复解决的步骤,将这些步骤抽象成一个框架。
以Spring为核心还衍生出了一系列框架,如Spring Web Flow、Spring Security、Spring Data、SpringBoot、Spring Cloud等。
Spring是企业级应用开发的“一站式”选择,Spring贯穿表现出、业务层、持久层。Spring并且以高度的开放性与其他框架无缝整合。
总结起来,Spring具有如下优点。

  • 低入侵式设计,代码的污染极小。
  • 独立于各种应用服务器,基于Spring框架的应用,可以真正实现“Write Once,Run Anywhere”的承诺——写一次,在任何地方运行。
  • Spring的IOC容器降低了业务对象替换的复杂性,提高了组件之间的解耦。
  • Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层数据库访问。
  • Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选用Spring框架的部分或全部功能。
    下图显示了Spring框架的组成结构图。

在这里插入图片描述

正如上图所看到的,当使用Spring框架时,必须使用Spring Core Container(即Spring容器),它代表了Spring框架的核心机制。Spring Core Container 主要由org.springframework.core、org.springframework.beans.org.springframework.context和org.springframework.expression4个包及其子包组成,主要提供SpringIOC容器支持。其中,org.springframework.expression及其子包是Spring3.0新增的,它提供了SpringExpression Language支持。

Spring入门

此处就不说怎么安装下载了,此处也并没有进行实机演示,文字描述和代码就行。

使用Spring管理Bean

Spring核心容器就是一个超级大工厂,可以生产所有的对象,包括第三方框架等其他对象(SqlSessionFactory、数据源等等)都会被当成Spring核心容器管理的对象——Spring把容器中的一切对象统称为Bean。
我们在初学JavaSE时,都听说过这样的说法,如果一个类,提供了成员变量以及对应的setter和getter方法和无参构造器等等,那么这个对象就是遵循了JavaBean的规范,此处所说的JavaBean和Spring的Bean本质上大同小异。
例如下面程序先定义一个简单的类。

public class Axe{
	public String chop(){
		return "使用斧头砍柴";
	}
}

我们再定义一个Person类,而Person类需要调用Axe的方法,这就叫做依赖,Person类依赖Axe类。

public class Person{
	private Axe axe;
	//设置注入所需的setter方法
	public void setAxe(Axe axe){
		this.axe = axe;
	}
	public void useAxe(){
		System.out.println("我打算去砍点柴火");
		//调用axe的chop()方法
		//表明Person对象依赖axe对象
		System.out.println(axe.chop());
	}
}

我们将这两个对象交给Spring核心容器去管理,通过XML配置文件让Spring将两个对象管理为Bean。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置名为person的Bean,其实现类是Person -->
<bean id = "person" class="Person">
	<!-- 控制调用setAxe()方法,将容器中的axe Bean作为传入参数 -->
	<property name = "axe" ref = "axe"/>
</bean>
<!-- 配置名为axe的Bean -->
<bean id = "axe" class = "Axe"/>

配置文件中的<bean …/>元素默认以反射方式来调用该类无参的构造器,以如下元素为例:

<bean id = "person" class = "Person">

Spring框架解析该<bean …/>元素后将可以得到两个字符串,其中idStr的值为"person"(解析该bean的id属性得到的值),classStr的值为"Person"(解析该bean的class属性得到的值)。
也就是说,Spring底层会执行如下形式的代码:

String idStr = ...;//解析<bean .../>元素的id属性得到该字符串的值为"Person"
//解析<bean .../>元素的class属性得到该字符串值为"Person"
String classStr = ...;
Class clazz = Class.forName(classStr);//全类名反射创建对象
Object obj = clazz.newInstance();//创建实例
//container代表Spring容器
//我们可以先把这个容器理解为一个Map集合
//保存对象实例的时候是put(key,value)->(String,Object)
//取值的时候是get(key)
container.put(idStr,obj);//obj是classStr反射得到后的对象实例

其实和之前的JavaWeb内容一模一样<bean>的id是存放到容器里的key值,class是保存Bean对象的全类名,通过全类名来反射创建对象实例然后保存到容器中的value值。如果依赖注入,则使用<proerty …/>来设置,name属性设置本对象中要赋值注入的属性,ref表示要注入的实际对象,该ref通常是另一个Bean标签的id,通过这个引用Bean标签的id来创建一个实例对象,然后将这个实例对象赋值到name属性所表示的对象属性上。
每个<bean…/>元素默认驱动Spring调用该类无参构造器来创建实例,并将该实例作为Spring容器中的Bean。
上面还包含了一个<property…/>子元素,它驱动Spring在底层以反射方式执行一次setter方法。其中,<property…/>的name属性值决定执行哪个setter方法,而value或ref决定执行setter方法的传入参数。

  • 如果传入参数是基本类型及其包装类、String等类型,则使用value属性指定传入参数。
  • 如果以容器中其他Bean作为传入参数,则使用ref数学指定传入参数。

Bean一旦被创建出来,Spring就会立即根据<propert…/>子元素来执行一次setter方法,也就是说,<bean…/>元素驱动Spring调用构造器创建对象;<propert…/>子元素驱动Spring执行setter方法,这两步是先后执行的,中间几乎没有任何间隔。
以上面配置文件中的如下配置为例:

<bean id = "person" class="Person">
	<!-- 控制调用setAxe()方法,将容器中的axe Bean作为传入参数 -->
	<property name = "axe" ref = "axe"/>
</bean>

上面配置中<proeprt…/>元素的name属性值为axe,该元素将驱动Spring以反射方式执行 person Bean的setAxe()方法;ref属性值为axe,该属性值指定以容器中名为axe的Bean作为执行setter方法的传入参数。
也就是说,Spring底层会执行以下形式的代码:

String nameStr = ...;//解析子元素的name属性得到字符串"axe"
String refStr = ...;//解析子元素的ref属性得到字符串"axe"
String setterName = "set" + nameStr.subString(0,1).toUpperCase()
+nameStr.substring(1);//生成将要调用的setter方法名 
//获取Spring容器中名为refStr的Bean,该Bean将会作为传入参数
Object paramBean = container.get(refSr);
//此处的clazz是前一段反射代码通过<bean.../>元素的class属性得到的Class对象
Method setter = clazz.getMethod(setterName,paramBean.getClass());
//此处的obj参数是前一段反射代码为<bean.../>元素创建的对象
setter.invoke(obj,paramBean);

我们来大概解读一下上面的代码:
在这里插入图片描述
这两个变量,第一个用于获取set方法,refStr用于获取引用的类实例,用来赋值nameStr所指向的实际执行方法参数。
在这里插入图片描述
这个字符串是获取实际要执行的set方法,首先以固定的一个set字符串来作为前缀,然后调用nameStr(此时是<property…/>的name属性的值)变量的截取字符串方法,截取第一个字符,并且将其转换为大写,获取一个子串,此时相当于set+A,后面的API是截取从索引x开始的所有字符串,所以就是set+A+xe;此时setterName=setAxe,刚好对应了类中的setter方法。
在这里插入图片描述
此处是为了获取<property…/>子元素的ref的值所代表的实体类对象,该对象的作用是为前面的setAxe方法赋值,作为形参。可以看到调用核心容器来获取该实例对象,就像在Map里通过key值取value值一样。
在这里插入图片描述
这里是获取setAxe方法的步骤,首先Method是大方法,clazz是前面Person类,然后getMethod是获取方法,第一个参数是方法名,第二个参数是形参的类型。
这里相当于Person.getMethod(“setAxe”,Axe.class);
在这里插入图片描述
我们获取了实际的方法了,也就是setter引用所指向的对象(万物皆对象,Class也是对象,这是一个很哲学的思考题),obj是前面创建的Person类的实例,invoke表示调用执行方法,第一个参数表示执行对象是谁,obj就是Person类对象,第二个参数表示要传入的参数是什么,paramBean是在核心容器中取出的Axe实例对象,注意这里是实际的Axe实例对象。
在这里插入图片描述
经过上面的操作等同于:

Axe axe = new Axe();
Person person = new Person();
person.setAxe(person,axe);//为person的axe实体属性赋值

每个<property…/>元素默认驱动Spring调用一次setter方法。
有了上面的配置文件后,我们就可以用Spring容器来访问容器中的Bean了,ApplicationContext是Spring容器最常用的接口,该接口有如下两个实现类。

  • ClassPathXmlApplicationContext:在类加载路径下搜索配置文件,并根据配置文件来创建Spring容器。
  • FileSystemXmlApplicationContext:在文件系统的相对路径或绝对路径下搜索配置文件,并根据配置文件来创建Spring容器。

对于Java项目而言,类加载路径总是稳定的,因此,通常总是使用ClassPathXmlApplicationContext创建Spring容器。下面是本例的示例代码。

public class BeanTest{
	public static void main(String[]args)throws Excetion{
		//创建Spring容器
		var ctx = new ClassPathXmlApplicationContext("beans.xml");
		//获取id为person的Bean
		var p = ctx.getBean("person",Person.class);
	}
}

getBean这里,可以只写个key值,但是这样需要强制类型转换,如果指定了泛型类型,则无须强制类型转换。

  • Object getBean(String id):根据容器中Bean的id来获取指定Bean,获取Bean之后需要进行强制类型转换。
  • T getBean(String name,Class<T>requiredType):根据容器中Bean的id来获取指定Bean,但该方法带一个泛型参数,因此获取Bean之后无须进行强制类型转换。

调用方法后可以看到结果:

我打算去砍点柴火
使用斧头砍柴

上面就完成了Spring的一个入门操作了。

Spring的核心机制:依赖注入

程序并没有给axe成员变量设置值,但是实际调用的时候依然正常执行,这说明axe成员变量并不是程序主动设置的,而是由Spring容器负责设置的——开发者主要为axe成员变量提供一个setter方法,并通过<property…/>元素驱动Spring容器调用该setter方法为Person对象的axe成员变量设置值。
像这种A调用B的方法,也可以称为A依赖B,Spring把这种互相调用的关系称为依赖关系。假如A组件调用了B组件的方法,即可称A组件依赖B组件。
Spring的核心功能有两个:

  • Spring容器作为超级大工厂,负责创建、管理所有的Java对象,这些Java对象被称为Bean。
  • Spring容器管理容器中Bean之间的依赖关系,Spring使用一种被称为“依赖注入”的方法来管理Bean之间的依赖关系。

这种依赖注入和直接创建对象以及工厂模式对比的话,首先实现了无硬编码的方式进行了解耦,其实根本无须关心自己依赖的实体对象的创建过程,只调用。

理解注入依赖。

Spring的注入依赖早期时被称为控制反转,意思是Java的对象的控制权(赋值等等)从程序员手中交给了程序负责,后来也被称为依赖注入,这两者是同一回事。
当某个Java对象(调用者)需要调用另一个Java对象(被依赖对象)的方法时,在传统模式下通常有如下两种做法:

  • 原始做法:调用者主动创建被依赖对象,然后调用被依赖对象的方法。
  • 简单工厂模式:调用者先找到被依赖对象的工厂,然后主动通过工厂获取被依赖对象,最后调用被依赖对方的方法。

使用简单工厂模式的坏处是:

  • 调用组件需要主动通过工厂去获取被依赖对象,这就会带来调用组件与被依赖对象工厂的耦合。
  • 程序需要额外维护一个工厂类,增加了编程的复杂度。

使用了Spring框架后,调用者无须主动获取被依赖对象,而是只要被动接收Spring容器为调用者的成员变量赋值即可。使用Spring框架后,调用者获取被依赖对象的方式从主动获取变成了被动接收——于是有了“依赖注入”的方式/概念。
打个比喻,人需要复杂,原始做法等于自己造了个斧子,简单工厂模式相当于去工厂买了个斧子,或者就是找地方买了个斧子,而依赖注入则只有等着给斧子用就行了,无须关心斧子的来头,只用即可。
依赖注入通常有如下三种方式:

  • 设值注入:IOC容器使用成员变量的setter方法注入被依赖对象。
  • 构造注入:IOC容器使用构造器注入被依赖对象。
  • 接口注入:调用者实现特定接口,并实现接口中的方法,而IOC容器会自动检测并调用这个特定的方法,从而完成依赖注入。

设值注入

设置注入指的是IOC容器通过成员变量的setter方法注入被依赖对象。这种方式简单、直观,因此在Spring的依赖中被大量使用。
当我们程序中需要将A依赖的B换一个B的时候,在Spring中操作很简单,将原来的对象id所代表的全类名更换为新的全类名即可。

构造注入

构造注入是指利用构造器来完成依赖注入,驱动Spring在底层以反射方式执行带参数的构造器,在执行带参数的构造器时,就可利用构造器参数对成员变量执行初始化——这就是构造注入的本质。
<bean…/>元素默认总是驱动Spring调用无参构造器来创建对象,可以使用<constructor-arg…/>子元素,每个子元素代表一个构造器参数,如果包含N个,则驱动Spring调用带N个参数的构造器来创建对象。
下面只简单演示下XML中配置变量。

<bean id="person" class="Person">
	<constructor-arg ref="Axe"/>
</bean>

其实变化就是把<property…/>元素去掉换成了更简单的构造器参数引用。
上面的代码相当于驱动Spring执行如下代码:

String idStr= ...;//元素id
String refStr = ...;//子元素ref
Object paramBean= container.get(refStr);//和前面一致
Object obj = new Person(paramBean);//传入参数进行构造器初始化操作
//container代表Spring容器
container.put(idStr,obj);

其实本质就是在保存创建的Person实例到容器前,就通过构造器完成了依赖注入,而非后续的依赖注入,创建时顺带就完成了依赖注入再存到容器里。
Spring虽然有自动类型转换,但是遇到字符串和整数类型时,Spring只能解析为字符串,所以此时可以通过type来指定,比如<constructor-arg value = “23” type = “int”/>,Spring明确知道此处配置了一个int类型的参数。前面的value也可以指定type属性,用于确定该属性值的数据类型。

两种注入方式的对比。

设值注入更直观和可读性更强,也很简单,对于很复杂的依赖关系,使用构造器注入则导致构造器过于臃肿,而且通过setter方法设定依赖关系显得更加直观、自然。
构造注入可以决定依赖关系注入的顺序,并且依赖关系只能在构造器中设定,只有组件的创建者才能改变组件的依赖关系,对于调用者而言完全透明,因为没有setter方法,所有的依赖关系全部在构造器内设定。因此也无须担心后续代码对依赖关系产生破坏,更符合高内聚的原则。

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

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

相关文章

【python】异常处理及程序调试(一)

异常类和异常处理 一、程序的错误类型 语法错误&#xff1a;不符合语法规则而产生的错误&#xff0c;如&#xff1a;标识符命名错误&#xff0c;缩进错误&#xff0c;访问未定义的变量等。这类错误在编辑或解释时就会被检测出来&#xff0c;产生这类错误&#xff0c;程序将会…

node.js+uni计算机毕设项目基于微信小程序点餐与结账系统LW(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

2023跨年代码(烟花+自定义文字+背景音乐+雪花+倒计时)

2023年快要到来啦&#xff0c;很高兴这次我们又能一起度过~ 目录 一、前言 二、跨年烟花 三、效果展示 倒计时 2023​编辑 兔圆圆​编辑 四、编码实现 index.html 烟花&#xff0c;雪花&#xff0c;背景音乐&#xff0c;页面样式 index.js 自定义文字 五、获取代码 需…

算法 | 如何通过Math.random()方法实现X平方或更多次方的概率?

前言 本文主要介绍Java中Math.random()方法以及该方法的简单应用。 每种语言都有随机方法&#xff0c;在Java中的随机方法有Math.random()方法、Random类。 Math.random Math.random()方法的返回值的是double类型&#xff0c;其返回值的范围为[0,1)&#xff0c;包含0&#…

每日笔记

下周要做一个hmi获取登录码、小程序码的新接口&#xff0c;需要用到新的知识来实现这个功能 接口英文名 getUnlimitedQRCode 功能描述 该接口用于获取小程序码&#xff0c;适用于需要的码数量极多的业务场景。通过该接口生成的小程序码&#xff0c;永久有效&#xff0c;数量…

Es客户端学习

版本&#xff1a;opensearch-rest-high-level-client-2.3.0.jar&#xff0c;httpcore-nio-4.4.11.jar&#xff0c;httpasyncclient-4.1.4.jar 问题背景 初始化es索引逻辑是监听大数据团队消息&#xff0c;然后异步写入es(org.opensearch.client.RestHighLevelClient#bulkAsyn…

七、Kubernetes1.25应用部署、发布的两种方式

1、概述 通过现代的 Web 服务&#xff0c;用户希望应用程序能够 24/7 全天候使用&#xff0c;开发人员希望每天可以多次发布部署新版本的应用程序。 容器化可以帮助软件包达成这些目标&#xff0c;使应用程序能够以简单快速的方式发布和更新&#xff0c;而无需停机。这段话是Ku…

智子商城项目实践开发文档

ZutShop Junior practical training project 在csdn暂存一下开发文档 本人大三做的实训项目&#xff0c;前后端分离。 后端&#xff1a;https://github.com/roydonGuo/ZutShop 前端使用Vue&#xff0c;前端项目地址&#xff1a;https://github.com/roydonGuo/ZutShop-Vue 0. …

python利用json和pyecharts画折线图实例

目录 一.json模块对数据进行处理 二.利用pyecharts画折线图 三.利用pyecharts画美、日、印三国家折线图 四.本文数据集 注&#xff1a;本次实验的数据在文章最后面&#xff0c;我已上传至百度网盘 一.json模块对数据进行处理 上面三个txt文本是这三个国家疫情爆发相关的数据 …

linux有磁盘空间却显示不足 linux中inode使用率过高处理办法

linux中inode使用率过高处理办法 前几天收到监控告警&#xff0c;说Inode节点空间不足&#xff0c;之前没处理过这种问题&#xff0c;所以记录一下处理过程&#xff0c;便于以后查阅。 Inode使用率高并不会影响系统正常运行和新文件的创建&#xff0c;但是当使用率达到100%的…

【Linux】Linux编译器gcc/g++的使用

今天不学习&#xff0c;明天变垃圾。 文章目录一、程序的翻译过程1.预处理&#xff08;1.2.3把你的代码编译成二进制代码&#xff09;2.编译&#xff08;C语言 > 汇编语言&#xff09;3.汇编&#xff08;无法被执行的二进制文件&#xff0c;为什么捏&#xff1f;&#xff09…

腾讯云Windows 轻量应用服务器如何搭建 FTP 服务?

本文档介绍如何在 Windows 操作系统的轻量应用服务器上通过 IIS 搭建 FTP 站点。 本文搭建 FTP 服务组成版本如下&#xff1a; Windows 操作系统&#xff0c;本文以系统镜像 Windows Server 2012 为例。IIS&#xff1a;Web 服务器&#xff0c;本文以 IIS 8.5 为例。 步骤1&am…

自行车在线租赁管理系统的设计与实现

自行车在线租赁管理系统 摘 要 随着Internet的不断发展&#xff0c;在线租赁在现实生活中的使用和普及&#xff0c;自行车在线租赁行业成为近年内出现的一个新行业&#xff0c;并且能够成为大众广为认可和接受的行为和选择。设计自行车在线租赁管理系统的目的就是借助计算机…

(四)汇编语言——简单程序

目录 编写程序的工作过程 编写汇编源程序 伪指令 汇编指令 注释 编写步骤 编译连接 编译 连接 运行 总结 编写程序的工作过程 接下来&#xff0c;我们简单介绍一下一个汇编程序&#xff0c;从写出到最后执行&#xff0c;他到底经历了哪些步骤。 编写汇编源程序 首先…

vue3 antd项目实战——修改table表格的默认样式(一)调整table表格每行(row)行高过高问题

vue3 antd项目实战——修改ant design vue table组件的默认样式&#xff08;调整每行行高&#xff09;知识调用场景复现实际操作解决a-table表格padding过宽知识调用 文章中可能会用到的知识链接vue3ant design vuets实战【ant-design-vue组件库引入】css样式穿透&#xff08;…

node.js+uni计算机毕设项目基于微信小程序的房屋交易平台(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

智能超表面(RIS)辅助双功能雷达和通信波束形成设计(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 智能超表面&#xff0c;也叫做“可重配智能表面”&#xff0c;或者“智能反射表面”&#xff0c;英文为RIS&#xff08;Reconf…

springboot消息

发送短信的简单原理 activemq 5.17 版本的 需要java11 环境 mq 网页版本控制台 账号密码为admin 指定发送的名 即上图的默认名为itheima 监听器 自动监听消息 接受 如果接受到消息 还想再其他地方使用 可以使用sendto 使用管理员权限 登录 然后列出插件 安装插件 r…

Java 注解 自定义注解 注解的解析

目录 一、认识Java自带注解 二、自定义注解 1、认识元注解 2、自定义注解 3、注解的使用 4、反射解析注解 一、认识Java自带注解 jdk中自带了一些注解&#xff0c;如下四个&#xff1a; Override类方法的的重写注解Deprecated类或方法被废除的注解FunctionalInterface函…

数据结构---串

&#xff08;一&#xff09;串的基本概念 串&#xff08;string或字符串&#xff09;是由零个或多个字符组成的有限序列&#xff0c;一般记为&#xff1a;s′a1a2…a′n(n≥0) 其中&#xff0c;s是串的名称&#xff0c;用单括号括起来的字符序列是串的值; ai(1≤i≤n)ai(1≤i…