SAS系统学习:

`

1
2
3
@Column` 注解通常用于 ORM(对象关系映射)框架中,如 Hibernate 或 JPA(Java Persistence API),它用于指定 Java 类中的字段或属性如何映射到数据库表中的列。

`@ExcelProperty` 注解通常用于 Excel 导入导出相关的框架中,它用于标记 Java 类中的字段或属性,以指定该字段或属性在 Excel 文件中的列名。

使用场景:

  • Excel 导入导出:当你需要从 Excel 文件中读取数据或将数据写入 Excel 文件时,使用 @ExcelProperty 注解。
  • 数据库持久化:当你需要将 Java 对象映射到数据库表时,使用 @Column 注解。

ElementType 枚举值:

  • METHOD:表示注解可以应用于方法。
  • FIELD:表示注解可以应用于字段(成员变量)。
  • TYPE:表示注解可以应用于类、接口或枚举。
  • PARAMETER:表示注解可以应用于方法参数。
  • CONSTRUCTOR:表示注解可以应用于构造函数。
  • LOCAL_VARIABLE:表示注解可以应用于局部变量。
  • ANNOTATION_TYPE:表示注解可以应用于其他注解。
  • PACKAGE:表示注解可以应用于包。

@Target 用于指定注解可以应用于哪些程序元素。程序元素可以是类、方法、字段、构造函数等。@Target 元注解接受一个或多个 ElementType 枚举值作为参数,这些枚举值指定了注解可以应用于哪些类型的程序元素。

@Retention 用于指定注解的生命周期。生命周期决定了注解在编译时和运行时的存在情况。@Retention 元注解接受一个 RetentionPolicy 枚举值作为参数。

  • @Target:用于指定注解可以应用于哪些类型的程序元素。
  • @Retention:用于指定注解的生命周期,决定注解在编译时和运行时的存在情况。

ORM(Object-Relational Mapping,对象关系映射)框架是一种编程技术,用于将对象模型中的对象与关系数据库中的表进行映射。ORM 框架的主要目的是简化数据库操作,使得开发者可以使用面向对象的方式来进行数据库交互,而不需要直接编写 SQL 语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import javax.persistence.*;

@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "username")
private String username;

@Column(name = "email")
private String email;

// 构造函数、getter 和 setter 省略
}

在 Navicat 中,你可以通过 SQL 查询来格式化数据库表中某一列的数据。如果你想在查询结果中保留两位小数,可以使用 SQL 的 FORMAT 函数或者其他相关函数来实现这一点。

类似如下:

1
2
3
4
5
6
7
8
SELECT id, name, FORMAT(column_name, 2) AS formatted_column
FROM your_table;
查看操作结果
SELECT id, name, FORMAT(column_name, 2) AS formatted_column
FROM your_table;
永久格式化
UPDATE your_table
SET column_name = ROUND(column_name, 2);

BaseMapper<T> 接口中定义的方法

BaseMapper<T> 接口中通常定义了以下方法:

    • int insert(T entity);:插入一条记录。
    • int deleteById(ID primaryKey);:根据主键删除一条记录。
    • int deleteByMap(@Param("cm") Map<String, Object> columnMap);:根据 map 对象删除记录。
    • int updateById(T entity);:根据主键更新一条记录。
    • int update(T entity, @Param("ew") Wrapper<T> updateWrapper);:根据 where 条件更新记录。
    • List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);:根据条件查询记录列表。
    • T selectOne(@Param("ew") Wrapper<T> queryWrapper);:根据条件查询单条记录。
    • IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);:根据条件分页查询记录列表。

CRUD 操作指的是数据库中常见的四种基本操作:Create(创建)、Read(读取)、Update(更新)和Delete(删除)。这些操作是任何应用程序与数据库交互的基础。

格式化方法:

String.format 方法可以方便地格式化字符串。

DecimalFormat 类提供了更强大的格式化数字的功能,特别是对于固定的小数点位数。

MS的学习

append 方法在 StringBuffer 类中用于将指定的内容追加到当前字符串的末尾。

trim 方法是 String 类中的一个常用方法,用于去除字符串两端的空白字符。空白字符包括空格、制表符、换行符等。使用 trim 方法可以确保字符串在进行比较或拼接时不会因为两端的空白字符而产生意外的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
*
* TODO 获取考官信息加载到前台动态数据表格中
* .本方法由 chenluwei 创建于 2019年11月13日上午10:27:36
* @param page
* @param limit
* @param basicMng
* @param pTitle
* @param job
* @return Map<String,Object>
*
*/
@RequestMapping("getExaminerList")
@ResponseBody
public Map<String, Object> getExaminerList(int page, int limit, String basicMng, String pTitle, String job, Integer subjectId, Integer majorId) {
Map<String, Object> result = new HashMap<String, Object>();
StringBuffer where = new StringBuffer();
if (null != basicMng && !"".equals(basicMng)) {
where.append("( ms_examiner.`name` like '%" + basicMng.trim() + "%' or ms_examiner.mbPhone = '" + basicMng.trim() + "') and ");
}
if (null != pTitle && !"".equals(pTitle)) {
where.append(" ms_examiner.pTitle = '" + pTitle.trim() + "' and ");
}
if (null != job && !"".equals(job)) {
where.append(" ms_examiner.job = '" + job.trim() + "' and ");
}
if (null != subjectId) {
where.append(" ms_examiner.subjectId = '" + subjectId + "' and ");
if (null != majorId) {
where.append(" ms_examiner.majorId = '" + majorId + "' and ");
}
}
where.append(" 1=1 ");
Page findByPage = this.examinerService.findByPageByLeftJoin(ExaminerVO.class, page, limit, where.toString(), null);
result.put("code", 0);
result.put("msg", "");
result.put("count", findByPage.getMaxRowCount());
result.put("data", findByPage.getResultList());
logOperation("加载考官数据", OperationType.AJAX_LOAD, true);
return result;
}

where.append(“( ms_examiner.name like ‘%” + basicMng.trim() + “%’ or ms_examiner.mbPhone = ‘“ + basicMng.trim() + “‘) and “);

这一行代码构建了一个SQL查询条件,具体如下:ms_examiner.namelike ‘% + basicMng.trim() + %’”:这部分表示考官的姓名中包含basicMng的值。

%是SQL中的通配符,表示任意字符序列。因此,like ‘%value%’表示字段中包含value的任何字符串。

or ms_examiner.mbPhone = ‘ + basicMng.trim() + ‘:这部分表示考官的手机号等于basicMng的值。

( 和 ):括号用于将两个条件组合在一起,形成一个复合条件。

and:在SQL查询中,and用于连接多个条件。

where.append(“ 1=1 “);作用是在所有条件之后追加 1=1。这是一个恒为真的条件,确保即使没有其他条件时,SQL 查询也不会因为 WHERE 子句为空而报错。

logOperation: 调用日志记录方法,记录当前的操作。

where.toString(): 将构建好的查询条件字符串传递给方法。

1
2
3
4
5
6
7
8
@RegisterMapper
public interface DeleteByPrimaryKeyMapper<T> {
@DeleteProvider(
type = BaseDeleteProvider.class,
method = "dynamicSQL"
)
int deleteByPrimaryKey(Object var1);
}

