第四章 Spring的基础用法

news2024/12/25 12:41:23

文章目录

  • Spring的起源和背景
  • 理解依赖注入
  • Spring容器
  • 理解Spring容器中的Bean
  • 管理容器中的Bean及其依赖注入
  • 自动装配
  • 使用Java类进行配置管理
  • 使用静态工厂、实例工厂创建Bean实例
  • 抽象Bean与子Bean
  • 容器中的工厂Bean
  • 管理Bean的生命周期
  • 几种特殊的依赖注入
  • Spring的简化配置
  • SpEL的功能和用法

前言

上面的目录内容本文章不一定都会写到,本次的文章将非常简略,主写主要内容,次要内容需要读者有一定的学习基础才可以看懂以及掌握,本文章比较偏向于理论复习,注意是理论,本文章代码不多,代码比较多的是高效率复习文章系列,该文章后续也会有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方法,所有的依赖关系全部在构造器内设定。因此也无须担心后续代码对依赖关系产生破坏,更符合高内聚的原则。

使用Spring容器

P283

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

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

相关文章

kali中间人攻击

数据来源 一、中间人攻击原理 1. 利用的ARP协议的漏洞 2. ARP协议原理&#xff1a; 1&#xff09;发送ARP广播请求目标MAC地址 2&#xff09;目标主机发送ARP单播应答&#xff0c;响应MAC地址 3. ARP攻击原理 攻击人通过发送虚假的ARP应答实现ARP缓存投毒!而受害人没有办法进行…

文件存储案例

1.文件存储-File文件存储案例 1.1.案例要求 1.2参考代码 文件读取 百度安全验证 文件最终的保存的目录在/data/data/user/0/包/files下&#xff08;1&#xff09;布局文件 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android&q…

kubernetes中使用Service反向代理外部服务

当我们的某个服务在外部集群的时候&#xff0c;但是又想k8s集群内的应用连接它&#xff0c;这是可以创建一个service&#xff0c;用service代理外部服务&#xff0c;然后集群内就能连接该service&#xff0c;从而间接的访问外部服务。 创建一个service代理外部的服务 创建一个…

代码挑战画 魔法圣诞树

一、前言 本文会基于C# GDI技术 从零到一 实现一颗 魔法圣诞树&#xff01;源码和素材在文末全部都有&#xff01; 二、魔法圣诞树 对于用代码画圣诞树&#xff0c;网上各种编程语言像python、css、java、c/c我们都有见到过了&#xff0c;那么在绘图方面&#xff0c;还有一位…

FastDDS(6)核心库综述

Fast DDS(前身为Fast RTPS)是DDS规范的高效高性能实现,DDS规范是一种用于分布式应用软件的以数据为中心的通信中间件(DCPS)。本次回顾Fast DDS的体系结构、操作和关键特性。 架构 Fast DDS的架构如下图所示,其中可以看到具有以下不同环境的层模型。 Application layer应…

人员工装未穿戴识别检测 opencv

人员工装未穿戴识别检测基于OpenCvyolo计算机视觉深度学习技术对现场画面中人员行为着装穿戴实时监测识别&#xff0c;发现不按要求着装违规行为立即抓拍存档同步后台。OpenCV-Python使用Numpy&#xff0c;这是一个高度优化的数据库操作库&#xff0c;具有MATLAB风格的语法。所…

RabbitMQ 第一天 基础 1 MQ的基本概念 1.4 MQ 的劣势 1.5 常见的MQ 产品

RabbitMQ 【黑马程序员RabbitMQ全套教程&#xff0c;rabbitmq消息中间件到实战】 文章目录RabbitMQ第一天 基础1 MQ的基本概念1.4 MQ 的劣势1.4.1 MQ 的劣势1.4.2 小结1.5 常见的MQ 产品第一天 基础 1 MQ的基本概念 1.4 MQ 的劣势 1.4.1 MQ 的劣势 从远程调用 到 利用 MQ 作…

css实现九宫格

首先是实现九宫格的样式&#xff0c;对每一行进行偏移&#xff0c;当鼠标放上去会使他们形成一张图片。 html <div class"img_container"><div class"img1"></div><div class"img1"></div><div class"i…

2022年,来者犹可追

始料未及的是&#xff0c; 疫情持续到了2022年。好在“大疫不过三年”&#xff0c;只不过是结束来的同样措不及防&#xff0c;全家的一次高烧免疫&#xff0c;没有朋友圈中的云淡风轻&#xff0c;冷暖自知&#xff0c;希望明年能够拥有平安喜乐的时光。回首这一年&#xff0c;“…

kotlin与java实现混编基础看这篇就够了

