This commit is contained in:
2026-02-13 23:38:38 +08:00
commit 0207414c50
551 changed files with 35558 additions and 0 deletions

19
面试/东软.md Normal file
View File

@@ -0,0 +1,19 @@
Collectuons集合
Java stream lanbada表达式的应用
error exception
项目里面的全局异常处理
spring注解
controller restcontroller
事务失效
事务的传播级别
你用过什么数据库
什么字段添加索引
内链接和外链接
sql注入
前端的知识
linux常用的命令
一个文件把文件传送到另一台服务器的命令
文本文件 内容清空 然后文件保存

919
面试/八股文/Spring.md Normal file
View File

@@ -0,0 +1,919 @@
## 什么是循环依赖
多个Bean 相互注入 相互依赖 那么就到底先加载谁??? 就卡住了 报错了
## 如何解决循环依赖
关键**`提前暴露还未创建完成的Bean`**
方法:**三级缓存**
1. 一级缓存
- 用于存储**完全初始化**的完整单例化的Bean
2. 二级缓存
- **尚未*完全初始化*** 但是**已经实例化**的Bean ,用于**提前暴露对象****避免循环依赖问题**
3. 三级缓存
- 用于存储对象工厂 当需要时 可以通过工厂创建早期的Bean特别是为了支持AOP代理对象的创建
解决步骤:
- Spring先**创建Bean的实例** 将其***加入三级缓存***中
- 当一个Bean需要依赖另一个未初始化的Bean的时候 Spring会从**三级缓存中获取Bean工厂**,并**生成该对象**
- 代理对象**存入二级缓存**,解决依赖
- 一旦所有依赖Bean被完**全初始化**Bean将**转移到一级缓存**中
### 扩展
### 只有是依赖的**Bean是单例**
什么是单例模式 就是一个Bean他只能有一个实例化
那么因此Bean1需要2的时候 就创建B B创建的时候需要A 那么正好
但是如果是原型模式的话 那么A需要B B需要A的时候就会创建A2 但是A2也需要B 因此就会创建B2 .....循环往复 周而复始
<img src="F:\记录\八股文\image-20251024154932330.png" alt="image-20251024154932330" style="zoom:50%;" />
### 并且 必须`不全是`构造器注入 && beanName字母序在前的不能是构造器注入
背景Spring 创建Bean分为三部
1. 实例化 --》createBeanInstance 就是new了一个对象
2. 属性注入 --》populateBean 就是set一些属性
3. 初始化 --》 initializeBean 执行一些aware接口中的方法 initMethod AOP代理
所以 当我们全部都使用构造器注入的时候 newA(B b) 就回去执行 new B(A a) 但是tmdA没有构造完啊 那他妈怎么办
还能怎么办 换一种思路 那就是set注入
<img src="F:\记录\八股文\image-20251024160334418.png" alt="image-20251024160334418" style="zoom:50%;" />
### 那么我们再次回到创建Bean的三级缓存中 重新回顾一下
1. 首先获取单例的Bean 的时候会通过BeanName去找一级缓存 查找完整的Bean 如果找到就返回 找不到就去2
2. 看看对应的Bean是否在创建中如果不在直接返回找不到NULL 如果找到 则去二级缓存中找BEAN 找到就返回 没有就3
3. 去三级缓存中 通过BeanName找到对应的工厂 如果有工厂则创建Bean 且放到earlySingletonObjects中
4. 三级缓存都没有找到 那么就返回null
我们发现在2中如果Bean没有创建 则是返回null 那么返回了null 以后呢就是去创建这个Bean了 就调用 createBean 创建Bean 调用的方法就是docreateBean 那么这个docreateBean 是干什么的呢 就是`` 实例化 初始化 属性注入``
### 在实例化了一个Bean以后
就会在**addSingletonFactory**中塞入一个工厂 然后这个工厂调用一个方法addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 我们就可以得到这个Bean
要注意,此时 Spring 是不知道会不会有循环依赖发生的,但是它不管,反正往 singletonFactories 塞这个工厂,这里就是`提前暴露`。 重点就是在对象实例化之后,都会在三级缓存里加入一个工厂,提前对外暴露还未完整的 Bean这样如果被循环依赖了对方就可以利用这个工厂得到一个不完整的 Bean破坏了循环的条件。
### Tip:
![image-20251024161935634](F:\记录\八股文\image-20251024161935634.png)
### Spring 二级缓存行不行 不要三级了
先说原因AOP代理和Bean早期的引用问题
如果只是解决循环依赖 那么很简单 二级就可以 但是在AOP的时候 直接使用二级而不做任何处理 会导致我们拿到的Bean 是 **未代理的原始对象** 如果二级都存的是这个 就违反了Bean的生命周期
破化了**Bean 初始化流程中 “代理对象生成” 与 “依赖注入” 的衔接环节**
**若 Bean 需要 AOP 代理,最终暴露给容器和其他依赖 Bean 的必须是 “代理对象”,而非原始对象**
## Bean的生命周期
`实例化` --》 `属性注入`--》 初始化前的扩展机制--》 初始化前--》 `初始化`--》 初始化后--》 `使用`--》`销毁`
实例化: SPring容器 根据配置文件或注解实例化Bean对象
属性注入: Spring将依赖setter/字段注入) 注入到 Bean实例中
初始化前的扩展机制: if(Bean对象实现了BeanNameAware... aware接口)则执行aware注入
初始化前: 可以通过BeanPostProcessor接口对Bean进行一些额外的处理 比如可以获取Bean的名字 工厂 ApplicationContext等容器资源
初始化: - `InitializingBean` 接口提供了一个 `afterPropertiesSet` 方法,用于在 Bean 的所有属性设置完成后执行一些自定义初始化逻辑。开发者也可以通过 `@PostConstruct` 注解或者 XML/Java 配置中的 `init-method` 属性,来指定初始化方法。
初始化后:
使用Bean
销毁调用DisposiableBean 接口提供了destory方法
<img src="F:\记录\八股文\image-20251024164026531.png" alt="image-20251024164026531" style="zoom:67%;" />
## SPring 核心特性
Spring框架核心特性包括
- **IoC容器**Spring通过控制反转实现了对象的创建和对象间的依赖关系管理。开发者只需要定义好Bean及其依赖关系Spring容器负责创建和组装这些对象。
- **AOP**面向切面编程允许开发者定义横切关注点例如事务管理、安全控制等独立于业务逻辑的代码。通过AOP可以将这些关注点模块化提高代码的可维护性和可重用性。
- **事务管理**Spring提供了一致的事务管理接口支持声明式和编程式事务。开发者可以轻松地进行事务管理而无需关心具体的事务API。
- **MVC框架**Spring MVC是一个基于Servlet API构建的Web框架采用了模型-视图-控制器MVC架构。它支持灵活的URL到页面控制器的映射以及多种视图技术。
但是也可以这么分
`核心容器`
- SpringCore 包含了`DI 依赖注入 & Ioc 控制反转`
- SpringBean 管理Bean的定义和生命周期
- SpringContext 基于Core 和Bean的高级容器
- SpringExpressionLanguage 表达式语言
`Aop(切面编程)`
- SPringAop
`数据访问`
- SPringJDBC
- SpringORM :mybatis就是这个管着
- SpringTransaction` 事务管理`
`Web层`
- SpringWeb 提供基础的Web开发支持
- SpringMVC 实现了MCV model-controller-view 用于构建Http请求的Web应用
- SpringWebFlux
## Spring 的核心特征
DI IOC AOP
<img src="F:\记录\八股文\image-20251024183530002.png" alt="image-20251024183530002" style="zoom:50%;" />
## 什么是SPring Ioc
**def**:Inversion of Control 控制反转 ioc是通过DI实现的 IOC让对象的**创建与管理职责由容器负责**,而不是由对象自身控制
`核心思想` 对象的创建和依赖不是由对象本身实现的 而是由Spring容器管理的
好处: `灵活性 解耦`
DI 依赖注入dependency injection )通过`构造器、setter方法、接口注入` 将对象的依赖项传递给他 而不是对象自行创建依赖.
扩展IOC 只是一个思想
控制的是什么?? 控制的是` 对象的创建 `
反转的是什么?? 反转的是 `创建对象且注入依赖对象的这个动作`
反转之前 是我们在一个类里面new一个类 这个类的创建是我们创建的
而现在类的生命周期是由Spring控制的了
exSpring容器使用了工厂模式为我们创建了所需要的对象我们使用时不需要自己去创建直接调用Spring为我们提供的对象
即可,这就是控制反转的思想。
**好处**
例如,对象 A 需要依赖一个实现 B但是对象都由 IOC 控制之后,我们不需要明确地在对象 A 的代码里写死依赖的实现 B只需要写明依赖一个接口这样我们的代码就能顺序的编写下去。 然后,我们可以在配置文件里定义 A 依赖的具体的实现 B根据配置文件在创建 A 的时候IOC 容器就知晓 A 依赖的 B这时候注入这个依赖即可。 如果之后你有新的实现需要替换,那 A 的代码不需要任何改动,你只需要将配置文件 A 依赖 B 改成 B1这样重启之后IOC 容器会为 A 注入 B1。
## Spring IOC容器初始化过程
### 启动阶段
- 配置加载:加载配置文件或者配置类
- 创建容器Spring 创建IOC容器Beanfactory ApplicationContext准备加载和管理Bean文件
### Bean注册阶段
- 解析和注册BeanDefinitionReader读取配置中的Bean定义并将其注册到容器中形成BeanDefinition对象
### 实例化和依赖注入
- 实例化: 根据BeanDefinition创建Bean实例
- 依赖注入根据BeanDefinitio中的依赖关系可以通过 构造注入 setter注入 或者字段注入 将依赖注入到Bean里面
### 初始化
- BeanPostProcessor处理 这些处理器会在Bean初始化生命周期中加入定义的处理逻辑
- Aware接口调用 如果Bean实现了Aware接口BeanNameAware BeanFactoryAware Spring会回调这些接口传递容器相关的信息
- 初始化方法调用调用Bean的初始化方法
## 什么是Bean
任何通过Spring容器实例化的 组装和管理的JAVA对象都可以称为bean
**Bean定义方式**
1. XML ->就是
```xml
<bean id="mobian3" class="pers.mobian.springseventh.MoBian3XML">
id就是你给他定义的名字 class就是路径名字
```
2. 注解
```java
@C@Component
@Controlle
@Service
@Repository
```
3. java配置类
```java
@Configuration
@ComponentScan("pers.mobian.firstTest")
public class ContextConfig {
}
@Configuration
public class Config {
@Bean(name = "mobian666")
public MoBian moBian(){
return new MoBian();
}
}
```
## 什么是BeanFactory
Bean工厂 创建和管理Bean的 底层就是Ioc容器
负责从配置源 XML java配置类 注解之类的读取Bean的定义 负责她的生命周期
**延迟加载** -》延迟初始化 只有在Bean首次请求的时候才会实例化这个Bean 而不是程序已启动就开始
## FactoryBean
FactoryBean 可以自定义任何所需的初始化逻辑,生产出一些定制化的 bean简单来说就是 可以封装Bean
**FactoryBean 与普通 Bean 的区别**
- **创建逻辑不同**:普通的 Bean 直接由 Spring 容器管理,而 FactoryBean 通过自定义的 `getObject()` 方法创建实际的对象。
- **动态代理和复杂对象**FactoryBean 适用于创建动态代理或复杂的 Bean而普通 Bean 通常只处理简单的对象创建。
想要成为FactoryBean需要实现
```java
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory> {
@Override
public SqlSessionFactory getObject() throws Exception {
// 创建并配置 SqlSessionFactory如加载 MyBatis 配置、数据源等)
return new SqlSessionFactoryBuilder().build(config);
}
}
```
## Bean的作用域
**singleton**:默认是单例含义不用解释了吧一个IoC容器内部仅此一个
**prototype**:原型,多实例
**request**:每个请求都会创建一个属于自己的Bean实例这种作用域仅存在Spring Web应用中
**session**:一个Http Session 中有一个bean的实例这种作用域仅存在Spring Web应用中
**application**:整个ServletContext生命周期里只有一个bean,这种作用域仅存在Spring Web应用中
**websocket**: 一个WebSocket生命周期内一个bean实例这种作用域仅存在Spring Web应用中
## 什么是AOP(切面编程)
是什么?: 其实就是将与业务无关的一些东西 比如log 权限校验 安全检查 之类的)与业务逻辑分开
**AOP面向切面编程的核心**是将日志、事务、权限等与业务逻辑无关的**横切关注点**从业务代码中抽取出来,通过声明式的方式动态应用到业务方法中,避免这些逻辑直接嵌入业务代码导致的冗余与耦合。
**主要组成部分** 切面 连接点 通知 切入点 织入
**AOP是通过动态代理来实现的**
动态代理:运行时 将切面的逻辑放入
静态代理:编译期间/类加载期间
Aop的核心概念
- **Aspect**:切面,只是一个概念,没有具体的接口或类与之对应,是 Join pointAdvice 和 Pointcut 的一个统称。
- ```java
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
System.out.println("Logging before method execution");
}
}
```
- **Join point**:连接点,指程序执行过程中的一个点,例如方法调用、异常处理等。在 Spring AOP 中,仅支持方法级别的连接点。
- **Advice**通知即我们定义的一个切面中的横切逻辑有“around”“before”和“after”三种类型。在很多的 AOP 实现框架中Advice 通常作为一个拦截器,也可以包含许多个拦截器作为一条链路围绕着 Join point 进行处理。
- **Pointcut**:切点,用于匹配连接点,一个 AspectJ 中包含哪些 Join point 需要由 Pointcut 进行筛选。
- ```java
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
```
- **Introduc**
- **tion**:引介,让一个切面可以声明被通知的对象实现任何他们没有真正实现的额外的接口。例如可以让一个代理对象代理两个目标类。
- **Weaving**:织入,在有了连接点、切点、通知以及切面,如何将它们应用到程序中呢?没错,就是织入,在切点的引导下,将通知逻辑插入到目标方法上,使得我们的通知逻辑在方法调用时得以执行。
## SpringAOP 默认是什么动态代理
Spring Framework 默认的是 JDK动态代理 SB 2x了以后 就是CGLIB
区别
**代理方式**
- JDK动态代理 基于**接口实现** 通过Proxy动态生成代理类
- CFLIB动态代理**基于类继承**,通过字节码技术生成目标类的子类
**使用场景**
- JDK动态代理 推荐用于`代理接口`的场景 适合代理的类实现了接口
- CGLIB动态代理 适合没有接口的类。`基于继承实现的`
## Spring 拦截连的实现
什么是拦截链 指一系列拦截器以此作用于请求或方法调用 实现横切关注点的处理
**主要方面**
- `HandlerInterceptor `MVC拦截器 用于拦截HTTP 请求并经行处理 实现`preHandle postHandle afterCompletion` 来实现拦截前中后 的方法
- `Filter` 基于servlet API 的过滤器 用于处理 跨域 安全验证 编码过滤 通过*`doFilter`*方法拦截请求
- `Aop切面` Spring AOP 提供的方法级别的拦截通过定义切面Aspect可以实现方法的前后处理。切面中的` @Before@After@Around` 等注解用于控制拦截的执行顺序。
这个底层的源码就是
有一个chain集合 这个集合里面存放着所有的interceptor的方法 然后每次通过index++来调取下一个方法 这个方法 被cglibmethodinvocation封装起来在proceed方法
## AOP与AspectJ有什么区别
- Aop 基于**动态代理** 在**运行时**的代理机制
- 轻量级 的SpringBean容器
- 适合大部分的业务场景 尤其是简单的AOP功能的Spring应用
AspectJ 更加强大
- 支持编译时 类加载时 运行时的AOP功能
- 支持更加灵活的切点和增强操作,提供编译期和加载期的织入方式,性能较高。
- 适合对性能要求较高或需要复杂切点匹配的场景,如日志、监控等。
## SpringMVC
M model 模型
V view 试图
C controller 控制
核心就是**DispatchServlet** 前端控制器。它通过注解、配置等方式 将Http的请求 映射到控制器方法中去 然后由Controller处理请求逻辑 并将数据返还给View层去渲染
Spring MVC 的工作流程可以分为以下几个关键步骤:
**客户端请求**:浏览器向服务器发送 HTTP 请求。
**DispatcherServlet**:所有的请求首先由 Spring MVC 的核心前端控制器 `DispatcherServlet` 接收,它充当整个流程的调度中心。
- ```java
@Controller --> 标记控制类
public class MyController {
@RequestMapping("/login") --> 方法和url的映射关系
public String mianshiya() {
return "哈哈哈";
}
}
```
**处理器映射Handler Mapping**`DispatcherServlet` 根据请求的 URL 使用处理器映射器找到对应的控制器Controller
**控制器Controller**:控制器接收请求并处理业务逻辑,通常通过注解 `@Controller` 和 `@RequestMapping` 定义请求的映射方法。
**模型和视图ModelAndView**:控制器处理完业务逻辑后,将数据封装到模型对象中,并指定返回的视图名称。
**视图解析器ViewResolver**`DispatcherServlet` 调用视图解析器,将逻辑视图名称解析为实际的视图,如 JSP、Thymeleaf 等模板引擎。
- ```java
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
//InternalResourceViewResolver等等就是一个视图解析器
@RequestMapping("/user")
public String getUser(@RequestParam("id") String userId) {
// 使用请求参数
}
@RequestMapping("/user/{id}")
public String getUserById(@PathVariable("id") String userId) {
// 使用路径变量
}
//@RequestParam获取请求参数
//@PathVariable 获取url的路径的变量
```
**视图渲染**:视图渲染引擎根据模型中的数据生成 HTML 页面并返回给客户端。
### 表单处理 & 数据绑定
数据绑定 就是通过`@ModelAttribute` 自动的将表单数据绑定到数据模型上面
```java
@RequestMapping("/submitForm")
public String submitForm(@ModelAttribute("user") User user) {
// 处理表单提交的数据
return "result";
}
```
表单校验
通过 `@Valid` 和`BindingResult` 可以处理表单之前就进行数据数据处理
```java
@RequestMapping("/submitForm")
public String submitForm(@Valid @ModelAttribute("user") User user, BindingResult result) {
if (result.hasErrors()) {
return "form";
}
return "result";
}
```
### 全局异常处理
通过` @ControllerAdvice `和 ` @ExceptionHandler(Exception.class)` 处理全局的异常
```java
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleException(Exception ex) {
// 处理异常
return "error";
}
}
```
<img src="F:\记录\八股文\image-20251025211652863.png" alt="image-20251025211652863" style="zoom:50%;" />
## Spring中的设计模式
<img src="F:\记录\八股文\image-20251025213007518.png" alt="image-20251025213007518" style="zoom: 50%;" />
<img src="F:\记录\八股文\image-20251025213031184.png" alt="image-20251025213031184" style="zoom: 50%;" />
## 脏读 不可重读 幻读
脏读Dirty Read一个事务**读取**了**另一个尚未提交**的事务的数据,如果该事务回滚,则数据是不一致的。
不可重复读Non-repeatable Read因为**其他事务修改了该数据并提交**,所以 在同一事务内的多次读取,前后数据不一致,。
幻读Phantom Read在一个事务内的多次查询查询结果集不同因为其他事务插入或删除了数据。
不可重复读 是在读的事务中其他事务update提交了。
幻读 是在读的事务中其他事务进行了insert或者delete。
## 事务隔离级别
### DEFAULT(默认)
默认通常为默认
### READ_UNCOMMITTED(读未提交)
- 最低的隔离级别
- 允许事务**读取**`尚未提交`的数据
- 可能会导致脏读、不可重读、幻读
### READ_COMMITTED(读已提交)
- 仅次于上面那位
- 仅允许读取已经提交的数据
- `避免了脏读` 但是还是**不可重读、幻读**有可能实现
### PEREATABLE_RAED(可重复读)
- 仅次于上面那位
- 确保在同一个事务内的``多次读取结果一致``
- `避免脏读和不可重复读`,但可能会有**幻读问题**
### SERIALIZABLE(可串行化)
- 最屌的那位
- 通过强事务按顺序执行
- 避免了所有的问题
<img src="F:\记录\八股文\image-20251027170227931.png" alt="image-20251027170227931" style="zoom: 50%;" />
```java
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void processTransaction() {
// 事务逻辑
}
```
## 事务的传播级别
前言:什么是传播?
就是当一个事务方法被另一个事务方法调用时这个事务方法应该如何进行。比如说A事务方法调用了B事务方法B是继续在调用者A的事务中运行呢还是为自己另开一个新事物运行 这就是由B的事务传播行为决定的。
正常来说有几种解决方案:
1. 融入事务:直接去掉 serviceB 中关于开启事务和提交事务的 begin 和 commit融入到 serviceA 的事务中 (直接用 ServiceA 的事务)。问题: B 事务的错误会引起 A 事务的回滚。
2. 挂起事务:如果不想 B 事务的错误引起 A 事务的回滚,可以开启两个连接,一个执行 A 一个执行 B互不影响执行到 B 的时候把 A 挂起新起连接去执行 BB 执行完了再唤醒 A 执行。
3. 嵌套事务: MySQL 中可以通过给 B 事务加 savepoint 和 rollback 去模拟嵌套事务,把 B 设置成伪事务。
spring 中的事务传播行为:
1. PROPAGATION_REQUIRED (需要):
- 需要事务。
- 如果存在一个事务,则直接使用当前事务。如果则开启一个新的事务。(A 如果存在事务,则 B 融入 A 事务,如果没有则新起一个事务)
2. SUPPORTS (支持):
- 支持 A 的事务。
- 如果存在一个事务,支持当前事务。如果则非事务的执行。(A 有,则 B 融入A 没有,则非事务执行)
3. MANDATORY (强制性):
- 强制什么?
- 强制必须有事务。如果已经当前存在一个事务,使用当前事务。没有则抛出异常。(A 有,则 B 融入A 没有,则抛异常)
4. REQUIRES_NEW (需要新的):
1. 如果一个事务已经存在,则先将这个存在的事务挂起。
2. 如果没有,则新起一个事务执行。(A 有,则将 A 事务挂起,新开一个事务来执行 B执行完后唤醒 A继续执行A 没有事务则新起一个事务执行 B)
5. NOT_SUPPORTED (不支持):
1. 不支持什么?不支持事务。
2. 总是非事务地执行,并挂起任何存在的事务。(A 有,则挂起 AB 非事务执行,执行完之后,唤醒 AA 继续执行A 还是以事务形式执行的)
6. NEVER (从不):
1. 总是非事务地执行,
2. 如果存在一个活动事务,则抛出异常。(A 有,则抛异常)
7. NESTED (嵌套的):
1. 如果一个活动的事务存在,则运行在一个嵌套的事务中。
2. 如果没有活动事务,则按 TransactionDefinition.PROPAGATION_REQUIRED 属性执行。(A 有,则 B 用 savepoint 方式嵌套执行与 A)
## 事务的传播有什么用
1. 控制事嵌套和传播
2. 确保独立操作的事务隔离
3. 控制事务的边界的一致性
<img src="F:\记录\八股文\image-20251027180538746.png" alt="image-20251027180538746" style="zoom: 50%;" />
## SpringAOP技术的相关术语
### 切面
切面是将通知应用到切入点的过程。切面通常由通知和切入点组成,用于定义增强逻辑的执行范围。
```java
@Aspect
public class LoggingAspect {
@Before("addPointcut()")
public void logBeforeAdd() {
System.out.println("前置通知:方法执行前");
}
@After("addPointcut()")
public void logAfterAdd() {
System.out.println("后置通知:方法执行后");
}
}
```
### 通知
通知是指在连接点上执行的增强逻辑。
- **前置通知**:在方法执行之前执行。
- **后置通知**:在方法执行之后执行。
- **环绕通知**:在方法执行前后都可以执行。
- **异常通知**:在方法抛出异常时执行。
- **最终通知**:在方法正常返回时执行。
- ```java
@Around("addPointcut()")
public Object aroundAdd(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知:方法执行前");
Object result = joinPoint.proceed(); // 执行目标方法
System.out.println("环绕通知:方法执行后");
return result;
}
```
### 切点
切入点是指我们实际增强的连接点。在Spring AOP中切入点是通过表达式定义的用于指定哪些连接点需要被增强。
```java
@Pointcut("execution(* com.example.User.add(..))")
public void addPointcut() {}
@Pointcut("execution(* com.example.User.delete(..))")
public void deletePointcut() {}
```
### 连接点
连接点是指程序执行过程中可以被增强的点。在Spring AOP中连接点通常是指方法的执行点。
假设我们有一个`User`类,其中包含四个方法:`add()`、`update()`、`select()`和`delete()`。这些方法都可以被增强,因此它们都是连接点。
```java
public class User {
public void add() {
System.out.println("执行添加方法");
}
public void update() {
System.out.println("执行更新方法");
}
public void select() {
System.out.println("执行查询方法");
}
public void delete() {
System.out.println("执行删除方法");
}
}
```
### 目标对象
给谁加 就谁是目标对象 很好理解
### 代理
- JDK动态代理
- CGLIB代理
### 织入
织入是将切面应用到目标对象的过程。织入可以发生在编译时、类加载时或运行时。在 Spring 中AOP 的织入通常发生在运行时。
- 使用 `@Aspect` 注解标记的类,会在运行时通过代理方式将切面织入目标类。
| 问题 | 答案 |
| ------------------------------ | ------------------------------------------------------------ |
| Q1: 什么是连接点? | A1: 连接点是指程序执行过程中可以被增强的点,通常是指方法的执行点。 |
| Q2: 切入点和连接点有什么区别? | A2: 连接点是程序中所有可以被增强的点,而切入点是指实际增强的连接点。 |
| Q3: 通知有哪些类型? | A3: Spring AOP支持五种类型的通知前置通知、后置通知、环绕通知、异常通知、最终通知。 |
| Q4: 切面的作用是什么? | A4: 切面是将通知应用到切入点的过程,用于定义增强逻辑的执行范围。 |
| Q5: 如何定义切入点? | A5: 通过Spring AOP的表达式语言如`execution()`)定义切入点。 |
## SPringBean 注册到容器的方式
- XML
- @Compoenet
- @Service@Controller@Repository是@Component行生注解
- @Configuration 和 @Bean
- ```java
@Configuration//声明配置类
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean(myDependency());
}
@Bean
public MyDependency myDependency() {
return new MyDependency();
}
}
```
- @Import注解
- ```java
@Configuration
@Import(MyComponent.class)
public class MainConfig {
// 当前配置类逻辑
}
public class MyComponent {
// 普通类逻辑
}
```
## 自动装配
### no 默认装配
默认方式 不进行自动装配 依赖关系必须显式定义
```xml
<bean id="myBean" class="com.example.MyBean">
<property name="myDependency" ref="myDependency"/>
</bean>
<bean id="myDependency" class="com.example.MyDependency"/>
```
### byName
根据名字定义
```xml
<bean id="myBean" class="com.example.MyBean" autowire="byName"/>
<bean id="myDependency" class="com.example.MyDependency"/>
```
### byType
要求容器中有且仅有一个符合类型的Bean
```xml
<bean id="myBean" class="com.example.MyBean" autowire="byType"/>
<bean id="myDependency" class="com.example.MyDependency"/>
```
### constructor
```xml
<bean id="myBean" class="com.example.MyBean" autowire="constructor"/>
<bean id="myDependency" class="com.example.MyDependency"/>
```
## @Conponent @Controller @Service @Repository
@Controller @Service @Repository都是@Conponent的衍生注解
@component:这是一个通用的注解,用于将任意类注册为 Spring 容器中的 Bean。它没有特定的语义适用于任何需要 Spring 管理的类
@contro11er:这是一个专门用于 Spring MVC 中的控制器(Controler)层的注解。用于处理 HTTP 请求,并将结果返回给客户端。
@service:用于标识业务逻辑层的类。它具有明确的语义,表明该类承担业务操作,主要用于编写服务类
@Repository:用于数据访问层(DAO)的类,与数据库交互。
```java
@Repository
public class UserRepository {
public User findUserById(Long id) {
// 从数据库中查询用户
return new User("John", "Doe");
}
}
```
## Spring启动过程
1. 加载配置文件 初始化文件
2. 实例化文件
- 根据配置中的信息创建文件 ApplicationContext
- 实例化BeanFactory 并且加载容器中的BeanDefination
3. 解析BeanDefination
4. 实例化Bean
5. 注入依赖
6. 处理Bean的生命周期
7. 处理BeanPostProcessor
8. 发布事件
9. 完成启动

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

