JavaWeb-04-Web后端基础(SpringBootWeb、HTTP协议、分层解耦、IOC和DI)
目录
一、SpringBootWeb入门
1.1 概述
1.2 入门程序
1.2.1 需求
1.2.2 开发步骤
1.3 入门解析
二、HTTP协议
2.1 HTTP概述
2.1.1 介绍
2.1.2 特点
2.2 HTTP请求协议
2.2.1 介绍
2.2.2 获取请求数据
2.3 HTTP响应协议
2.3.1 格式介绍
2.3.2 响应状态码
2.3.3 设置响应数据
三、SpringBootWeb案例
3.1 需求说明
3.2 代码实现
3.3 @ResponseBody
四、分层解耦
4.1 三层架构
4.1.1 介绍
4.1.2 代码拆分
4.2 分层解耦
4.2.1 问题分析
4.2.2 解耦思路
4.3 IOC&DI入门
4.4 IOC详解
4.4.1 Bean的声明
4.4.2 组件扫描
4.5 DI详解
4.5.1 @Autowired用法
4.5.2 注意事项
静态资源: 服务器上存储的不会改变的数据,通常不会根据用户的请求而变化。比如: HTML 、 CSS 、 JS 、图片、视频等 ( 负责页面展示 )动态资源:服务器端根据用户请求和其他数据动态生成的,内容可能会在每次请求时都发生变化。比如: Servlet 、 JSP 等 ( 负责逻辑处理)(注:现在基本使用Spring框架 )B/S 架构: Browser/Server ,浏览器 / 服务器架构模式。客户端只需浏览器,应用程序的逻辑和数据都存在服务器端。 ( 维护方便 体验一般 )C/S 架构 : Client/Server ,客户端 / 服务器架构模式。需要单独开发维护客户端。( 体验不错 开发维护麻烦 )
一、SpringBootWeb入门
1.1 概述
Spring家族旗下这么多的技术,最基础、最核心的是 SpringFramework。其他的spring家族的技术,都是基于SpringFramework的,SpringFramework中提供很多实用功能,如:依赖注入、事务管理、web开发支持、数据访问、消息服务等等。
直接基于SpringBoot进行项目构建和开发,不仅是Spring官方推荐的方式,也是现在企业开发的主流。
1.2 入门程序
1.2.1 需求
需求:基于SpringBoot的方式开发一个web应用,浏览器发起请求/hello后,给浏览器返回字符串 "Hello xxx ~"。
1.2.2 开发步骤
第1步:创建SpringBoot工程,并勾选Web开发相关依赖
第2步:定义HelloController类,添加方法hello,并添加注解
1)创建SpringBoot工程(需要联网) (案例是在已经创建的空项目上进行的,空项目创建见上一章3.1)
注意!注意!注意! 如果下载失败可能是网络问题,建议更换成阿里云
https://start.aliyun.com/
下图中该类叫做启动类(可简单理解为主类)
2)定义HelloController类,添加方法hello,并添加注解
在下图路径新建一个类
上图类中的代码如下:
package com.orange.springbootweb_quickstart;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController //表示当前类是一个请求处理类
public class HelloController {@RequestMapping("/hello")public String sayHello(String name) {System.out.println("name: "+ name);return "Hello " + name+ "~~~~" ;}}
接着到启动类中选择debug的方式进行运行(如下图)
在控制台可以看到以下内容(如下图)(可以看到端口是8080)
接下来在浏览器中输入下图内容并按下回车
1.3 入门解析
1). 为什么一个main方法就可以将Web应用启动了?
因为我们在创建springboot项目的时候,选择了web开发的起步依赖 spring-boot-starter-web
。而spring-boot-starter-web
依赖,又依赖了spring-boot-starter-tomcat
,由于maven的依赖传递特性,那么在我们创建的springboot项目中也就已经有了tomcat的依赖,这个其实就是springboot中内嵌的tomcat。(tomcat:web服务器)
而我们运行引导类中的main方法,其实启动的就是springboot中内嵌的Tomcat服务器。 而我们所开发的项目,也会自动的部署在该tomcat服务器中,并占用8080端口号 。(如下图)
起步依赖:
一种为开发者提供简化配置和集成的机制,使得构建Spring应用程序更加轻松。起步依赖本质上是一组预定义的依赖项集合,它们一起提供了在特定场景下开发Spring应用所需的所有库和配置。
spring-boot-starter-web:包含了web应用开发所需要的常见依赖。
spring-boot-starter-test:包含了单元测试所需要的常见依赖。
官方提供的starter:https://docs.spring.io/spring-boot/docs/3.1.3/reference/htmlsingle/#using.build-systems.starters
二、HTTP协议
2.1 HTTP概述
2.1.1 介绍
概念:Hyper Text Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则。
http是互联网上应用最为广泛的一种网络协议
http协议要求:浏览器在向服务器发送请求数据时,或是服务器在向浏览器发送响应数据时,都必须按照固定的格式进行数据传输
2.1.2 特点
基于TCP协议: 面向连接,安全
TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全
基于请求-响应模型: 一次请求对应一次响应(先请求后响应)
请求和响应是一一对应关系,没有请求,就没有响应
HTTP协议是无状态协议: 对于数据没有记忆能力。每次请求-响应都是独立的
无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。
缺点: 多次请求间不能共享数据
优点: 速度快
请求之间无法共享数据会引发的问题:
如:京东购物。加入购物车和去购物车结算是两次请求
由于HTTP协议的无状态特性,加入购物车请求响应结束后,并未记录加入购物车是何商品
发起去购物车结算的请求后,因为无法获取哪些商品加入了购物车,会导致此次请求无法正确展示数据
具体使用的时候,我们发现京东是可以正常展示数据的,原因是Java早已考虑到这个问题,并提出了使用会话技术(Cookie、Session)来解决这个问题。具体如何来做,我们后面课程中会讲到。
2.2 HTTP请求协议
2.2.1 介绍
请求协议:浏览器将数据以请求格式发送到服务器。包括:请求行、请求头 、请求体
GET方式的请求协议:
请求行(以上图中红色部分) :HTTP请求中的第一行数据。由:
请求方式
、资源路径
、协议/版本
组成(之间使用空格分隔)
请求方式:GET
资源路径:/brand/findAll?name=OPPO&status=1
请求路径:/brand/findAll
请求参数:name=OPPO&status=1
请求参数是以key=value形式出现
多个请求参数之间使用
&
连接请求路径和请求参数之间使用
?
连接协议/版本:HTTP/1.1
请求头(以上图中黄色部分) :第二行开始,上图黄色部分内容就是请求头。格式为key: value形式
http是个无状态的协议,所以在请求头设置浏览器的一些自身信息和想要响应的形式。这样服务器在收到信息后,就可以知道是谁,想干什么了
-
常见的HTTP请求头有:
举例说明:服务端可以根据请求头中的内容来获取客户端的相关信息,有了这些信息服务端就可以处理不同的业务需求。
比如:
不同浏览器解析HTML和CSS标签的结果会有不一致,所以就会导致相同的代码在不同的浏览器会出现不同的效果
服务端根据客户端请求头中的数据获取到客户端的浏览器类型,就可以根据不同的浏览器设置不同的代码来达到一致的效果(这就是我们常说的浏览器兼容问题)
请求体 :存储请求参数
GET请求的请求参数在请求行中,故不需要设置请求体
POST方式的请求协议:
请求行(以上图中红色部分):包含请求方式、资源路径、协议/版本
请求方式:POST
资源路径:/brand
协议/版本:HTTP/1.1
请求头(以上图中黄色部分)
请求体(以上图中绿色部分) :存储请求参数
请求体和请求头之间是有一个空行隔开(作用:用于标记请求头结束)
GET请求和POST请求的区别:
2.2.2 获取请求数据
Web服务器(Tomcat)对HTTP协议的请求数据进行解析,并进行了封装(HttpServletRequest),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷。
代码演示如下(项目创建看1.2入门程序):
代码如下:
package com.orange.springbootweb_quickstart;import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RequestController {/*** 请求路径 http://localhost:8080/request?name=橙序研&age=22* @param request* @return*/@RequestMapping("/request")public String request(HttpServletRequest request){//1.获取请求参数 name, ageString name = request.getParameter("name");String age = request.getParameter("age");System.out.println("name = " + name + ", age = " + age);//2.获取请求路径String uri = request.getRequestURI();String url = request.getRequestURL().toString();System.out.println("uri = " + uri);System.out.println("url = " + url);//3.获取请求方式String method = request.getMethod();System.out.println("method = " + method);//4.获取请求头String header = request.getHeader("User-Agent");System.out.println("header = " + header);return "request success";}}
2.3 HTTP响应协议
2.3.1 格式介绍
-
响应行(以上图中红色部分):响应数据的第一行。响应行由
协议及版本
、响应状态码
、状态码描述
组成-
协议/版本:HTTP/1.1
-
响应状态码:200
-
状态码描述:OK
-
-
响应头(以上图中黄色部分):响应数据的第二行开始。格式为key:value形式
-
http是个无状态的协议,所以可以在请求头和响应头中设置一些信息和想要执行的动作,这样,对方在收到信息后,就可以知道你是谁,你想干什么
-
常见的HTTP响应头有(如下):
-
Content-Type:表示该响应内容的类型,例如text/html,image/jpeg ;Content-Length:表示该响应内容的长度(字节数);Content-Encoding:表示该响应压缩算法,例如gzip ;Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒 ;Set-Cookie: 告诉浏览器为当前页面所在的域设置cookie ;
-
响应体(以上图中绿色部分): 响应数据的最后一部分。存储响应的数据
-
响应体和响应头之间有一个空行隔开(作用:用于标记响应头结束)
-
2.3.2 响应状态码
重定向解释:先向A服务器发出请求,但是A没有却提供了Location,于是浏览器会再次向B服务器发出请求(但其实用户并不知道)
可以看到设置了状态码302,并且设置了Location,于是便进行重定向到Location(如下图)
下图可以看出,重定向后的状态码为成功了
常见的如下:
200 ok
客户端请求成功
404 Not Found
请求资源不存在
500 Internal Server Error
服务端发生不可预期的错误
状态码大全:
2.3.3 设置响应数据
Web服务器对HTTP协议的响应数据进行了封装(HttpServletResponse),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷。
方式一(项目文件管理参考2.2.2中的例子):
代码如下:
package com.orange.springbootweb_quickstart;import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.IOException;@RestController
public class ResponseController {@RequestMapping("response")public void response(HttpServletResponse response) throws IOException {//1. 设置响应状态码response.setStatus(401);//2. 设置响应头response.setHeader("name","orange");//3. 设置响应体response.getWriter().write("<h1>hello response</h1>");}
}
方式二:
代码如下:
package com.orange.springbootweb_quickstart;import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.IOException;@RestController
public class ResponseController {@RequestMapping("response")public void response(HttpServletResponse response) throws IOException {//1. 设置响应状态码response.setStatus(401);//2. 设置响应头response.setHeader("name","orange");//3. 设置响应体response.getWriter().write("<h1>hello response</h1>");}// 方式二@RequestMapping("/response2")public ResponseEntity<String> response2(){return ResponseEntity.status(401).header("name","orange2").body("<h1>hello response2</h1>");}}
响应状态码 和 响应头如果没有特殊要求的话,通常不手动设定。服务器会根据请求处理的逻辑,自动设置响应状态码和响应头。
三、SpringBootWeb案例
3.1 需求说明
需求:基于SpringBoot开发web程序,完成用户列表的渲染展示
当在浏览器地址栏,访问前端静态页面(http://localhost:8080/user.html)后,在前端页面上,会发送ajax请求,请求服务端(http://localhost:8080/list),服务端程序加载 user.txt 文件中的数据,读取出来后最终给前端页面响应json格式的数据,前端页面再将数据渲染展示在表格中。
3.2 代码实现
1). 准备工作:再创建一个SpringBoot工程(见1.2.2),并勾选web依赖、lombok依赖。
在pom文件中在引入以下依赖(记得刷新)
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.22</version></dependency>
2)资料准备如下图:
四个准备文件内容如下(链接: https://pan.baidu.com/s/1Q8TpWxIL-Q44Q05fJIOHgA?pwd=0706 提取码: 0706 )
准备一个txt文件,内容如下
1,daqiao,1234567890,大乔,22,2024-07-15 15:05:45
2,xiaoqiao,1234567890,小乔,18,2024-07-15 15:12:09
3,diaochan,1234567890,貂蝉,21,2024-07-15 15:07:16
4,lvbu,1234567890,吕布,28,2024-07-16 10:05:15
5,zhaoyun,1234567890,赵云,27,2024-07-16 11:03:28
6,zhangfei,1234567890,张飞,31,2024-07-16 11:03:28
7,guanyu,1234567890,关羽,34,2024-07-16 12:05:12
8,liubei,1234567890,刘备,37,2024-07-16 15:03:28
3)创建代码如下
创建一个Java文件(如下图):
User代码如下:
package com.orange.springbootweb_001.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDateTime;@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private Integer id;private String username;private String password;private String name;private Integer age;private LocalDateTime updateTime;}
如果报以下错误:
java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid'
解决方法一:更新lombok 版本
解决方法二:使用以下代码:
package com.orange.springbootweb_001.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDateTime;public class User {private Integer id;private String username;private String password;private String name;private Integer age;private LocalDateTime updateTime;public User(Integer id, String username, String password, String name, Integer age, LocalDateTime updateTime) {this.id = id;this.username = username;this.password = password;this.name = name;this.age = age;this.updateTime = updateTime;}public LocalDateTime getUpdateTime() {return updateTime;}public void setUpdateTime(LocalDateTime updateTime) {this.updateTime = updateTime;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}}
再创建如下包和类(如下图)
代码如下:
package com.orange.springbootweb_001.controller;import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;// 用户信息Controller
@RestController
public class UserController {@RequestMapping("/list")public List<User> list() throws FileNotFoundException {//1. 加载并读取user.txt文件,获取用户数据//InputStream in = new FileInputStream("src/main/resources/user.txt");InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());//2. 解析用户信息,封装为User对象 ->list集合List<User> userList = lines.stream().map(line ->{String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id,username,password,name,age,updateTime);}).toList();//3. 返回数据(json)return userList;}
}
完整项目分享:
通过网盘分享的文件:springbootweb_001
链接: https://pan.baidu.com/s/1Xth0setM-nuRjKjqqw4HEA?pwd=0706 提取码: 0706
--来自百度网盘超级会员v4的分享
3.3 @ResponseBody
前面我们学习过HTTL协议的交互方式:请求响应模式(有请求就有响应)。那么Controller程序呢,除了接收请求外,还可以进行响应。
在我们前面所编写的controller方法中,都已经设置了响应数据。
controller方法中的return的结果,怎么就可以响应给浏览器呢?
答案:使用@ResponseBody注解
@ResponseBody注解:
-
类型:方法注解、类注解
-
位置:书写在Controller方法上或类上
-
作用:将方法返回值直接响应给浏览器,如果返回值类型是实体对象/集合,将会转换为JSON格式后在响应给浏览器
但是在我们所书写的Controller中,只在类上添加了@RestController注解、方法添加了@RequestMapping注解,并没有使用@ResponseBody注解,怎么给浏览器响应呢?
这是因为,我们在类上加了@RestController注解,而这个注解是由两个注解组合起来的,分别是:@Controller 、@ResponseBody。 那也就意味着,我们在类上已经添加了@ResponseBody注解了,而一旦在类上加了@ResponseBody注解,就相当于该类所有的方法中都已经添加了@ResponseBody注解。
提示:前后端分离的项目中,一般直接在请求处理类上加@RestController注解,就无需在方法上加@ResponseBody注解了
四、分层解耦
4.1 三层架构
4.1.1 介绍
在我们进行程序设计以及程序开发时,尽可能让每一个接口、类、方法的职责更单一些(单一职责原则)。
单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。
这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利于后期的维护。
我们之前开发的程序呢,并不满足单一职责原则:
那其实我们上述案例的处理逻辑呢,从组成上看可以分为三个部分:
数据访问:负责业务数据的维护操作,包括增、删、改、查等操作。
逻辑处理:负责业务逻辑处理的代码。
请求处理、响应数据:负责,接收页面的请求,给页面响应数据。
按照上述的三个组成部分,在我们项目开发中呢,可以将代码分为三层,如下图所示:
Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
Service:业务逻辑层。处理具体的业务逻辑。
Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。
4.1.2 代码拆分
完整项目:通过网盘分享的文件:springbootweb_001_拆分项目
链接: https://pan.baidu.com/s/1_ZrT8gsT24huK2aqKRQvrg?pwd=0706 提取码: 0706
--来自百度网盘超级会员v4的分享
我们使用三层架构思想,来改造下之前的程序:
控制层包名:
com.
orange.controller
业务逻辑层包名:
com.
orange.service
数据访问层包名:
com.
orange.dao
项目架构如下图所示:
(下面顺序倒着来,好理解一点)
3). 数据访问层:负责数据的访问操作,包含数据的增、删、改、查
在 com.orange.dao
中创建UserDao接口,代码如下:
package com.orange.springbootweb_001.dao;import java.util.List;public interface UserDao {public List<String> findAll();
}
在 com.orange.dao.impl
中创建UserDaoImpl接口,代码如下
package com.orange.springbootweb_001.dao.impl;import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.dao.UserDao;import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;public class UserDaoImpl implements UserDao {public List<String> findAll() {//1. 加载并读取user.txt文件,获取用户数据//InputStream in = new FileInputStream("src/main/resources/user.txt");InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());return lines;}
}
2). 业务逻辑层:处理具体的业务逻辑
在 com.orange.service
中创建UserSerivce接口,代码如下:
package com.orange.springbootweb_001.service;import com.orange.springbootweb_001.pojo.User;import java.util.List;public interface UserService {public List<User> findAll();
}
在 com.orange.service.impl
中创建UserSerivceImpl接口,代码如下:
package com.orange.springbootweb_001.service.impl;import com.orange.springbootweb_001.dao.UserDao;
import com.orange.springbootweb_001.dao.impl.UserDaoImpl;
import com.orange.springbootweb_001.pojo.User;
import com.orange.springbootweb_001.service.UserService;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;public class UserServiceImpl implements UserService {//调用Dao,获取数据private UserDao userDao = new UserDaoImpl();@Overridepublic List<User> findAll() {List<String> lines = userDao.findAll();//2. 解析用户信息,封装为User对象 ->list集合List<User> userList = lines.stream().map(line ->{String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id,username,password,name,age,updateTime);}).toList();return userList;}
}
1). 控制层:接收前端发送的请求,对请求进行处理,并响应数据
在 com.orange.controller
中创建UserController类,代码如下:
package com.orange.springbootweb_001.controller;import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.pojo.User;
import com.orange.springbootweb_001.service.UserService;
import com.orange.springbootweb_001.service.impl.UserServiceImpl;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;// 用户信息Controller
@RestController
public class UserController {// 调用service,获取数据private UserService userService = new UserServiceImpl();@RequestMapping("/list")public List<User> list() throws FileNotFoundException {//3. 返回数据(json)return userService.findAll();}
}/*package com.orange.springbootweb_001.controller;import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;// 用户信息Controller
@RestController
public class UserController {@RequestMapping("/list")public List<User> list() throws FileNotFoundException {//1. 加载并读取user.txt文件,获取用户数据//InputStream in = new FileInputStream("src/main/resources/user.txt");InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());//2. 解析用户信息,封装为User对象 ->list集合List<User> userList = lines.stream().map(line ->{String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id,username,password,name,age,updateTime);}).toList();//3. 返回数据(json)return userList;}
}*/
4.2 分层解耦
4.2.1 问题分析
由于我们现在在程序中,需要什么对象,直接new一个对象 new UserServiceImpl(),如下图
如果说我们需要更换实现类,比如由于业务的变更,UserServiceImpl 不能满足现有的业务需求,我们需要切换为 UserServiceImpl2 这套实现,就需要修改Contorller的代码,需要创建 UserServiceImpl2 的实现new UserServiceImpl2(),如下图
Service中调用Dao,也是类似的问题。这种呢,我们就称之为层与层之间 耦合 了。 那什么是耦合呢 ?
首先需要了解软件开发涉及到的两个概念:内聚和耦合。
-
内聚:软件中各个功能模块内部的功能联系。
-
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合。
高内聚:指的是一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 "高内聚"。
低耦合:指的是软件中各个层、模块之间的依赖关联程序越低越好。
4.2.2 解耦思路
1)将要用到的对象交给一个容器管理。
2). 应用程序中用到这个对象,就直接从容器中获取
想要实现上述解耦操作,就涉及到Spring中的两个核心概念:
控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器。
依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
程序运行时需要某个资源,此时容器就为其提供这个资源。
例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象。
bean对象:IOC容器中创建、管理的对象,称之为:bean对象。
4.3 IOC&DI入门
1). 将Service及Dao层的实现类,交给IOC容器管理
在实现类加上 @Component
注解,就代表把当前类产生的对象交给IOC容器管理。
A. UserDaoImpl
package com.orange.springbootweb_001.dao.impl;import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.dao.UserDao;
import org.springframework.stereotype.Component;import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;@Component
public class UserDaoImpl implements UserDao {public List<String> findAll() {//1. 加载并读取user.txt文件,获取用户数据//InputStream in = new FileInputStream("src/main/resources/user.txt");InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());return lines;}
}
B. UserServiceImpl
package com.orange.springbootweb_001.service.impl;import com.orange.springbootweb_001.dao.UserDao;
import com.orange.springbootweb_001.dao.impl.UserDaoImpl;
import com.orange.springbootweb_001.pojo.User;
import com.orange.springbootweb_001.service.UserService;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;@Component
public class UserServiceImpl implements UserService {//调用Dao,获取数据private UserDao userDao = new UserDaoImpl();@Overridepublic List<User> findAll() {List<String> lines = userDao.findAll();//2. 解析用户信息,封装为User对象 ->list集合List<User> userList = lines.stream().map(line ->{String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id,username,password,name,age,updateTime);}).toList();return userList;}
}
2). 为Controller 及 Service注入运行时所依赖的对象
加入注解:@Autowired,表示程序在运行的时候会自动从容器中在到这个类型的bean对象,并赋值给成员变量
A. UserServiceImpl
package com.orange.springbootweb_001.service.impl;import com.orange.springbootweb_001.dao.UserDao;
import com.orange.springbootweb_001.dao.impl.UserDaoImpl;
import com.orange.springbootweb_001.pojo.User;
import com.orange.springbootweb_001.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;@Component
public class UserServiceImpl implements UserService {//调用Dao,获取数据@Autowiredprivate UserDao userDao;@Overridepublic List<User> findAll() {List<String> lines = userDao.findAll();//2. 解析用户信息,封装为User对象 ->list集合List<User> userList = lines.stream().map(line ->{String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id,username,password,name,age,updateTime);}).toList();return userList;}
}
B. UserController
package com.orange.springbootweb_001.controller;import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.pojo.User;
import com.orange.springbootweb_001.service.UserService;
import com.orange.springbootweb_001.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;// 用户信息Controller
@RestController
public class UserController {// 调用service,获取数据@Autowiredprivate UserService userService ;@RequestMapping("/list")public List<User> list() throws FileNotFoundException {//3. 返回数据(json)return userService.findAll();}
}/*package com.orange.springbootweb_001.controller;import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;// 用户信息Controller
@RestController
public class UserController {@RequestMapping("/list")public List<User> list() throws FileNotFoundException {//1. 加载并读取user.txt文件,获取用户数据//InputStream in = new FileInputStream("src/main/resources/user.txt");InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());//2. 解析用户信息,封装为User对象 ->list集合List<User> userList = lines.stream().map(line ->{String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id,username,password,name,age,updateTime);}).toList();//3. 返回数据(json)return userList;}
}*/
启动服务,运行测试。 打开浏览器,地址栏直接访问:http://localhost:8080/user.html 。 依然正常访问,就说明入门程序完成了。 已经完成了层与层之间的解耦。
4.4 IOC详解
4.4.1 Bean的声明
要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一:
注意1:声明bean的时候,可以通过注解的value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
注意2:使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。
那么此时,我们就可以使用 @Service
注解声明Service层的bean。 使用 @Repository
注解声明Dao层的bean。 代码实现如下:
Service层:
package com.orange.springbootweb_001.service.impl;import com.orange.springbootweb_001.dao.UserDao;
import com.orange.springbootweb_001.dao.impl.UserDaoImpl;
import com.orange.springbootweb_001.pojo.User;
import com.orange.springbootweb_001.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;@Service
public class UserServiceImpl implements UserService {//调用Dao,获取数据@Autowiredprivate UserDao userDao;@Overridepublic List<User> findAll() {List<String> lines = userDao.findAll();//2. 解析用户信息,封装为User对象 ->list集合List<User> userList = lines.stream().map(line ->{String[] parts = line.split(",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id,username,password,name,age,updateTime);}).toList();return userList;}
}
Dao层:
package com.orange.springbootweb_001.dao.impl;import cn.hutool.core.io.IoUtil;
import com.orange.springbootweb_001.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;@Repository
public class UserDaoImpl implements UserDao {public List<String> findAll() {//1. 加载并读取user.txt文件,获取用户数据//InputStream in = new FileInputStream("src/main/resources/user.txt");InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8,new ArrayList<>());return lines;}
}
4.4.2 组件扫描
问题:使用前面学习的四个注解声明的bean,一定会生效吗?
答案:不一定。(原因:bean想要生效,还需要被组件扫描)
-
前面声明bean的四大注解,要想生效,还需要被组件扫描注解
@ComponentScan
扫描。 -
该注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解
@SpringBootApplication
中,默认扫描的范围是启动类所在包及其
所以,我们在项目开发中,只需要按照如上项目结构,将项目中的所有的业务类,都放在启动类所在包的子包中,就无需考虑组件扫描问题。
4.5 DI详解
4.5.1 @Autowired用法
依赖注入,是指IOC容器要为应用程序去提供运行时所依赖的资源,而资源指的就是对象。
在入门程序案例中,我们使用了@Autowired这个注解,完成了依赖注入的操作,而这个Autowired翻译过来叫:自动装配。
@Autowired
注解,默认是按照类型进行自动装配的(去IOC容器中找某个类型的对象,然后完成注入操作)
在项目开发中,基于@Autowired进行依赖注入时,基本都是第一种和第二种方式。(官方推荐第二种方式,因为会更加规范)但是在企业项目开发中,很多的项目中,也会选择第一种方式因为更加简洁、高效(在规范性方面进行了妥协)。
4.5.2 注意事项
那如果在IOC容器中,存在多个相同类型的bean对象
在下面的例子中,我们准备了两个UserService的实现类,并且都交给了IOC容器管理。 代码如下:
此时,我们启动项目会发现,控制台报错了:
出现错误的原因呢,是因为在Spring的容器中,UserService这个类型的bean存在两个,框架不知道具体要注入哪个bean使用,所以就报错了。
如何解决上述问题呢?Spring提供了以下几种解决方案:
-
@Primary
-
@Qualifier
-
@Resource
@Autowird 与 @Resource的区别
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
@Autowired 默认是按照类型注入,而@Resource是按照名称注入
相关文章:
JavaWeb-04-Web后端基础(SpringBootWeb、HTTP协议、分层解耦、IOC和DI)
目录 一、SpringBootWeb入门 1.1 概述 1.2 入门程序 1.2.1 需求 1.2.2 开发步骤 1.3 入门解析 二、HTTP协议 2.1 HTTP概述 2.1.1 介绍 2.1.2 特点 2.2 HTTP请求协议 2.2.1 介绍 2.2.2 获取请求数据 2.3 HTTP响应协议 2.3.1 格式介绍 2.3.2 响应状态码 2.3…...
随笔 20250413 Elasticsearch 的 term 查询
你这个问题非常经典,来自于 Elasticsearch 的 term 查询是 ✅精确匹配(case-sensitive,大小写敏感)! 🧨 为什么查不到 "World"? 你的查询语句是: GET /movie/_search {&…...
zk(Zookeeper)实现分布式锁
Zookeeper实现分布式锁 1,zk中锁的种类: 读锁:大家都可以读,要想上读锁的前提:之前的锁没有写锁 写锁:只有得到写锁的才能写。要想上写锁的前提是:之前没有任何锁 2,zk如何上读锁 创…...
操作系统简要概述
操作系统是计算机系统的核心软件,它管理和控制计算机硬件与软件资源,为用户提供方便、高效、安全的使用环境。以下是关于操作系统的详细介绍: 一、定义 操作系统(Operating System,简称 OS)是计算机硬件与…...
开漏模式的触发条件和工作状态
MOS管的漏-栅-源三极 漏极开路--开漏 电路整体概述 这是开漏(Open - Drain)电路结构,核心由输出控制模块和一对互补的MOS管(P - MOS和N - MOS)组成。开漏电路的特点是MOS管漏极开路,这种结构常用于需要实现…...
【Java学习笔记】Java第一课,梦开始的地方!!!
目录 1. 基本内容介绍和软件安装 2. 快速入门之第一个程序 hello world 3. 学习方法 基本介绍和软件安装 一、 Java 岗位与应用场景 说明:java 基础也称javaSE 岗位 1. javaEE 软件工程师 电商领域 团购 众筹 sns(社交网络) 教育 金…...
共享内存技术
一、共享内存 共享内存是一种高效的 进程间通信(IPC) 机制,允许多个进程直接访问同一块物理内存区域,无需通过内核缓冲区或文件进行数据拷贝。它通常用于需要 低延迟、高吞吐量 的数据交换场景(如实时系统、高频交易、…...
Lc 大数运算--快速幂 | 统计好数字的数目
快速幂算法是一种高效计算大数幂运算的方法,能将时间复杂度从传统算法的O(n)降低到O(log n)。它的核心思想是 分解指数分治思想,类似生活中的「拆快递」—— 把大包裹拆成小份,分批处理更高效。 一、生活示例:存钱罐的复利计算 假…...
Linux内存管理架构(2)
4.虚拟地址空间布局 4.1虚拟地址空间划分 对于64位处理器,目前不支持完全的64位虚拟地址 1.ARM64内核/用户虚拟地址划分 1. 虚拟地址的最大宽度 最大宽度:虚拟地址的最大宽度是48位。 内核虚拟地址: 在64位地址空间的顶部。高16位全是1。范围…...
图论基础理论
在我看来,想要掌握图的基础应用,仅需要三步走。 什么是图(基本概念)、图的构造(打地基)、图的遍历方式(应用的基础) 只要能OK的掌握这三步、就算图论入门了!࿰…...
最大子序和问题——动态规划/贪心算法解决
目录 一:问题描述 二:解决思路1——动态规划思想 三:C 语言代码实现 四:复杂度分析 五:解决思路2——贪心算法思想 六:具体步骤 七: C语言代码实现 八:复杂度分析 一:问题描述 …...
车载以太网-SOMEIP
文章目录 基本概念SOME/IP的起源与核心定位核心定位设计目标协议栈架构与OSI模型映射报文结构与数据序列化SOME/IP的核心通信机制通信模式分类服务发现协议(SOME/IP-SD)服务发现流程服务质量(QoS)管理SOME/IP在智能汽车中的典型应用SOME/IP测试与验证体系SOME/IP测试环境构…...
DrissionPage详细教程
1. 基本概述 DrissionPage 是一个基于 python 的网页自动化工具。它既能控制浏览器,也能像requests一样收发数据包,更重要的是还能把两者合二为一。因此,简单来说DrissionPage可兼顾浏览器自动化的便利性和 requests 的高效率。 DrissionPa…...
6.1 GitHub亿级数据采集实战:双通道架构+三级容灾设计,破解API限制与反爬难题
GitHub 项目数据获取功能设计与实现 关键词:GitHub API 集成、网页爬虫开发、数据存储设计、定时任务调度、异常处理机制 1. 数据获取架构设计 采用双通道数据采集策略,同时使用 GitHub 官方 API 和网页爬虫技术确保数据完整性: #mermaid-svg-XUg7xhHrzFAozG4J {font-fami…...
LabVIEW 控制电机需注意的关键问题
在自动化控制系统中,LabVIEW 作为图形化编程平台,因其高度可视化、易于集成硬件等优势,被广泛应用于电机控制场景。然而,要实现稳定、精确、高效的电机控制,仅有软件并不足够,还需结合硬件选型、控制逻辑设…...
Linux系统远程操作和程序编译
目录 一、Linux远程终端登录、图形桌面访问、 X图形窗口访问和FTP文件传输操作 1.1 桥接模式 1.2 putty远程登录Ubuntu 1.3 win10远程登录并上传下载文件 1.4 X server仿真软件安装 1.5 树莓派在putty上的远程登录 1.6 使用ftp远程登录并实现文件上传下载 1.7 Linux下的…...
Mac配置开发环境
博主是一名Python后端开发,有时候环境太多 需要配置太多,故做此文章 环境Macbook ,请注意自己的是ARM 还是x86 结构 Vscode/Cursor配置Python debug 配置Debug launch.json {"version": "0.2.0","configuratio…...
LabVIEW配电器自动测试系统
随着航天技术的迅猛发展,航天器供配电系统的结构越来越复杂,对配电器的功能完整性、稳定性和可靠性提出了更高要求。传统人工测试方式难以满足高效率、高精度、可重复的测试需求。本项目开发了一套基于LabVIEW平台的宇航配电器自动测试系统,融…...
生成与强化学习:赋予VLA系统物理行动能力
引言:从“理解世界”到“改变世界” 当机器能够“看懂”图像、“听懂”指令时,一个更根本的挑战浮现:如何让它们像人类一样,将认知转化为精准的物理动作?无论是机械臂抓取杯子,还是自动驾驶汽车紧急避障&a…...
基于Springboot+Mysql的闲一品(含LW+PPT+源码+系统演示视频+安装说明)
系统功能 管理员功能:首页、个人中心、用户管理、零食分类管理、零食信息管理、订单评价管理、系统管理、订单管理。用户功能:首页、个人中心、订单评价管理、我的收藏管理、订单管理。前台首页功能:首页、零食信息、零食资讯、个人中心、后…...
jupyter4.4安装使用
一、chrome谷歌浏览器 1. 安装 1.1 下载地址: 下载地址: https://www.google.cn/intl/zh-CN_ALL/chrome/fallback/ 2 插件markdown-viewer 2.1 下载地址: 下载地址:https://github.com/simov/markdown-viewer/releases 2.2…...
Linux虚拟内存详解
引言 虚拟内存是现代操作系统中的核心概念之一,它为进程提供了一个连续的、独立的地址空间,有效解决了物理内存限制问题,并大大简化了程序开发和执行。本文将深入探讨Linux系统中虚拟内存的工作原理、实现机制以及相关的内存管理技术&#x…...
数据库安装(基于Linux下centos7)(保姆级教程)
前言:笔者有段时间没写博客了,今天笔者要分享新的知识了,那就是数据库,笔者会通过博客系统的且通俗易懂的分享数据库知识,对于想要学习数据库和学习过数据库的老铁复习都是非常有用的,绝对干货满满,那么今天…...
【自动驾驶 机器人】速度规划 |梯形/S型速度曲线
参考文章: (1)【自动驾驶】运动规划丨速度规划丨T型/S型速度曲线 (2)一文教你快速搞懂速度曲线规划之S形曲线(超详细图文推导附件代码) 1 梯形速度曲线 如下图所示梯形速度/加速度/加加速度曲…...
Qt C++内存泄漏排查方法
在Qt C++中排查内存泄漏可以按照以下步骤进行,结合工具使用和代码审查: 1. 使用内存检测工具 Valgrind (Linux/macOS) 安装Valgrind:sudo apt-get install valgrind运行程序并检测内存泄漏:valgrind --leak-check=full ./your_qt_app分析输出结果,定位未释放的内存块。Dr…...
[redis进阶一]redis的持久化(2)AOF篇章
目录 一 为什么有了RDB持久化机制还要有AOF呢 板书介绍具体原因: 编辑二 详细讲解AOF机制 (1)AOF的基本使用 1)板书如下 2)开启AOF机制: 3) AOF工作流程 (2)AOF是否会影响到redis性能 编辑 (3)AOF缓冲区刷新策略 (4)AOF的重写机制 板书如下: 为什么要有这个重写机…...
聊天室项目day4(redis实现验证码期限,实现redis连接池)
1.redis连接池操作和之前所学过的io_context连接池原理一样这里不多赘述,也是创建多个连接,使用时按顺序取出来。 2.知识补充redisConnect()函数建立与 Redis 服务器的非阻塞网络连接,成功返回 redisContext*(连接上下文指针&…...
Redis之分布式锁
面试切入点 锁的分类 单机版同一个JVM虚拟机内,synchronized或者Lock接口分布式多个不同JVM虚拟机,单机的线程锁不再起作用,资源类在不同的服务器之间共享了 一个靠谱分布式锁需要具备的条件与刚需 独占性:onlyOneÿ…...
AF3 ProteinDataset类的__getitem__方法解读
AlphaFold3 protein_dataset 模块 ProteinDataset 类 __getitem__ 方法用于从数据集中获取一个条目,并根据配置对数据进行处理。 源代码: def __getitem__(self, idx):"""Return an entry from the dataset.If a clusters file is provided, then the idx i…...
NLP 梳理02 — 标点符号和大小写
文章目录 一、说明二、为什么文本预处理中需要小写2.1 为什么小写在文本预处理中至关重要?2.2 区分大小写对 NLP 任务的影响 三、删除标点符号及其对 NLP 任务的影响3.1 什么是标点符号?3.2 为什么在文本预处理中删除标点符号?3.3 删除标点符…...
HarmonyOS中的多线程并发机制
目录 多线程并发1. 多线程并发概述2 多线程并发模型3 TaskPool简介4 Worker简介4.1 Woker注意事项4.2 Woker基本用法示例 5. TaskPool和Worker的对比5.1 实现特点对比5.2 适用场景对比 多线程并发 1. 多线程并发概述 并发模型是用来实现不同应用场景中并发任务的编程模型&…...
游戏引擎学习第221天:(实现多层次过场动画)
资产: intro_art.hha 已发布 在下载页面,你会看到一个新的艺术包。你将需要这个艺术包来进行接下来的开发工作。这个艺术包是由一位艺术家精心制作并打包成我们设计的格式,旨在将这些艺术资源直接应用到游戏中。它包含了许多我们会在接下来的直播中使用…...
Python | 在Pandas中按照中值对箱形图排序
箱形图是可视化数据分布的强大工具,因为它们提供了对数据集内的散布、四分位数和离群值的洞察。然而,当处理多个组或类别时,通过特定的测量(如中位数)对箱形图进行排序可以提高清晰度并有助于揭示模式。在本文中&#…...
openapi + knife4j的使用
一、依赖作用与关系 1. springdoc-openapi-starter-webmvc-api • 核心功能: 基于 OpenAPI 3 规范,自动生成 API 文档元数据(JSON 格式),并集成 Spring MVC。 提供Tag Operation、Schema 等注解,支持通过…...
数据结构*包装类泛型
包装类 什么是包装类 在讲基本数据类型的时候,有提到过包装类。 基本数据类型包装类byteByteshortShortintIntegerlongLongfloatFloatdoubleDoublecharCharacterbooleanBoolean 我们知道:基本数据类型并不是对象,没有对象所具有的方法和属…...
Azure Synapse Dedicated SQL pool里大型表对大型表分批合并数据的策略
Azure Synapse Dedicated SQL pool中大型表的数据通过MERGE INTO语句合并到另一张大型表的时间很长,容易造成运行超时,而有的时候超时的时间是管理设置,由客户控制,无法修改。这种时候为了确保操作可以运行成功,需要将…...
Day81 | 灵神 | 快慢指针 链表的中间结点 环形链表
Day81 | 灵神 | 快慢指针 链表的中间结点 环形链表 876.链表的中间结点 876. 链表的中间结点 - 力扣(LeetCode) 思路: 设置两个指针,一个快指针r一个慢指针l 初始都是头结点 我们要求的是中间节点 所以快指针走两步&#x…...
【DDR 内存学习专栏 1.2 -- DDR Channel 介绍】
文章目录 1. DDR中的通道(Channel)概念1.1 DDR Channel 与 DDRC1.2 DIMM 内存插槽1.3 物理通道的定义1.3.1 多通道的作用 1.4 通道的硬件实现1.5 多核系统的DDR通道分配策略 1. DDR中的通道(Channel)概念 关于 DDR 通道ÿ…...
深入解析xDeepFM:结合压缩交互网络与深度神经网络的推荐系统新突破
今天是周日,我来解读一篇有趣的文章——xDeepFM。这篇文章由 Mao et al. 发表在SIGIR 2019会议。文章提出了一个新的网络模型——压缩交互网络(CIN),用于显式地学习高阶特征交互。通过结合 CIN 和传统的深度神经网络(D…...
Mybatis 中 <mappers> 标签四种配置方式
在MyBatis中,我们可以通过四种不同的方式来配置Mappers标签 : 1. 使用 <package name=""> 批量扫描包 这种方式通过指定一个包名,MyBatis 会自动扫描该包下的所有接口并注册为映射器。 <mappers><package name="com.example.mapper"/&…...
科技赋能记忆共生-郑州
故事背景 故事发生在中国河南郑州的现代城市环境中,这里描绘了人与科技的交融与共生。多样的场景展示了人与自然、历史与未来的互动,通过各种科技手段与古老文化相结合,展现出未来城市的独特魅力。 故事内容 在中国河南郑州,一座科…...
根据开始日期和结束日志统计共有多少天和每天的营业额
controller 重点:根据时间格式接受时间类型参数 DateTimeFormat(pattern "yyyy-MM-dd") LocalDateTime begin, DateTimeFormat(pattern "yyyy-MM-dd") LocalDateTime end) RestController RequestMapping("/admin/report") Slf4…...
LLMs之Agent之A2A:A2A的简介、安装和使用方法、案例应用之详细攻略
LLMs之Agent之A2A:A2A的简介、安装和使用方法、案例应用之详细攻略 目录 相关文章 LLMs之Agent之A2A:《Announcing the Agent2Agent Protocol (A2A)》翻译与解读 LLMs之Agent之A2A:A2A的简介、安装和使用方法、案例应用之详细攻略 A2A协议…...
深入学习OpenCV:第一章简介
本专栏为零基础开发者打造,聚焦OpenCV在Python中的高效应用,用100%代码实践带你玩转图像处理! 从 环境配置到实战项目,内容涵盖: 1️⃣ 基础篇:图像读写、阈值处理、色彩空间转换 2️⃣ 进阶篇ÿ…...
汉诺塔问题——用贪心算法解决
目录 一:起源 二:问题描述 三:规律 三:解决方案 递归算法 四:代码实现 复杂度分析 一:起源 汉诺塔(Tower of Hanoi)问题起源于一个印度的古老传说。在世界中心贝拿勒斯&#…...
【双指针】专题:LeetCode 283题解——移动零
移动零 一、题目链接二、题目三、题目解析四、算法原理两个指针的作用以及三个区间总结 五、与快速排序的联系六、编写代码七、时间复杂度、空间复杂度 一、题目链接 移动零 二、题目 三、题目解析 “保持非零元素的相对顺序”,比如,示例1中非零元素1…...
2025-4-12-C++ 学习 XOR 三元组 异或 急转弯问题
C的学习必须更加精进一些,对于好多的函数和库的了解必须深入一些。 文章目录 3513. 不同 XOR 三元组的数目 I题解代码 3514. 不同 XOR 三元组的数目 II题解代码 晚上,10点半,参加了LC的竞赛,ok了一道,哈哈~ 第二道…...
[MySQL] 索引
索引 1.为什么有索引?2.MySQL的存储(MySQL与磁盘交互的基本单位)3.小总结4.索引的进一步理解4.1测试案例4.2 理解单个page4.3 理解多个page页目录单页情况多页情况 4.4 B树 VS B树4.5 聚簇索引 VS 非聚簇索引1.非聚簇索引2.聚簇索引 5.索引操…...
软考高级--案例分析
架构风格 重点 交互方式数据结构控制结构扩展方法 分类 管道-过滤器风格 数据流 数据仓储风格 星型结构以数据为中心,其他构件围绕数据进行交互 企业服务总线esb 定义 以一个服务总线充当中间件的角色,把各方服务对接起来,所有服务…...
Go - 内存逃逸
概念 每个函数都有自己的内存区域来存放自己的局部变量、返回地址等,这个内存区域在栈中进行分配。当函数结束时,这段内存区域会进行释放。 但有些变量,我们想在函数结束后仍然使用它,那么就要把这个变量在堆上分配,这…...