前几年一直关注安卓&#xff0c;想换个方向&#xff0c;奔着移动端大步向前&#xff0c;由于比较懒就一直停留在想法&#xff0c;这不今天勤快点&#xff0c;动手搞了一个基础的java和kotlin混编&#xff0c;和大家总结分享一下。 首先需要了解什么事kotlin&#xff0c;kotlin…

如何使用腾讯云轻量应用服务器挂载 CFS 文件系统

文件存储&#xff08;Cloud File Storage&#xff0c;CFS&#xff09;提供了可扩展的共享文件存储服务&#xff0c;可与腾讯云云服务器 、容器、批量计算、轻量应用服务器等服务搭配使用。CFS 提供了标准的 NFS 及 CIFS/SMB 文件系统访问协议&#xff0c;可为计算服务提供共享的…

【Unity】【Pico】手柄摇杆控制第一人称移动和旋转

【Unity】【Pico】手柄摇杆控制第一人称移动和旋转 背景&#xff1a;开发影院系统 环境&#xff1a;Unity2021.3、PicoNeo3ProEye 描述&#xff1a;已经在Unity项目中实现第一人称WASD移动和鼠标旋转&#xff08;代码见我的其他博文&#xff09; 需求&#xff1a;希望项目在Pi…

Cobalt Strike Beacon 初探

背景 RTO I 的课程结束了&#xff0c;Cobalt Strike 算是会用了。然后继上一篇文章之后&#xff0c;我还没有机会用 Cobalt Strike Beacon 做一下 Windows Defender Bypass。之后会写。 另外&#xff0c;我也想问一下我自己&#xff0c;Cobalt Strike 里面最基本的 payload -…

Springboot+Netty实现基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-应用订阅端(北向应用)

之前实现了使用SpringbootNetty基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-设备终端&#xff08;南向设备&#xff09;&#xff0c;模拟设备发送的数据给到了AIOT平台&#xff0c;那么在第三方应用也需要去订阅AIOT平台的数据&#xff0c;以及对设备进行下发指令(消…

FastGithub的下载和使用

前言 github访问很不稳定&#xff0c;时断时续&#xff0c;有时候根本打不开&#xff01; 下载 方式一&#xff1a;官方地址下载&#xff08;有及时更新&#xff09; FastGithub1.1.7下载、FastGithub2.1.4_windows、FastGithub2.1.4_Linux、 更多 方式二&#xff1a;本地上传…

[编程语言][C++][Qt]单独添加UI文件

单独添加UI文件问题描述解决方案1. 添加UI文件2. 与对应的界面类进行关联3. 修改UI文件4. 设置界面类读取UI文件总结问题描述 不知什么原因&#xff0c;Qt Creator并不是很完美很智能。当先写好界面类的头文件和源代码文件后&#xff0c;我们再添加用于可视化界面设计的UI文件…

美国顶级在线教育平台泄露22TB数据

©网络研究院 事件发生时&#xff0c;属于美国“三大”教育出版商之一的麦格劳希尔教育(McGraw Hill) 的两个配置错误的 AWS S3 存储桶在没有任何安全认证的情况下暴露在外。 vpnMentor 的网络安全研究人员发现了几个配置错误的 Amazon Web Services (AWS) S3 存储桶&…

RV1126笔记二十一:车辆颜色识别

若该文为原创文章,转载请注明原文出处。 一、介绍 在学习RV1126的过程中,测试了yolov5可以实现物体检测,物体目标识别等功能,Rock-X也自带了车牌识别功能,具体可以了解下正点原子的资料,里面有详细的介绍,这里介绍一个如何识别车辆颜色。只是提供一个思路,效果不是很…

RabbitMQ 第一天 基础 4 RabbitMQ 的工作模式 4.4 Topic 通配符模式 4.5 工作模式总结

RabbitMQ 【黑马程序员RabbitMQ全套教程&#xff0c;rabbitmq消息中间件到实战】 文章目录RabbitMQ第一天 基础4 RabbitMQ 的工作模式4.4 Topic 通配符模式4.4.1 模式说明4.4.2 代码编写4.4.3 小结4.5 工作模式总结第一天 基础 4 RabbitMQ 的工作模式 4.4 Topic 通配符模式 …

人工智能期末复习:人工神经网络(详细笔记和练习题)

文章目录1.概述2.基本单元-神经元3.激活函数3.1.阶跃函数3.2.Sigmoid函数3.3.TanH函数3.4.ReLU函数3.5.Softplus函数4.多层前馈神经网络5.损失函数5.1.均方误差5.2.交叉熵6.调参方法6.1.梯度下降法1.概述 神经网络定义&#xff1a;神经网络是具有适应性的简单单元组成的广泛并…