@RegisterMapper 是MyBatis的注解,用于注册映射器接口。这意味着MyBatis会自动扫描并注册带有此注解的接口,使其可以在应用程序中使用。

@DeleteProvider 是MyBatis的注解,用于指定动态SQL的提供者类和方法。

type = BaseDeleteProvider.class指定了提供动态SQL的类是 BaseDeleteProvider。 method = “dynamicSQL”指定了在 BaseDeleteProvider 类中生成动态SQL的方法名是 dynamicSQL|

工具类作用

AssertUtil 类是一个工具类,提供了多种方法来检查和比较对象的状态。这些方法在开发中非常有用,可以帮助开发者快速判断对象是否为空、集合是否为空、字符串是否为空等,以及比较两个对象的属性值是否相等。

Base64Util 类是一个工具类,用于将图片文件转换为 Base64 编码的字符串。这个类的主要功能是读取图片文件的内容,并将其编码为 Base64 字符串。

BaseController 类是一个工具类,主要用于在 Spring MVC 应用程序中获取 HttpServletRequestHttpServletResponseHttpSessionServletContext 等对象

ClassType 类是一个工具类,用于定义一些常用的 Java 类型常量。这些常量可以在其他类中引用,以便于类型检查和转换

Serializable 接口是 Java 标准库中的一个接口,位于 java.io 包中。这个接口的主要目的是标记一个类的对象可以被序列化,即可以将其状态转换为字节流,以便于存储或传输。

服务器网络判断

  1. 检查是否有网络连接:首先需要确保服务器本身是否有正常的网络连接。可以通过ping命令或者使用网络监测工具来测试服务器是否可以与其他设备进行通信。
  2. 检查服务器网络配置:确认服务器的IP地址、子网掩码、网关等网络配置是否正确。可以通过查看服务器的网络设置或者执行ipconfig(Windows)或ifconfig(Linux)命令来获取相关信息。
  3. 检查服务器是否能够访问互联网:使用浏览器或者命令行工具(如curl或wget)尝试访问互联网上的一些常见网站,以确认服务器是否能够正常访问外部网络。如果无法访问,可能是防火墙或路由器设置问题。
  4. 检查本地网络设备:如果服务器连接到本地网络,需要检查与服务器相连的交换机、路由器等网络设备的状态。检查是否有物理连接问题、端口是否正常工作,以及设备的配置是否正确。
  5. 检查网络负载和带宽:如果服务器的网络访问速度较慢或者出现丢包现象,可能是网络负载过高或者带宽不足。可以通过网络监测工具或者流量分析工具来检测服务器的网络负载情况,以及网络带宽的使用情况。
  6. 检查防火墙设置:如果服务器安装了防火墙软件或配置了网络访问限制规则,可以检查防火墙的设置,确保访问规则没有阻止服务器对外部网络的访问。
  7. 联系网络服务提供商:如果以上步骤都没有找到问题,可以与服务器所在的数据中心或网络服务提供商联系,跟他们反映问题,并请他们协助解决。

java面试准备:

redis篇

最近的项目中那些使用了redis?

结合项目,一是验证项目场景的真实性,二是做为深入发问的切入点。

缓存:缓存3兄弟(穿透,击穿,雪崩),双写一致,持久化,数据过期测试,数据淘汰策略

分布式锁:setnx,redisson

消息队列,延迟队列:何种数据类型

缓存穿透

查询一个不存在的数据,MySQL查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库。数据访问过多,数据库宕机

解决方案一

缓存空数据

解决方案二

布隆过滤器:可以检索一个元素是否在一个集合中。

会有误判,所以需要误判率。

误判率:数组越小误判率越大,数组越大误判率越小,但内存使用

缓存击穿

给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发请求可能会瞬间把DB压垮

解决方案一

互斥锁,强一致,性能差(业务范围与钱相关)

解决方案二

逻辑过期,高可用,性能优,不能保证数据绝对一致

缓存雪崩

是指在同一时段大量的缓存key同时失效或者redis服务宕机,导致大量请求到达数据库,带来巨大压力。

解决方案

给不同的key的ttl添加随机值

利用redis集群提高服务的可用性

给缓存业务添加降级限流策略

给业务添加多级缓存

穿透无中生有key,布隆过滤null隔离。

缓存击穿过期key,锁与非器解难题。

雪崩大量过期key,过期时间要随机。

面试必考3兄弟,可用限流来保底。

redis作为缓存,mysql的数据如何与redis进行同步呢?双写一致性

设置前提,业务背景。

docker学习

什么是docker,docker就是一个快速交付应用,运行应用的技术。

可以将程序及其依赖,运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统,运行时利用沙箱机制形成隔离容器,各个应用互不干扰。

项目部署问题

大型项目组件较多,运行环境较为复杂,部署时会碰到一些问题:

依赖关系复杂,容易产生兼容性问题

开发,测试,生产环境有差异

docker如何解决依赖的兼容问题的?

将应用的Libs(函数库),Deps(依赖),配置与应用一起打包

将每个应用放到一个隔离容器去运行,避免相互干扰

不同环境操作系统不同,docker怎么解决?

docker将用户程序与所调用的系统(比如Ubuntu)函数库一起打包

docker运行到不同操作系统时,直接基于打包的库函数,借助于操作系统的Linux内核来运行。

虚拟机是操作系统中模拟硬件设备,然后运行另一个操作系统。

docker架构

docker是一个cs架构的程序,由两部分组成:

服务端:docker守护进程,负责处理docker指令,管理镜像,容器等。

客服端:通过命令或restAPI向docker服务端发送指令。可以在本地或远程服务端发送指令。

卸载docker

卸载旧版本docker

1
2
3
4
5
6
7
8
9
10
11
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine \
docker-ce

安装yum工具:

1
2
3
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 --skip-borken

更新镜像源:

1
更新为阿里云镜像源

输入命令

1
yum install y docker-ce //docker-ce为社区免费版本。

启动docker需要关闭防火墙。

关闭防火墙:systemctl stop firewalld

systemctl disable firewalld

启动docker:

systemctl start docker

查看是否成功dockers -v查看版本。

docker基本操作

镜像相关命令

镜像名称一般分为两部分组成:[repository]:[tag]

在没有指定tag时,默认是latest,代表最新版本的镜像

docker build 构建镜像

docker imges 查看镜像

docker rmi 删除镜像

docker pull 从服务拉取镜像

docker push 推送镜像到服务

docker save 压缩

docker load 解压

容器相关命令

docker run 运行

docker exec 进入容器

docker logs 查看容器运行日志

docker ps 查看所有和运行的容器

docker rm 删除指定容器

docker pause暂停

docker unpause

docker start

docker stop 停止

创建运行一个nginx容器

步骤一:取dockerhub上查看nginx的容器运行命令

1
docker run --name containerName -p 80:80 -d nginx

docker run : 创建并运行一个容器

—name: 给容器起一个名字,比如叫做mn

-p: 将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口

-d: 后台运行容器