170
面试/八股文/java.md Normal file
View File

@@ -0,0 +1,170 @@
## [java新特性]()
### java8
- **用元空间替代了永久代**
- **引用了Lambda表达式**
- **函数式接口**
- Predicate ->条件判断
- ```java
// Predicate<T> 用于条件判断
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate(); // 取反
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(isEven)
.collect(Collectors.toList());
```
- Function 数据转换
- ```java
// Function<T, R> 用于转换
Function<String, Integer> stringLength = String::length;
Function<Integer, String> intToString = Object::toString;
// 函数组合
Function<String, String> addPrefix = s -> "前缀-" + s;
Function<String, String> addSuffix = s -> s + "-后缀";
Function<String, String> combined = addPrefix.andThen(addSuffix);
String result = combined.apply("鱼皮"); // "前缀-鱼皮-后缀"
```
- **引入了日期类**
- 从原来的Date&Calendar 引入新的LoacalDate
- 解决了一些旧日历的线程安全..可变性...之类的
- **接口默认方法、静态方法**
- **接口升级**:比如 `Java 8` *中对 `Collection` 接口新增了 `stream()` 等默认方法,所有集合实现类(如 `ArrayList`、`HashMap`)无需修改即可直接使用这些方法。
- **提供通用实现**:接口中可以定义通用逻辑的默认方法,减少实现类的重复代码。
- **新增Stream流式接口**
- 新增一些中间的stream API 能够让对集合的处理更加的`优雅`
- filter()
- map()
- sorted()
- distinct()
- limit()
- skip()
- 也有一些终端的操作 让
- collection()
- foreach()
- count()
- findFirst()
- anyMatch()//是否有匹配的
- reduce()//归约操作
- **引入Optional类**
- **新增了CompletableFuture,StampedLock...并发实现类**
### java11
- HTTP客户端的API
- String类的新特性
- isBlank()-->判空
- strip() -->出去空白字符
- lines()-->将字符串分割成Stream
- repeat()-->重复字符串
- File类
- 改进了原先难用的FileReader FileWriter
- ```java
// 写入文件 Files.writeString
String content = "这是一个测试文件\n包含多行内容\n中文支持测试";
Path tempFile = Files.writeString(
Paths.get("temp.txt"),
content,
StandardCharsets.UTF_8
);
// 读取文件 Files.readString
String readContent = Files.readString(tempFile, StandardCharsets.UTF_8);
System.out.println("读取的内容:\n" + readContent);
```
### java17 `代码设计安全性` `JDK稳定性`
- **虚拟线程**
- 传统的线程 每一个线程都对应着一个真实的线程 那么 虚拟线程 就可以 栈内存按需分配(初始可小至 KB 级),创建 / 销毁成本极低。
- 可轻松创建百万甚至千万级
- 由 JVM 的**虚拟线程调度器**管理,最终映射到平台线程执行。
- 使用简单
- ```java
// 直接创建虚拟线程
public void handleSingleUser(Long userId) {
Thread.ofVirtual().start(() -> {
// 要异步执行的任务
User user = userService.findById(userId);
processUser(user);
});
}
```
- **Switch匹配方式重大跟新**
- 支持条件判断
- 可以匹配对象类型 然后直接转换 优雅~~
- **Record模式不想看 极简化不是我的代码风格)**
- 记住一次性可以提取所有的信息即可
### java25
- **Scoped** **Value**
- 这个方法允许在`线程`内以及`子线程`之间安全高效的`共享不可变数据`
- 并且开销小 why
- 因为是这样的比如一个线程里面有一个资源 你开1000个 总不能复制1000个吧 不然太慢了 所以ScopedValue 就是为了不让复制1000个而产生的
## 双亲委派模型
**Def**:当类加载器 去加载某个类的时候 会委派父类加载器 ---直到->根类加载器 BootStrap ClassLoader ,当父类的无法加载时 才给本类加载
**为了什么?:**
1. 安全性 比如java.lang.object 只能由根类加载器使用 因此如果有人替换了Object也没事 、
2. 一致性 保证一个类在JVM中只会被加载一次 让我们每次用的都是用一个类对象
3. <img src="F:\记录\八股文\image-20251024152138996-1761290506783-1.png" alt="image-20251024152138996" style="zoom:33%;" />
三种类加载器
1. 启动类加载器 --》虚拟机的一部分 由C++实现的 负责加载<JAVA_HOME>\lib 目录中或被 -Xbootclasspath 指定的路径中的并且文件名是被虚拟机识别的文件。
2. 扩展类加载器--》java实现的独立于虚拟机 负责加载 <JAVA_HOME>\lib\ext 目录中或被 java.ext.dirs 系统变量所指定的路径的类库。
3. 应用程序类加载器--》java实现的独立于虚拟机 主要负责加载用户类路径 ( classPath ) 上的类库,如果我们没有实现自定义的类加载器那这玩意就是我们程序中的默认加载器。