nginx:镜像名称

进入容器内部,修改HTML文件内容

步骤一:进入容器。

1
docker exec -it mn bash

docker exec :进入容器内部,执行一个命令

-it: 给当前进入的容器创建一个标准输入,输出终端,允许我们与容器交互

mn:要进入的容器的名称

bash:进入容器后执行的命令,bash是一个linux终端交互命令

数据卷

是一个虚拟目录,指向宿主机文件系统中的某个目录。

容器与数据耦合的问题

不便于修改

数据不可复用

升级维护困难

1
docker volume [COMMAND]

create 创建一个volume

inspect 显示一个或多个volume的信息

Ls 列出所有的volume

prune 删除未使用的volume

rm 删除一个或多个指定的volume

微服务学习

eureka框架

微服务角色有两类:

EurekaServer:服务端,注册中心

记录服务信息

心跳监控:发送http请求

EurekaClient:客服端

服务提供者:注册自己的信息到EurekaServer

每隔30秒发送心跳,即发送http请求

服务消费者:根据服务名称从EurekaServer拉取服务列表

基于服务列表做负载均衡,选中一个微服务后发起远程调用

搭建注册中心》服务注册》服务发现

依赖注入:

1
2
3
4
5
6
7
8
9
10
11
<!-- eureka服务端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version> <!-- 使用最新版本 -->
<scope>provided</scope>
</dependency>

.yml文件修改:

1
2
3
4
5
6
7
8
9
server:
port: 10086 # 服务端口
spring:
application:
name: eurekaserver # eureka的服务名称
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka

restTemplate修改:

1
2
3
4
5
6
7
8
String url = "http://userservice/user/" + order.getUserId();
//这里把具体的网络端口改成userservice
//加入注解@LoadBalanced
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

调整负载均衡规则:

1
2
3
4
@Bean
Public IRule randomRule{
return new randomRule();
}

作用全局。

1
2
3
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalaner.RandomRule

作用服务器。局部作用

springMvc

入门案例

报错解决:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.3.0:resources (default-resources) on project springmvc_01_quickstart: Execution default-resources of goal org.apache.maven.plugins:maven-resources-plugin:3.3.0:resources failed: Unable to load the mojo ‘resources’ (or one of its required components) from the plugin ‘org.apache.maven.plugins:maven-resources-plugin:3.3.0’: com.google.inject.ProvisionException: Unable to provision, see the following errors: [ERROR]

解决加入插件

1
2
3
4
5
6
7
8
9
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version> <!-- 使用最新版本 -->
</plugin>
</plugins>
</build>

springmvc主要进行处理请求的控制器类,定义处理请求的控制器方法,并配置映射路径与返回json数据。

流程:

容器初始化:

1.服务器启动,指向servletContainersInitConfig类,初始化web容器

2.指向createServletApplicationContext方法,创建了WebApplicationContext对象

3.加载SpringMvcConfig

4.执行@ComponenScan加载对应的bean

5.加载userController,每个@RequestMapping的名称对应一个具体的方法

6.执行getServletMappings方法,定义所有的请求都通过SpringMVC

单次请求过程

1.发送请求Localhost/save

2.web容器发现所有请求都经过springMvc,将请求交给springMvc处理

3.解析请求路径/save

4.由/save匹配执行对应的方法save

5.执行save

6检测到有@ResponseBody直接将save方法的返回值作为响应请求体返回给请求方

因为功能不同,如何避免spring错误的加载到springMvc的bean?

加载spring控制的bean的时候排除掉springmvc控制的bean。

方式1:spring加载的bean设定扫描范围为com.itheima.排除掉controller包内的bean。

方式2:spring扫描设为精准范围。

@ComponentScan

类型:类注解

1
2
3
4
5
6
7
8
9
@Configuration
@ComponentScan(vale = "com.itheima",
@ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class))

public class SpringConfig{

}

excludeFilters:排除扫描路径中加载的bean,需要指定类别和具体项。

includeFilters:加载指定的bean,需要指定类别和具体项

Spring Boot 解决 Spring 与 Spring MVC 的 Bean 扫描问题

Spring Boot 通过自动配置机制巧妙地解决了传统 Spring 和 Spring MVC 应用中可能出现的 Bean 扫描冲突问题。以下是其解决方案的核心要点:

1. 自动配置与组件扫描分离

Spring Boot 使用 @SpringBootApplication 注解(组合了 @Configuration, @EnableAutoConfiguration@ComponentScan)来启动应用,其中:

  • @ComponentScan 默认扫描主类所在包及其子包
  • @EnableAutoConfiguration 负责加载 Spring Boot 提供的自动配置

2. 区分 Web 和非 Web 配置

Spring Boot 自动配置会检测应用是否是 Web 应用,并相应配置:

  • 对于 Web 应用,会自动配置 DispatcherServlet 和相关的 MVC 组件
  • 非 Web 应用则不会加载这些配置

3. 内部实现机制

具体来说,Spring Boot 通过以下方式解决扫描问题:

  1. 父子容器结构:传统 Spring MVC 使用父子容器,而 Spring Boot 默认使用单一容器

    • 子容器可以访问父容器的 Bean,但父容器不能访问子容器的 Bean

    • 父容器:通常包含服务层、数据层等 Bean

    • 子容器:包含控制器、视图解析器等 MVC 相关 Bean
    • Spring Boot 2.x 之后默认使用单一容器简化了这种结构
  2. 自动配置类条件过滤

    • WebMvcAutoConfiguration 只在 Web 环境下生效
    • 通过 @ConditionalOnWebApplication 等条件注解控制配置类的加载
  3. 组件扫描顺序控制

    • Spring Boot 确保核心组件先被扫描和初始化
    • MVC 相关组件随后加载

4. 自定义配置

如果需要更细粒度的控制,可以:

1
2
3
4
5
6
7
8
9
@Configuration
public class WebConfig implements WebMvcConfigurer {
// 自定义 MVC 配置
}

@Configuration
public class AppConfig {
// 非 Web 相关配置
}

5. 避免冲突的最佳实践

  1. 将主类放在项目根包中
  2. 避免使用 @ComponentScan 手动覆盖默认扫描路径
  3. 如果需要排除某些自动配置,使用 @EnableAutoConfiguration(exclude = {...})

通过以上机制,Spring Boot 简化了传统 Spring MVC 中需要手动配置父子容器和重复扫描的问题,使开发者能够更专注于业务逻辑而非配置。

pojo与json区别

特性 POJO JSON
可读性 需要熟悉 Java 语法 易于人类阅读和编写
跨平台支持 仅限于 Java 环境 支持所有编程语言
数据类型支持 支持复杂的 Java 数据类型 支持基本类型和简单结构
性能 在 Java 中操作高效 需要解析和序列化,性能较低
用途 内部数据表示 数据交换和存储

ISO 日期格式遵循 ISO 8601 标准,这是一种国际标准化的日期和时间表示方法。

Pattern 是指一种自定义的日期格式模式,通常用于编程语言或工具中,用于定义日期的显示方式。

Rest风格是一张描述访问资源的形式,使用这种风格开发叫restful

1
@RequestMapping(value = "/users",method = RequestMethod.POST)
特性 @RequestBody @RequestParam @PathVariable
数据来源 请求体(Request Body) URL 查询参数(Query Params) URL 路径(Path Variables)
适用请求方法 POST、PUT GET GET、POST、PUT、DELETE 等
数据类型 复杂对象(如 JSON) 简单键值对 简单值(如 ID)
示例 URL 无(数据在请求体中) /users?name=John&age=30 /users/123
示例代码 @RequestBody User user @RequestParam String name @PathVariable int id

拦截器(Interceptor)和过滤器(Filter)的区别与相同点的表格展示:

特性 拦截器(Interceptor) 过滤器(Filter) 相同点
作用范围 方法级别,通常用于AOP编程,拦截方法调用前后。 请求和响应级别,作用于整个Web请求和响应的全局处理。 都用于请求和响应的处理,增强应用程序功能。
使用场景 Spring框架中的控制器方法拦截、RPC框架中的远程方法调用拦截。 Java Web应用中的请求和响应处理(如Servlet过滤器)。 都可以用于日志记录、安全控制、性能监控等。
执行顺序 在Web应用中,拦截器的执行顺序在过滤器之后,因为过滤器先处理请求。 在Web应用中,过滤器先于拦截器执行,因为它在请求到达Servlet之前进行处理。 都遵循一定的执行顺序,且可以在请求处理链中协同工作。
粒度 更细粒度,针对方法级别的拦截和控制。 较粗粒度,针对整个请求和响应的全局处理。 都可以对请求和响应进行干预,但粒度不同。
实现框架 常见于Spring等AOP框架。 常见于Java Web应用(如Servlet)。 都是通过框架或容器提供的机制实现。
典型功能 日志记录、权限验证、数据预处理、性能监控等。 字符编码设置、响应压缩、安全控制(如身份验证)、日志记录等。 功能上有重叠,例如日志记录和安全控制。
适用场景 适用于需要对方法调用过程进行精细化控制的场景。 适用于需要对整个Web请求和响应进行全局处理的场景。 都用于增强应用程序的功能,但适用场景不同。
拦截器是Spring MVC框架的一部分,专门用于在控制器(Controller)方法执行前后进行拦截和处理。它的作用主要集中在控制器层面,能够对请求的处理过程进行更细粒度的控制。 过滤器是Servlet规范的一部分,作用于Spring MVC之外,主要用于对HTTP请求和响应进行全局处理。它的作用范围比拦截器更广,通常在请求进入Spring MVC框架之前就已经开始工作。

什么是代理?

是一种设计模式,它允许通过一个代理对象来控制对另一个对象的访问。代理模式的核心思想是为目标对象提供一个代理,并由代理对象控制对目标对象的访问。

  1. 静态代理

    代理类和目标类实现相同的接口。

    代理类在编译时就已经确定。

  2. 动态代理

    代理类在运行时动态生成。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    // 1. 定义接口
    interface Star {
    void sing();
    }

    // 2. 创建目标类
    class BigStar implements Star {
    @Override
    public void sing() {
    System.out.println("大明星唱歌");
    }
    }

    // 3. 创建动态代理
    class ProxyUtil {
    public static Star createProxy(Star target) {
    return (Star) Proxy.newProxyInstance(
    ProxyUtil.class.getClassLoader(), // 类加载器
    new Class[]{Star.class}, // 目标接口
    new InvocationHandler() { // 调用处理器
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("代理开始");
    Object result = method.invoke(target, args); // 调用目标方法
    System.out.println("代理结束");
    return result;
    }
    });
    }
    }

    // 测试类
    public class DynamicProxyDemo {
    public static void main(String[] args) {
    Star bigStar = new BigStar();
    Star proxy = ProxyUtil.createProxy(bigStar);
    proxy.sing();
    }
    }

    Proxy.newProxyInstance

    用于创建代理对象。

    参数:

    1. 类加载器(ClassLoader)。
    2. 目标接口数组(Class<?>[])。
    3. 调用处理器(InvocationHandler)。

    InvocationHandler.invoke

    在代理对象调用方法时执行。

    参数:

    1. 代理对象(Object proxy)。
    2. 调用的方法(Method method)。
    3. 方法参数(Object[] args)。

什么是代理?

是 Java 提供的一种机制,允许程序在运行时动态地获取类的信息(如类名、方法、字段、构造函数等),并操作这些信息。通过反射,程序可以在运行时检查和修改对象的行为,甚至调用私有方法或访问私有字段。

1. 反射的核心功能

反射的主要功能包括:

  1. 获取类的信息
    • 获取类的名称、修饰符、父类、接口、字段、方法、构造函数等。
  2. 创建对象
    • 动态创建类的实例。
  3. 调用方法
    • 动态调用对象的方法(包括私有方法)。
  4. 访问字段
    • 动态获取或修改对象的字段值(包括私有字段)。
  5. 操作数组
    • 动态创建和操作数组。

2. 反射的核心类

Java 反射机制主要通过以下类和接口实现:

  1. Class<T>
    • 表示一个类或接口的类型信息。
    • 可以通过 Class.forName("类名")对象.getClass() 获取。
  2. Field
    • 表示类的字段(成员变量)。
    • 可以通过 Class.getField()Class.getDeclaredField() 获取。
  3. Method
    • 表示类的方法。
    • 可以通过 Class.getMethod()Class.getDeclaredMethod() 获取。
  4. Constructor<T>
    • 表示类的构造函数。
    • 可以通过 Class.getConstructor()Class.getDeclaredConstructor() 获取。
  5. Modifier
    • 提供对类、字段、方法等的修饰符(如 publicprivatestatic 等)的解析。

(1) 获取 Class 对象

  • Class.forName("类名")

    • 通过类的全限定名获取 Class 对象。

    java

    复制

    1
    Class<?> clazz = Class.forName("java.util.ArrayList");
  • 对象.getClass()

    • 通过对象获取 Class 对象。

    java

    复制

    1
    2
    String str = "Hello";
    Class<?> clazz = str.getClass();
  • 类名.class

    • 通过类字面量获取 Class 对象。

    java

    复制

    1
    Class<?> clazz = String.class;

5. 反射的优缺点

优点

  1. 动态性
    • 可以在运行时动态加载类、创建对象、调用方法等。
  2. 灵活性
    • 可以操作私有字段和方法,突破访问限制。
  3. 通用性
    • 适用于框架开发、动态代理、注解处理等场景。

缺点

  1. 性能开销
    • 反射操作比直接调用方法或访问字段慢,因为需要额外的类型检查和动态解析。
  2. 安全性问题
    • 反射可以绕过访问控制(如访问私有字段和方法),可能导致安全问题。
  3. 代码复杂性
    • 反射代码通常比普通代码更复杂,可读性和维护性较差。

Spring 管理与方法调用的区别