254
面试/八股文/并发.md Normal file
View File

@@ -0,0 +1,254 @@
## JMM
JMM 是专门解决多线程并发问题的一套规则
核心就是`可见性、原子性、有序性`
可见性: 像volatile关键字 就是保证了 所有线程见到的数据是一样的
原子性JMM 里用 synchronized 或者 Lock 锁就能保证原子性 保证了一个程序的步骤不会被打断
有序性:编译器或者 CPU 为了提速,会在不影响单线程结果的情况下调整指令顺序。但多线程下,另一个线程拿到 a 的引用时A 可能还没初始化好,一调用就报错。这时候 volatile 或者 synchronized 就能通过内存屏障阻止这种重排序,保证顺序正确。
## 如何保证数据的一致性
- 事务管理
- 数据库的事务
- 读未提交 读已提交 可重复读 串行化
- 锁机制
- sychornized ReenrantLock
- 版本控制
- 通过乐观锁的方式 在更新数据时记录版本信息
## 线程创建的方法
1. 继承`Thread`类 然后通过`run`方法 使用的话 就是.start()
2. 实现Runnable接口 重写`run`方法
3. 实现Callable接口与FutureTask
1. ```JAVA
class MyCallable implements Callable<Integer> {
@Overridepublic Integer call() throws Exception {
// 线程执行的代码这里返回一个整型结果return 1;}}
public static void main(String[] args) {
MyCallable task = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(task);
Thread t = new Thread(futureTask);t.start();
try {Integer result = futureTask.get();
// 获取线程执行结果
System.out.println("Result: " + result);}
catch (InterruptedException | ExecutionException e)
{
e.printStackTrace();
}
}
```
4. 使用线程池Executor框架
## Sleep和wait的区别
<img src="F:\记录\八股文\image-20251028191927109.png" alt="image-20251028191927109" style="zoom:67%;" />
## blocked和waiting有啥区别
`blocked`
- 主要是因为sychon了没有抢到锁 导致进入了blocked
- blocked可以通过获得锁 主动的唤醒
`waiting`
- 线程进入WAITING状态是因为它正在等待另一个线程执行某些操作例如调用Object.wait()方法、Thread.join()方法或LockSupport.park()方法。在这种状态下线程将不会消耗CPU资源并且不会参与锁的竞争
- waiting主要是通过主动的唤醒 notify
## notify 和 notifyAll 的区别?
同样是唤醒等待的线程,同样最多只有一个线程能获得锁,同样不能控制哪个线程获得锁。 区别在于:
- notify唤醒一个线程其他线程依然处于wait的等待唤醒状态如果被唤醒的线程结束时没调用notify其他线程就永远没人去唤醒只能等待超时或者被中断
- notifyAll所有线程退出wait的状态开始竞争锁但只有一个线程能抢到这个线程执行完后其他线程又会有一个幸运儿脱颖而出得到锁
## 如何停止一个线程
**核心**:通过协作式的逻辑控制线程中止
1. 通过共享标志位主动停止 就是线程控制一个公共的变量 然后通过那个变量来关闭线程
2. 线程中断机制 `Thread.interrupt()`
3. 通过`Future`取消任务 Future.cancel()
4. 资源关闭
<img src="F:\记录\八股文\image-20251028194534789.png" alt="image-20251028194534789" style="zoom:80%;" />
#
# 并发安全
## JUC包常用的类
### 线程池相关
- `**ThreadPoolExecutor **` **最核心的线程池类** 用于创建和管理线程池 通过它可以灵活地配置线程池的参数,如核心线程数、最大线程数、任务队列等,以满足不同的并发处理需求。
- **Executor **线程池工厂类 提供了一系列的方法来创建不同类型的线程池
### 并发集合类
- **ConcurrentHashMap **线程安全的哈希表 允许多个线程同时访问不同的段,提高了并发性能,在高并发场景下比传统的`Hashtable`性能更好。
- **CopyOnWriteArrayList **线程安全的列表将修改操作应用到新数组上而读操作仍然可以在旧数组上进行,从而实现了读写分离提高了并发读的性能`,适用于读多写少的场景。`
### 同步工具类
- **CountDownLatch** 允许一个或多个线程等待其他一组线程完成操作后再继续执行。 就是通过一个计数器 每次用完以后 调用**countDown ** 就-1当计数器为零时等待的线程可以继续执行。常用于多个线程完成各自任务后再进行汇总或下一步操作的场景。
- **CyclicBarrier** `可以重复使用`当所有线程都通过屏障后,计数器会重置,可以再次用于下一轮的等待。
- **Semaphore **信号量,用于控制同时访问某个资源的线程数量。
### 原子类
- **AtomicInteger **原子整数类,提供了对**整数类型的原子操作**
- **AtomicReference** 原子引用类,用于对 **对象引用**进行原子操作。
### 怎么保证多线程安全?
- **synchronized关键字**: 同步代码块或方法
- **volatile关键字**: `volatile`关键字用于变量,确保所有线程看到的是该变量的最新值
- **Lock接口和ReentrantLock类**: `Lock`接口提供了比`synchronized`更强大的锁定机制 `ReentrantLock`是一个实现该接口的例子,提供了更灵活的锁管理和更高的性能。
- **原子类** `ReentrantLock`是一个实现该接口的例子,提供了更灵活的锁管理和更高的性能。
- ```java
AtomicInteger counter = new AtomicInteger(0);
int newValue = counter.incrementAndGet();
```
- **线程局部变量**
`ThreadLocal`类可以为每个线程提供独立的变量副本, 这样每个线程都拥有自己的变量,消除了竞争条件。
```java
ThreadLocal<Integer> threadLocal new ThreadLocal<>();
threadLocal.set(数值)
int value = threadLocalVar.get();
```
- **并发集合**
CopyOnWriteArrayList ConcurrentHashMap 等 就是自己就是实现了线程安全的集合
- **JUC工具类**:
工具类可以用于控制线程间的同步和协作。 Semaphore``CyclicBarrier
## java常见的锁
- **内置锁synchronized**
- 当一个线程进入`synchronized`代码块或方法时,它会获取关联对象的锁;当线程离开该代码块或方法时,锁会被释放。如果其他线程尝试获取同一个对象的锁,它们将被阻塞,直到锁被释放。
- 分类 **无锁、偏向锁、轻量级锁和重量级锁**
- 无锁
- 偏向锁 当一个线程进入同步块时,如果没有任何其他线程竞争,就会使用偏向锁 **以减少锁的开销**
- 轻量级锁 使用线程``上的数据结构,避免了操作系统级别的锁。
- 重量级锁 涉及``操作系统级``的互斥锁。
- **ReentrantLock**
- 是一个显式的锁类,如可中断的锁等待、定时锁等待、公平锁选项等
- 使用`lock()`和`unlock()`方法来获取和释放锁。
- 公平锁按照线程请求锁的顺序来分配锁,保证了锁分配的公平性,但可能增加锁的等待时间。
- 非公平锁不保证锁分配的顺序,可以减少锁的竞争,提高性能,但可能造成某些线程的饥饿。
- **读写锁**ReadWriteLock
- 允许多个读取者同时访问共享资源,但只允许一个写入者。
- 读写锁通常用于读取远多于写入的情况,以提高并发性。
- **乐观&悲观锁**
- 乐观锁 **通常不锁定资源,而是在更新数据时检查数据是否已被其他线程修改。**
- 使用版本号或时间戳来实现。
- 悲观锁 **通常指在访问数据前就锁定资源,假设最坏的情况** --》即数据很可能被其他线程修改
- `synchronized`和`ReentrantLock`都是悲观锁的例子
- **自旋锁**
- 锁机制
- 线程在等待锁的时候 会循环检查锁是否可用 而不是放弃CPU并阻塞
- 通常可以使用CAS来实现
- 这在锁等待时间很短的情况下可以提高性能但过度自旋会浪费CPU资源。
## CountDownLatch 是做什么的讲一讲
并发包中的一个同步工具类,**用于让一个或多个线程等待其他线程完成操作后再继续执行**。
本质就是一个计数器 然后
- **初始化计数器**:创建 `CountDownLatch` 时指定一个初始计数值(如 `N`)。
- **等待线程阻塞**:调用 `await()` 的线程会被阻塞,直到计数器变为 0。
- **任务完成通知**:其他线程完成任务后调用 `countDown()`,使计数器减 1。
- **唤醒等待线程**:当计数器减到 0 时,所有等待的线程会被唤醒。
## synchronized
会在编译之后在同步的代码块前后加上**monitorenter**和**monitorexit**字节码指令
**monitorenter**尝试获取对象锁 对象没有被锁定或者已经获得了锁 锁的计数器+1
**monitorexit**指令时则会把计数器-1
当计数器值为0时则锁释放处于等待队列中的线程再继续竞争锁
- **主要作用**:就是实现**原子性操作**和**解决共享变量的内存可见性**问题
- 是一种 `排他锁` 当一个线程获得锁之后其他的线程必须等待线程释放以后才能获得锁
- 线程被阻塞或者唤醒时时会从用户态切换到内核态,这种转换非常消耗性能。
<img src="F:\记录\八股文\image-20251028220439394.png" alt="image-20251028220439394" style="zoom:67%;" />
### Reentrantlock
`底层实现主要依赖于AQS` AQS通过内部类 Sync来实现具体的锁操作。不同的 Sync 子类实现了公平锁和非公平锁的不同逻辑
### 特性
- **可中断性 **
- 这意味着线程在等待锁的过程中,可以被其他线程中断而提前结束等待
- 使用了与 **LockSupport.park() **和 **LockSupport.unpark() **相关的机制来实现可中断性
- **设置超时时间**
- 等待一定时间后如果还未获得锁,则放弃锁的获取。
- 通过内部的 **tryAcquireNanos **方法来实现的
- **公平锁和非公平锁**
- 默认情况下是非公平锁 也可以设置成 公平锁
- **多个条件变量**
- **可重入性**

View File

File diff suppressed because it is too large Load Diff