特性 Spring 管理 方法调用
对象创建 由 Spring 容器负责创建 手动创建
依赖注入 支持依赖注入(DI) 不支持依赖注入
生命周期管理 Spring 负责对象的初始化和销毁 需要手动管理对象的生命周期
配置集中化 通过配置文件或注解集中管理 配置分散在代码中
适用场景 适用于复杂的依赖关系和大型项目 适用于简单的场景或非 Spring 项目
代码耦合度 低耦合,易于维护和扩展 高耦合,维护和扩展较困难

Spring 管理 DataSource 的优势

  • 简化配置:通过配置文件或注解集中管理数据库连接信息。
  • 依赖注入:无需手动创建 DataSource,Spring 自动注入。
  • 连接池支持:Spring 支持多种连接池(如 HikariCP、Tomcat JDBC Pool),可以轻松切换。
  • 事务管理:Spring 提供了强大的事务管理功能,与 DataSource 无缝集成。

表现层数据封装

前端接收数据格式-创建结果模型类,封装数据到data属性中。

前后端人员约定形式。

异常诱因:

框架内部抛出异常:使用不合规

数据层抛出的异常:外部服务器故障导致

业务层抛出的异常:业务罗欧锦书写错误导致

表现层抛出的异常:数据收集,校验等规则导致

工具类抛出的异常:因工具类书写不严谨不够健壮导致

异常处理:

所有异常放在表现层处理

以下是 @RestControllerAdvice@ExceptionHandler 的对比表格:

特性 @RestControllerAdvice @ExceptionHandler
作用 定义全局异常处理类,结合 @ExceptionHandler 处理控制器抛出的异常。 标记处理特定异常的方法。
使用场景 用于集中管理全局异常处理逻辑,通常与 @ExceptionHandler 一起使用。 用于处理控制器或 @RestControllerAdvice 类中抛出的特定异常。
返回值 通常返回 ResponseEntity 或直接返回对象(自动转换为 JSON/XML)。 通常返回 ResponseEntity 或直接返回对象(自动转换为 JSON/XML)。
作用范围 默认全局生效,可通过 basePackagesannotations 限定范围。 仅在定义它的类中生效(控制器或 @RestControllerAdvice 类)。
组合使用 通常与 @ExceptionHandler 一起使用,提供全局异常处理能力。 通常与 @RestControllerAdvice 或控制器一起使用,处理特定异常。
示例 java @RestControllerAdvice public class GlobalExceptionHandler { ... } java @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity<String> handleException() { ... }
灵活性 更灵活,可以处理多个控制器的异常,并支持全局配置。 更具体,专注于处理特定异常。
适用场景 适用于需要统一处理多个控制器异常的复杂项目。 适用于处理特定控制器或特定异常的场景。

总结

@RestControllerAdvice 更适合全局异常处理,集中管理异常逻辑。

@ExceptionHandler更适合处理特定异常,通常与@RestControllerAdvice` 或控制器结合使用。

两者结合使用可以构建一个健壮的异常处理机制,提升代码的可维护性和可读性。

项目异常分类:

业务异常:

规范德用户行为产生的异常

不规范的用户产生的异常

解决:发送信息给用户,提醒规范操作

系统异常:

项目运行过程可预计且无法避免的异常

解决:发送信息给运维,记录日志

其他异常

编程人员未预期到的异常

解决:发送信息给编程,提醒维护,记录日志

面试Star法则

situation:事情是在什么情况发生;

task:你是如何明确你得任务得;

Action:争对这样得情况分析,你采用了什么行动方式

Result:结果怎么样,在这样得情况你学习到了什么

FAB法则:

feature:是什么

Advantage:比别人好在哪里

Benefit:如果雇佣你,招聘方会得到什么好处

项目经历怎么写:

1.对项目整体设计的一个感受

2.在这个项目中你负责什么,做了什么,担任什么角色

3.从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用

4.在项目描述中,最好体现自己的综合素质

那些有价值的问题值得提问?

1.能不能谈谈你作为一个公司老员工对公司的感受?

2.能不能问一下,你当时因为什么原因加入这家公司,或者这家公司有那些地方吸引你?

3.我觉得我表现的不是太好,你有什么建议或者评价给我吗?

4.接下来我会有一段很长的空档期,有什么要注意或者建议学习的吗?

5这个为什么还招人

6.大概什么时候能给我回复呢?

java复习

接口和抽象类的区别

1.接口的方法默认是public,所有方法在接口中不能实现(Java8后开始默认实现),抽象类可以有非抽象的方法

2.接口中的实例变量默认是final类型的,而抽象类则不一定

3.一个类可以实现多个接口,但最多实现一个抽象类

4.一个类实现接口的话要实现接口的所有的方法,而抽象类不一定

5.接口不能用new实例化,但可以声明,但是必须引用一个实现该接口的对象,从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一个行为的规范

java集合框架

ArrayList与linkdeList异同:

1.是否保证线程安全:ArrayList与LinkedList都是不同的,也就是不保证线程安全。

2.底层数据结构:ArrayList底层使用的是Object数组;LinkedList底层使用的是双向链表数据结构。

3.插入和删除是否受元素位置的影响:ArrayList采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。

LinkedList采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似o(1)而数组为近似O(n);

4.是否支持快速随机访问:快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。

5.内存空间占用:ArrayList的空间浪费主要体现在list列表的结尾会预留一定的容量空间,而LinkedList的空间浪费体现在它的每一个元素都需要消耗比ArrayList更多空间。

Vector

vector类的所有都是同步的。可以由两个线程安全地访问Vector对象,但是一个线程访问Vector地话代码要在同步上耗费大量的时间。

ArrayList不是同步的。所以不需要线程安全时考虑ArrayList。

HashMap与Hashtable的区别:

hashtable是线程安全的。里面的方法都经过synchronized修饰。

hashtable已经被淘汰了,效率低。

hashMap可以将null作为键,键只有一个,可以有多个键对应的值为null。

hashMap扩大2倍。

当链表长度超过默认长度8时,链表转换为红黑树,减少搜索时间。

hashMap与hashSet

hashSet底层是基于hashMap实现的。

collection

1.List

ArrayList

Vector

LinkedList

2.Set

HashSet

LinedHashSet

TreeSet

Map

Hashmap

LinkedHashMap

HashTable

TreeMap

Java多线程

1.synchoized关键字了解

解决的是多个线程之间访问的同步性,synchoized关键字可以保证被它修饰的方法或者代码快在任意时刻只能由一个线程执行。

2怎么使用synchronized关键字

修饰实例方法,作用于当前对象实例加锁,进入同步diamond前要获得当前对象实例的锁。

修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁。

synchronized与ReenTrantLock的区别

1.两者都是可重入锁

2.synchronized依赖于jvm厄尔ReenTrantLock依赖于API

3.ReenTranLock比synchronized增加了一些高级功能

如何解决多线程时变量数据不一致问题?

变量声明为volatile,这就指示jvm,这个变量是不稳定的,每次使用它都到主存中读取。作用:volatile关键字的主要作用是保证变量的可见性然后还有一个作用就是放在指令重排序。

synchronized与voliatile区别

voliatile是线程同步的轻量级实现,性能好,但是synchronized使用场景多(修饰方法以及代码块)

多线程访问volatile关键字不会发生阻塞,而synchronized可能会发送阻塞

volatile保证数据的可见性,不能保证数据的原子性。

volatile关键字主要用于解决变量在多个线程之间的可见性,而后者解决的是多个线程之间访问资源的同步性

为什么要使用线程池

线程池提供了一种现在和管理资源。每个线程池还维护一些基本统计信息。

好处:1.降低资源消耗。

2.提高响应速度

3.提高线程的可管理性。

实现Runnable和Callable接口区别:前者不会返回结果,后者会返回结果。

execute方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否。

submit用于执行提交需要返回值的任务。任务池返回一个future类型的对象,通过future对象可以判断任务是否执行成功。

Mysql

事务:是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

银行转账,典型的事务。

默认mysql的事务自动提交,也就是说,当执行一条DML语句,mysql会立即隐式的提交事务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 设置手动提交
SET autocommit = 0;

-- 开始事务
START TRANSACTION;

-- 执行SQL操作
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;

-- 检查是否有错误
-- 如果没有错误则提交
COMMIT;

-- 如果有错误则回滚
-- ROLLBACK;

-- 恢复自动提交
SET autocommit = 1;

事务的四大特性(ACID)是数据库操作的基石:

  1. 原子性:事务是不可分割的整体,所有操作要么全部成功,要么全部回滚,不存在中间状态。
  2. 一致性:事务执行前后,数据库必须保持逻辑规则(如约束、触发器),确保数据始终有效。
  3. 隔离性:并发事务相互隔离,防止脏读、幻读等问题,通过锁或MVCC机制实现。
  4. 持久性:事务一旦提交,结果永久保存,即使系统故障也不丢失,依赖日志(如Redo Log)恢复。

并发事务可能引发以下四类核心问题:

  1. 脏读(Dirty Read)
    事务A读取了事务B未提交的修改数据,若事务B回滚,则事务A读到的是无效的”脏数据”。
  2. 不可重复读(Non-repeatable Read)
    事务A多次读取同一数据,期间事务B修改并提交了该数据,导致事务A前后读取结果不一致。
  3. 幻读(Phantom Read)
    事务A按条件查询数据,期间事务B新增或删除了符合该条件的记录,导致事务A两次查询结果集不同(如原本10条变为12条)。
  4. 丢失更新(Lost Update)
    两个事务同时读取并修改同一数据,后提交的事务会覆盖先提交的事务的修改(如并发扣款导致余额错误)。

:不可重复读针对数据值的变化,幻读针对数据行的增减。数据库通过隔离级别(如READ COMMITTEDSERIALIZABLE)控制这些问题。

事务隔离级别分为四类,用于控制并发事务间的可见性:

  1. 读未提交(Read Uncommitted):事务可读取其他事务未提交的数据,可能导致脏读、不可重复读和幻读,性能最高但安全性最差。
  2. 读已提交(Read Committed):只能读取已提交的数据,避免脏读,但可能出现不可重复读和幻读(如Oracle默认级别)。
  3. 可重复读(Repeatable Read):确保同一事务内多次读取数据结果一致,避免脏读和不可重复读,但可能发生幻读(MySQL默认级别)。
  4. 串行化(Serializable):完全隔离事务,通过强制排序避免所有并发问题,但性能最低,适用于严格要求一致性的场景。

隔离级别越高,数据一致性越强,但并发性能越低,需根据业务需求权衡选择。

Kafka

borker

kafka服务器进程,生产者、消费者都要连接broker

一个集群由度过broker组成,功能实现Kafka集群的负载均衡,容错

producer:生产者

consumer:消费者

topic:主题,一个Kafka集群中,可以包含多个topic。一个topic可以包含多个分区

是一个逻辑结构,生产消费消息都需要指定topic

partition:Kafka集群的分布式就是由分区来实现的。一个topic中的消息可以分布在topic中的不同paritition中。

replica:副本,实现Kafka集群的容错,实现partition的容错。一个topic至少包含大于1个的副本

consumer group:消费者组,一个消费组中的消费者可以共同消费topic中的分区数据。每一个消费组都一个唯一一个的名字。配置group-id一样的消费者是属于同一个组中。

offest:偏移量。相对于消费者来说,可以通过offest来拉取数据。

消费者组

一个消费者组中可以包含多个消费者,共同来消费topic中的数据。

一个topic中如果只有一个分区,那么这个分区只能被某个组的一个消费者消费

有多少个分区,那么就可以被同一个组内的多少个消费者消费。

幂等性是指对同一个操作执行一次或多次,其产生的结果是相同的。在分布式系统和API设计中,幂等性是一个非常重要的概念。

Kafka 就像「快递分拣中心」

生活案例
假设你开了一家网红奶茶店,顾客线上下单后:

  • 没有 Kafka:订单直接交给制作员,高峰期时订单堆积,店员手忙脚乱,容易漏单或出错。
  • 使用 Kafka:订单统一送到「智能分拣中心」(Kafka),自动按顺序排队、分发到多个制作台,还能:
    • 抗拥堵:即使瞬间涌入1000单,系统也能平稳处理(高吞吐)。
    • 防丢单:所有订单记录在系统,即使停电也能恢复(持久化)。
    • 灵活扩展:新增制作台(消费者)即可提高产能(水平扩展)。

好处总结
订单不丢失(数据可靠)
高峰期不卡顿(高并发)
分工更高效(解耦生产与消费)

就像分拣中心让奶茶店忙而不乱,Kafka 让数据流井然有序

RabbitMQ 就像「餐厅服务员」

生活案例
假设你经营一家火锅店,顾客通过扫码点餐:

  • 没有 RabbitMQ:订单直接打印到后厨,所有菜品一起处理,导致「毛肚等10分钟,青菜却先上」的混乱情况。
  • 使用 RabbitMQ:订单交给「智能服务员」(RabbitMQ),它会:
    • 按规则分发
      • 肉类订单优先给切肉间(优先级队列
      • 饮料订单直达吧台(路由规则
    • 灵活应对需求
      • VIP订单插队处理(消息优先级
      • 某菜品缺货时,自动通知顾客(死信队列

好处总结
精准投递(消息按规则路由)
忙闲调配(消费者动态伸缩)
异常处理(订单失败自动重试/补偿)

就像专业服务员让火锅店井井有条,RabbitMQ 让业务消息精准可控


对比记忆

  • Kafka:快递分拣中心(海量订单快速流转
  • RabbitMQ:餐厅服务员(精细调度每个任务

面试

面向对象编程(OOP)的七大原则通常指的是SOLID原则加上额外的三个原则,这些原则帮助开发者创建更加健壮、可维护和灵活的软件系统。以下是这七个原则的简要介绍:

  1. 单一职责原则(Single Responsibility Principle, SRP)

    • 一个类应该只有一个引起它变化的原因,即一个类只负责一项职责。这样可以使类更加专注于执行单一功能,从而提高内聚性。
  2. 开放封闭原则(Open/Closed Principle, OCP)

    • 软件实体(类、模块、函数等)应该是可以扩展的,但是不可修改。这意味着你可以通过添加新的代码来扩展功能,而不是改变现有的代码,有助于减少错误的风险。
  3. 里氏替换原则(Liskov Substitution Principle, LSP)

    • 子类应当能够替换它们的基类而不影响程序的正确性。也就是说,使用基类的地方都可以透明地使用子类而不会出现问题。
  4. 接口隔离原则(Interface Segregation Principle, ISP)

    • 不应该强迫客户端依赖于他们不使用的接口。换句话说,应将庞大的接口拆分成小而具体的接口,使得实现类只需关注自己需要的接口方法。
  5. 依赖倒置原则(Dependency Inversion Principle, DIP)

    • 高层模块不应该依赖于低层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。该原则提倡使用接口或抽象类来减少具体实现之间的耦合度。
  6. 迪米特法则(Law of Demeter, LoD)

    • 也被称为最少知识原则,指一个对象应当尽可能少地了解其他对象。具体来说,对象仅与直接的朋友交流,避免内部信息暴露给不必要的对象,增强封装性并降低耦合度。
  7. 组合/聚合复用原则(Composite/Aggregate Reuse Principle, CARP)

    • 在新对象中尽量使用已有的对象去组合,而不是通过继承来达到复用的目的。相比继承,组合提供了更大的灵活性,并且减少了由于层次结构带来的复杂性和风险。

HTTP协议结构

HTTP(HyperText Transfer Protocol,超文本传输协议)是用于分布式、协作式和超媒体信息系统的应用层协议。它是互联网数据通信的基础,特别是在浏览器与服务器之间的交互中扮演着关键角色。HTTP协议的请求和响应消息由以下几个主要部分组成:

  1. 起始行(Start Line)

    • 请求行(Request Line):在请求消息中,它包含了请求方法(如GET、POST)、请求的URI(统一资源标识符)以及使用的HTTP版本。
    • 状态行(Status Line):在响应消息中,它包括了HTTP版本、状态码(表明请求的结果如何)以及状态描述。
  2. 头部(Headers)

    • 包含关于请求或响应的元数据,比如内容类型、字符编码、缓存指令等。分为通用头、请求头、响应头和实体头。
  3. 空行(Blank Line)

    • 用来分隔头部和接下来的消息体。
  4. 消息体(Body)

    • 可选的,包含实际传输的数据,如HTML文档、图片或其他类型的文件。并非所有的HTTP请求和响应都包含消息体。

23种设计模式概览

创建型模式(Creational Patterns)

  1. 单例模式(Singleton):确保一个类只有一个实例,并提供全局访问点。
  2. 工厂方法(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。
  3. 抽象工厂(Abstract Factory):提供一系列相关或依赖对象的接口,而无需指定它们具体的类。
  4. 建造者模式(Builder):将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
  5. 原型模式(Prototype):通过复制现有实例来创建新实例,而不是通过实例化类来创建。

结构型模式(Structural Patterns)

  1. 适配器模式(Adapter):允许原本由于接口不兼容而不能一起工作的类能够协同工作。
  2. 桥接模式(Bridge):将抽象部分与它的实现部分分离,使它们都可以独立地变化。
  3. 组合模式(Composite):允许你将对象组合成树形结构来表示“部分-整体”的层次结构。
  4. 装饰模式(Decorator):动态地给一个对象添加一些额外的职责。
  5. 外观模式(Facade):为复杂的子系统提供一个统一的接口,简化其使用。
  6. 享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象。
  7. 代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问。

行为型模式(Behavioral Patterns)

  1. 观察者模式(Observer):定义一种一对多的依赖关系,当一个对象状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
  2. 策略模式(Strategy):定义一系列算法,把它们一个个封装起来,并使它们之间可以互换使用。
  3. 模板方法(Template Method):定义一个操作中的算法骨架,而将一些步骤延迟到子类中。
  4. 解释器模式(Interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
  5. 责任链模式(Chain of Responsibility):避免请求发送者与接收者耦合在一起,让多个对象都有机会处理请求。
  6. 命令模式(Command):将请求封装成对象,从而使你可以用不同的请求对客户进行参数化。
  7. 迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
  8. 中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。
  9. 备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
  10. 状态模式(State):允许对象在其内部状态改变时改变它的行为。
  11. 访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。

TCP3次握手和4次挥手

TCP三次握手

三次握手的主要目的是在客户端和服务器之间建立一条可靠的双向通信通道。它确保双方都准备好进行数据传输,并且能够同步初始序列号。以下是三次握手的过程:

  1. 第一次握手:客户端发送一个SYN(同步序列编号,Synchronize Sequence Numbers)包到服务器,表示请求建立连接,同时附带客户端的初始序列号。

  2. 第二次握手:服务器接收到客户端的SYN包后,需要回复一个ACK(确认字符,Acknowledgement)包作为响应,表示已经接收到客户端的请求,并且也会发送自己的SYN包给客户端,这个步骤同时包含了对客户端SYN的确认以及服务器端的SYN请求。此时,服务器进入了SYN_RECEIVED状态。

  3. 第三次握手:客户端收到服务器的SYN+ACK包后,会再次发送一个ACK包给服务器以确认连接,之后双方进入ESTABLISHED状态,即连接已建立,可以开始数据传输。

TCP四次挥手

四次挥手用于安全地关闭TCP连接,确保双方都已经完成了数据交换并且释放了资源。由于TCP连接是全双工的,意味着每个方向都需要独立关闭,因此需要四次挥手来完成这一过程:

  1. 第一次挥手:主动关闭方(通常是客户端或首先决定结束会话的一方)发送一个FIN(结束标志,Finish),表示希望断开连接。

  2. 第二次挥手:被动关闭方(通常是服务器)接收到FIN后,会发送一个ACK作为响应,确认收到了对方的关闭请求。此时,主动关闭方到被动关闭方的方向上的连接被关闭,但被动关闭方到主动关闭方的方向仍可继续传输数据。

  3. 第三次挥手:当被动关闭方也准备好了关闭连接时(即所有数据都已发送完毕),它会发送一个FIN给主动关闭方。

  4. 第四次挥手:主动关闭方收到FIN后,会发送最后一个ACK作为响应,然后等待一段时间(通常称为TIME_WAIT状态),确保被动关闭方收到了确认信息。一旦这个时间段结束,连接才完全关闭。

从url回车到页面显示发送了什么?

从你在浏览器地址栏输入一个URL并按下回车键,直到页面完全显示出来,这个过程涉及多个步骤和协议。以下是详细的流程:

1. DNS查询

  • 域名解析:首先,浏览器需要将你输入的URL中的域名转换成IP地址。它会检查本地DNS缓存,如果没有找到对应的IP地址,则会向配置的DNS服务器发起查询请求。
  • 递归查询:如果本地DNS服务器没有记录,它将继续向其他DNS服务器查询,直到获得目标域名的IP地址。

2. 建立TCP连接

  • 三次握手:一旦获取了服务器的IP地址,浏览器会与该服务器建立TCP连接。这通过三次握手完成:
    1. 客户端发送SYN包到服务器。
    2. 服务器回应SYN-ACK包给客户端。
    3. 客户端再发送ACK包确认连接,至此连接建立成功。

3. 发送HTTP/HTTPS请求

  • HTTP请求:如果是HTTP请求,浏览器直接发送请求报文到服务器;如果是HTTPS请求,则在发送前还需要进行SSL/TLS握手以加密通信。
  • HTTPS握手:包括证书验证、密钥交换等步骤,确保数据传输的安全性。

4. 接收响应

  • 服务器处理:服务器接收到请求后,根据请求的内容(如GET请求),查找相应的资源或执行相应的逻辑,并生成HTTP响应返回给客户端。
  • 状态码和消息体:响应包含状态码(如200表示成功)、响应头以及响应体(实际要展示的内容)。

5. 渲染页面

  • HTML解析:浏览器开始解析返回的HTML文档,构建DOM树。
  • CSS解析与渲染:同时加载外部样式表并解析CSS,构建CSSOM树,然后结合DOM树生成渲染树。
  • JavaScript执行:遇到<script>标签时,暂停HTML解析,下载并执行脚本文件,之后继续解析剩余的HTML。
  • 布局与绘制:计算每个节点的位置大小等信息,完成布局后,将内容绘制到屏幕上。

6. 加载附加资源

  • 异步加载:在整个过程中,浏览器还会异步加载图片、视频、字体等资源,并可能再次触发上述部分流程(如DNS查询、建立TCP连接等)。

7. 结束

  • 当所有资源都已加载完毕且页面完全渲染好后,用户就可以看到最终的网页了。

整个过程包含了网络通信、协议处理、安全措施以及前端技术的应用等多个方面,是现代Web体验的基础。

RabbitMQ简介

RabbitMQ在项目中主要用于实现系统间的异步通信和解耦,通过消息队列的方式让不同的服务能够独立地发送和接收消息,从而提高系统的响应速度和稳定性。它支持多种消息传递模式,如点对点和发布/订阅,适用于处理耗时任务的异步执行、流量削峰、日志记录以及构建高可用性和可扩展性的分布式应用,确保即使在高负载情况下也能平稳运行并有效管理服务间的交互。简而言之,RabbitMQ帮助项目实现了更高效、灵活且可靠的服务间通信机制。

RabbitMQ 深入讲解

RabbitMQ 是一个开源的消息代理软件(消息队列),它实现了高级消息队列协议(AMQP)。RabbitMQ主要用于在分布式系统中提供可靠的消息传递服务,支持多种消息传递模式,并具有高度的可扩展性和灵活性。

  • 核心概念

    • 生产者(Producer):发送消息的应用程序。
    • 交换机(Exchange):接收生产者发送的消息,并根据路由键(Routing Key)将消息转发到相应的队列。
    • 队列(Queue):存储消息直到被消费者处理。
    • 绑定(Binding):定义了交换机和队列之间的关系,包括路由规则。
    • 消费者(Consumer):接收并处理队列中的消息的应用程序。
  • 消息传递模式

    • 简单模式(Simple/Around-robin Dispatching):每个消息都被发送给下一个消费者。
    • 发布/订阅模式(Publish/Subscribe):广播消息给所有消费者。
    • 路由模式(Routing):基于路由键精确地发送消息给特定消费者。
    • 主题模式(Topics):使用通配符路由键灵活地匹配消息到消费者。
  • 特性

    • 支持消息持久化、确认机制确保消息不丢失。
    • 提供高可用性配置,如集群、镜像队列等。
    • 支持多种编程语言客户端,易于集成到现有系统中。

Elasticsearch 深入讲解

Elasticsearch 是一个分布式的搜索和分析引擎,基于Apache Lucene构建,专为大规模数据的实时搜索和分析而设计。它非常适合全文搜索、日志分析、应用监控等多种场景。

  • 核心概念

    • 索引(Index):类似于传统数据库中的“数据库”,是文档的集合。
    • 类型(Type):早期版本中类似表的概念,但在较新版本中已被弃用,推荐使用单一类型。
    • 文档(Document):以JSON格式存储的基本信息单元,对应于传统数据库中的行。
    • 映射(Mapping):定义文档及其字段如何被存储和索引。
    • 分片(Shards):索引可以分成多个分片,允许水平扩展。
    • 副本(Replicas):为了提高容错性和查询性能,每个分片可以有多个副本。
  • 功能特点

    • 全文搜索:强大的倒排索引技术实现快速准确的全文检索。
    • 聚合分析:支持复杂的统计分析和数据汇总。
    • 近实时搜索:新增或修改的数据可以在秒级内被搜索到。
    • 高可用性与扩展性:通过分片和副本机制支持大规模数据集和高并发访问。
  • 应用场景

    • 日志和事件数据分析。
    • 实时应用程序监控和报警。
    • 构建搜索引擎前端,如网站搜索、电子商务搜索等。

总之,RabbitMQ专注于解决分布式系统中的消息传递问题,提供可靠的异步通信机制;而Elasticsearch则致力于数据的搜索和分析,尤其擅长处理非结构化数据的快速检索和复杂查询。两者在现代软件架构中扮演着不同的角色,但都能显著提升系统的功能性和效率。

JVM调优

首先表态

如果使用合理的JVM参数配置,在大多数情况下应该是不需要进行额外的调优的。合理的初始配置通常能够满足大部分应用的需求。

其次说明

尽管如此,仍然存在少量场景需要进一步调优。我们可以对一些JVM核心指标配置监控告警,当出现波动时人为介入分析评估。这些指标包括但不限于内存使用情况、垃圾回收频率和持续时间等。

实际的调优例子

假设有一个Web应用在高负载下响应变慢,并且频繁出现Full GC导致长时间停顿。初步分析后发现是由于老年代内存不足引起。此时可以考虑如下调整:

  1. 增加堆内存大小

    • 将最大堆大小(-Xmx)和初始堆大小(-Xms)设置为相同的值,以避免动态扩展带来的性能损耗。例如,从2GB增加到4GB。
      1
      -Xms4G -Xmx4G
  2. 调整年轻代大小

    • 根据应用的特点调整年轻代大小(-Xmn)。较大的年轻代可以减少Minor GC次数但会增加每次GC的时间;较小的年轻代则相反。例如,可以尝试将年轻代设置为堆内存的三分之一。
      1
      -Xmn1.5G
  3. 选择合适的垃圾收集器

    • 切换到G1垃圾收集器(-XX:+UseG1GC),并根据应用需求微调G1相关的参数,例如期望的最大GC暂停时间。
      1
      -XX:+UseG1GC -XX:MaxGCPauseMillis=200
  4. 启用详细日志记录

    • 通过设置详细的GC日志记录来帮助分析GC行为。
      1
      -XX:+PrintGCDetails -XX:+PrintGCDateStamps
  5. 监控与分析

    • 使用工具如JVisualVM或JConsole监控JVM的运行状况,包括CPU、内存使用情况、线程信息等。
    • 设置监控告警,当出现异常波动时及时介入分析。

通过上述步骤,可以在特定场景下有效优化JVM的性能。合理配置JVM参数并结合有效的监控和分析工具,可以帮助我们更好地理解和优化应用程序的运行状态。