java学习笔记

1、JavaWeb三大组件:Listener、Fliter、Servlet

2、springMVC执行流程:

①用户先发送http请求到服务器

②请求到达前端控制器(DispatcherServlet),前端控制器首先会对请求进行统一处理,比如判断请求是否需要拦截、读取请求参数等。

③前端控制器请求处理映射处理器(HandlerMapping)去查找Handler

④处理器映射向前端控制器返回执行链,执行链包含Handler以及各种拦截器(Handler其实就是controller)

⑤前端控制器拿到执行链调用处理器适配器(HandlerAdapte ),处理器适配器会根据请求去执行Handler,Handler执行完毕后返回一个ModelAndView

⑥处理器适配器会将ModelAndView返回给前端控制器,前端控制器会请求视图解析器去解析视图

⑦视图解析器会向前端控制器返回一个view视图

⑧前端控制器会对视图进行渲染,前端控制器向用户响应结果,其实数据填充到模板页面中生成最终的HTML页面然后传递给前端

3、spring依赖注入方式

构造方法注入:在类的构造函数中声明所需的依赖项,Spring IoC容器在实例化该Bean时,会根据构造函数的参数类型和顺序,自动将对应的Bean注入到构造函数中

set方法注入:通过Bean的setter方法来注入依赖。每个需要注入的属性都应有一个对应的setter方法

属性注入:分为两种

@Autowired  :根据类型注入,由spring提供
@Resource:先根据名称注入,再根据类型注入,由j2EE提供

4、springboot优点:


约定大于配置
开箱即用
内置tomcat

5、springIOC

基本概念;

  1. 控制反转:传统程序设计中,我们自己在对象中主动创建依赖对象,而在IoC模式下,创建被依赖对象的责任交给了IoC容器,由容器在对象初始化时不等对象请求就主动将依赖对象注入到对象中。

  2. 依赖注入(Dependency Injection, DI):是实现IoC的一种具体方式,即组件之间的依赖关系由容器在运行期决定,形象地说就是“由容器动态地将某个依赖关系注入到组件之中”。Spring框架主要通过构造器注入、setter方法注入等方式来实现依赖注入。

  3. Spring IoC容器:是Spring用来管理Bean(即应用程序中的组件或Java对象)的容器。容器负责创建Bean实例,管理Bean之间的依赖关系,以及负责Bean的生命周期管理。

6、springAOP

AOP就是面向切面编程,用于将与业务无关,但是却对多个Bean产生影响的公共逻辑抽取并封装成一个可重用的模块,该模块称为切面,好处如下。

1、重用代码
2、简化代码
3、封装切面逻辑
4、促进松耦合

AOP的关键概念包括:

  1. 切面(Aspect):切面是关注点的模块化,包含了对横切关注点的实现,比如日志记录切面。
  2. 通知(Advice):通知定义了切面中的具体动作和执行点,比如在方法执行前、执行后或抛出异常时执行的代码。
  3. 切点(Pointcut):切入点定义了在哪些Joinpoint应用切面中的Advice,即匹配连接点的规则。

7、SPI 支持接口和抽象类,

SPI全名Service Provider interface,翻译过来就是“服务提供接口”,再说简单就是提供某一个服务的接口, 提供给服务开发者或者服务生产商来进行实现。

SPI:JDK的SPI支持接口抽象,springSPI支持注解

8、Spring自动装载原理(重点)*****

主要由以下几个核心环节实现

@SpringBootApplication注解:它是一个复合注解,由@SpringBootConfiguration, @EnableAutoConfiguration, 和 @ComponentScan组成

@SpringBootConfiguration表明他是一个spring配置类

@ComponentScan扫描标记的包以及子包,用于组件扫描

@EnableAutoConfiguration启用了自动配置类

@EnableAutoConfiguration里面有一个AutoConfigurationImportSelector类,这个类引入了SpringFactoriesLoader工具类,通过SPI原理,它会遍历META-INF中的spring.factories文件,从中读取并实例化指定的自动配置类

9、retentionPolicy枚举类型:


                            源代码      类文件         JVM运行时
source                    存在
class(默认)        存在        存在
runtime                   存在        存在               存在

10、JWT(JSON Web Token)包含哪几个部分

①Header头部(明文):里面主要包含了typ(传递的数据类型,通常是JWT)和alg(使用的签名算法),这些信息会被转换成JSON格式,然后经过Base64Url编码形成JWT的第一个部分

②Payload载荷(铭文):主要是传递的数据,载荷同样会被转换成JSON格式,并进行Base64Url编码,生成JWT的第二个部分,建议不要存储敏感信息

③Signature签名(可加密):依据头部里的算法生成

  • 签名的生成需要将前面两部分(Header和Payload的Base64Url编码后的内容)用.连接起来,然后与一个密钥(secret)一起,通过指定的算法(如之前头部声明的alg)进行HMAC计算或RSA/ECDSA签名。
  • 计算出的签名结果也会经过Base64Url编码,作为JWT的第三个部分。

11、解决session共享问题

①将session数据持久化存储写入数据库或者别的持久层,服务器接收请求后向持久层发送请求

②将数据保存在客户端,利用令牌实现session共享,原理是JWT

传统的session和cookie缺点是不能分布式

12、session和cookie的区别和关系

关系:session和cookie都可以用来实现跟踪用户状态,二者的关系是session依赖于cookie

①当浏览器第一次发送请求并获取session后,服务端在服务器内部创建一个session对象,并将该session的id以(JSESSIONID=id值)的cookie写会浏览器

②当浏览器二次请求时,会自动携带cookie(JSESSIONID=id值),服务端根据cookie记录的id值获取相应的session

区别:

1存储位置不同:cookie保存在客户端,session保存在服务器端

2存储数据大小不同:cookie存储的数据不超过4K,session可以存储任意大小

3生命周期不同:cookie可以主动设置生命周期

13、门面模式

门面模式(Facade Pattern)是一种结构型设计模式,它的主要目的是为子系统中的一组接口提供一个统一的高层接口。这样做可以简化客户端与子系统的交互,隐藏系统的复杂性,并且提供更加易用的接口给客户端使用。

日志用的门面模式常用的日志为logback,log4f,log4f2

spring自带的是logback

maven常用操作:

clean(清理)

package(打包)

deploy(部署)

14、http协议组成部分

1、url(请求路径)

2、head(请求头)

3、body(请求体)

15、jwt有哪几部分?

JWT是啥:把信息进行安全的封装,再用json串的形式传递

jwt(json web tokens)

组成部分:header,payload,签名(Signature)

解决session共享问题:

16、jwt如何实现session共享(token)

服务器认证以后,生成一个 JSON 对象,发回给用户,以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

17、springboot如何统一返回格式(ApiWapper-ljj-spring-boot-starter)

1、定义统一的响应对象

首先需要创建一个统一的响应对象,例如ApiResponseResult类,它通常包含以下字段:

  • code:响应码,用于表示请求是否成功。

  • message:响应信息,描述请求的结果或错误详情。

  • data:实际的数据,如果有的话。

  • 在这个类中定义失败和成功的返回格式

    @Data
    public class ApiResponse{
        private Integer code = 200;//设置默认值200,表示“成功”
        private String msg;
        private Object data;
    }
 public static ResultData  success(Object body)
    {
        ResultData resultData = new ResultData();
        resultData.setMsg("成功");
        resultData.setData(body);
        return  resultData;
​
    }
​
    public static ResultData  error(int code,String message)
    {
        ResultData resultData = new ResultData();
        resultData.setCode(code);
        resultData.setMsg(message);
        return  resultData;
​
    }

18、你在开发前端过程用到了什么组件(你对前端的看法)

 Pinia :新一代状态管理工具,是 Vue 的存储库,它允许跨组件/页面共享状态。实际上,pinia就是Vuex的升级版,Pinia的优势就是,更加简洁的语法,完美支持Vue3的Composition api 和 对TypesCcript的完美支持。

vue-router:路由,根据URL分配到对应的处理程序

vite:前端构建工具,能够显著提高开发效率

axios:跨域,调用后端的一些接口;Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 node.js 中的异步通信。它是一个非常流行的选择,用于在前端应用中发送 AJAX 请求到服务器,以获取或发送数据。Axios 的主要优点是它支持多种请求方式,能够拦截请求和响应,转换请求和响应数据,并且可以取消请求

type/node:使用@别名

elementplus:是一个基于 Vue 3 的现代 UI 组件库,专为开发者提供一套完整的设计解决方案

19、vue中route和router的区别:

route指的是,表示当前激活的页面:它获取有关当前页面的信息

router:用在页面之间的跳转,例如:router.push({ name: "login" });跳转到名为login的页面

20、idea的打断点debug功能三个常用的键位:

F7:进入方法内部

F8:往下执行

F9:跳过到下一个断点

21、getmappering(查询)

postmappering:新增

putmappering:修改

deletemappering:删除

22、软删除(逻辑删除)和硬删除(物理删除)

软删除:只是把数据的状态修改了,比如0代表商品下架,1代表在售,我们软删除的时候只需要把1改为0即可,只修改状态。

硬删除:就是把数据全删了,不方便再次查找

23、JDBC执行流程

24、mybatis执行流程

第一步,加载mybatis配置文件和映射文件

第二步,构建会话工厂Sqlsessionfactory

第三步,构建会话对象sqlsession

第四步,sqlsession底层new了一个executor执行器

第五步,excutor执行器找到对应的mappedStatement

第六步,StatementHandler 根据 MappedStatement 中的 SQL 语句及参数映射信息设置 SQL 参数并执行 SQL。

第七步,执行SQL语句之后,statementhandler处理数据库返回的结果集,并按照mappedstatement中定义的结果映射转换为Java对象。
    
第八步,将Java对象返回到客户端

25、mybatis四种拦截器

1执行器拦截器(Executor):负责 SQL 语句的执行,包括缓存策略、批处理等

2语句拦截器(StatementHandler):负责预编译 SQL 语句以及执行 SQL 语句(监控表的更新或者修改)

3参数拦截器(ParameterHandler):负责将传入的参数设置到预编译 SQL 语句中(lastupdateby)

结果集拦截器(ResultHandler):负责从数据库返回的结果集中提取数据

  1. Executor 拦截器

    • 用于拦截执行器(如 SimpleExecutor、ReuseExecutor 或 BatchExecutor)的行为。
    • 可以用来修改 SQL 执行逻辑或添加额外的操作,比如记录 SQL 执行时间。
  2. ParameterHandler 拦截器

    • 用于拦截参数处理器,可以改变传入 SQL 的参数值或类型。
    • 例如,在参数处理器将参数设置到 PreparedStatement 之前对其进行修改。
  3. ResultSetHandler 拦截器

    • 用于拦截结果集处理器,可以改变结果集的处理方式。
    • 例如,在从结果集中提取数据之前或之后进行一些操作。
  4. StatementHandler 拦截器

    • 用于拦截 Statement 的创建和执行。
    • 可以用来修改 SQL 语句或预编译语句的创建过程,例如添加额外的 SQL 输出。

26、反射(Reflection)

动态语言与静态语言

动态语言:在运行时代码可以根据某些条件改变自身结构的语言。如C#,PHP,JavaScript等

静态语言:与动态语言相对应,即运行时不能改变自身结构的语言,如Java,C,C++

Java不是动态语言,但是我们可以通过反射机制获得类似动态语言的特性,所以说Java具有一定动态性。

反射概念

在Java中,反射(Reflection)是一种强大的工具,它允许程序在运行时获取和操作类、接口、构造器、方法和字段等。反射是Java语言的一个重要特性,它为开发人员提供了许多灵活性,尤其是在构建框架和库时非常有用。

获取Class对象的三种方式:注意class对象是不能创建的,只能获取

1.Class c1 =类名.class

public class MyClass {
    public static void main(String[] args) {
        Class<?> myClass = MyClass.class; // 获取 MyClass 的 Class 对象
        System.out.println(myClass.getName()); // 输出: com.example.MyClass
    }
}

2.通过 instance.getClass() 方法,如果你有一个类的实例,你可以调用 getClass() 方法来获取其 Class 对象

public class MyClass {
    public static void main(String[] args) {
        MyClass instance = new MyClass();
        Class<?> myClass = instance.getClass(); // 获取 MyClass 实例的 Class 对象
        System.out.println(myClass.getName()); // 输出: com.example.MyClass
    }
}

3.通过 Class.forName() 方法,通过全类名动态加载类,并获取其 Class 对象

public class MyClass {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> myClass = Class.forName("com.example.MyClass"); // 动态加载 MyClass 的 Class 对象
        System.out.println(myClass.getName()); // 输出: com.example.MyClass
    }
}

注意,一个class类只会有一个class对象,不管用这个类创建多少个对象,这些对象的hashcode值都是一样的,说明他们是同一个对象。

类的初始化顺序一定是先初始化父类再初始化子类

Java反射的主要用途包括:

  1. 获取类信息:获取类名、包名、父类、接口等。

获取类名:c1.getName()

获取类的属性:c1.getFields(),只能获取public的属性,getDeclaredFields(),获取全部属性

获取方法:c1.getMethods(),只能获取public的方法,getDeclaredMethodes()获取全部方法

  1. 创建实例:通过类名或Class对象动态创建实例。
  2. 访问类成员:获取并操作类的字段、方法和构造器。
  3. 调用方法:即使方法是私有的,也可以调用。
  4. 修改字段值:可以读取和设置字段的值,即使字段是私有的,反射可以在程序运行阶段对字段进行修改。

基本概念:

  • Class对象:每个类都有一个Class对象,它是Class类的实例。Class对象包含有关类的所有信息。
  • Field对象:表示类中的字段(变量)。
  • Method对象:表示类中的方法。
  • Constructor对象:表示类中的构造器。

效率:通过new获取对象的操作要比通过反射获取对象速度快得多。

反射获取构造方法

反射获取成员变量

反射获取成员方法

反射的使用

MyBatis拦截器处理参数时使用,在做商城项目时,我们的数据表中都有lastUpdatBy这个字段,我们绝大多数业务都需要对lastUpdateBy进行赋值,所以我们就在Mybatis拦截器中通过反射获取参数,然后通过ThreadLocal中的localuser进行统一对其赋值。ThreadLocal为每一个线程提供一个独立的变量副本,使得每个线程在访问给变量时获取的都是自己独有的线程副本,我们定义了一个LocalUser对象存储在ThreadLocal中,然后就可以获取ThreadLocal中的Localuser对象为lastUpdateBy赋值。

27、什么是ThreadLocal

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。ThreadLocal能够实现跨类跨方法实现变量的传递。

ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:

因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。
ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

总的来说,ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景

二、ThreadLocal与Synchronized的区别
ThreadLocal<T>其实是与线程绑定的一个变量。ThreadLocal和Synchonized都用于解决多线程并发访问。

但是ThreadLocal与synchronized有本质的区别:

1、Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

2、Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本

,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。

而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

一句话理解ThreadLocal,threadlocl是作为当前线程中属性ThreadLocalMap集合中的某一个Entry的key值Entry(threadlocl,value),虽然不同的线程之间threadlocal这个key值是一样,但是不同的线程所拥有的ThreadLocalMap是独一无二的,也就是不同的线程间同一个ThreadLocal(key)对应存储的值(value)不一样,从而到达了线程间变量隔离的目的,但是在同一个线程中这个value变量地址是一样的。

28、vue父子传递参数

默认是父传子,需要defineProps这个参数,使用前需要先导入defineProps

当然也可以子传父,需要写defineEmits,使用前需要先导入defineEmits

子传父编码规范就是defineEmits(["update:modelValue"]);;;modelValue是要更新的属性名

VUE3.4之后我们用defineModel,它是一个简化父子组件双向数据绑定的实用工具,无需声明props和emits,用的时候直接定义,例如

const modelValue1 = defineModel("modelValue ");要改变的时候只需要改变modelValue1的值就行,modelValue1只是一个变量,随便起名字都可以,括号里的modelValue可以省略不写,前提是前边v-model:modelValue里的modelValue没有写,他会默认是modelValue这个名字;使用的时候只需要在子组件的标签上加上defineModel就行。

核心是父组件中:<Tuku v-model="formData.img"  v-model:img=formData.imgb > </Tuku>里的

v-model:img 的img名称要和子组件中 const b =defineModel("img")括号里的名称要保持一直。

29、v-if和v-show的区别

v-if :

是一个条件渲染指令,它会根据表达式的真假来判断是否应该渲染出声明它的元素。当表达式的结果为假时,元素及其子元素不会被编译或渲染到 DOM 中。这意味着,当 v-if 的条件变为 false 时,DOM 元素会被完全移除,当条件再次变为 true 时,DOM 元素会重新创建并插入到页面中。

v-show:

v-show 不同于 v-if,它仅控制元素的 CSS display 属性。无论 v-show 的条件如何,元素始终会被编译和保留在 DOM 中,只是当条件为假时,元素的 display 属性会被设置为 none,从而使元素不可见。

30、nginx是什么

Nginx 是一个开源的、高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP邮件代理服务器。它以其稳定性、丰富的功能集、简单的配置文件和低资源消耗而闻名。Nginx 特别适合处理高并发连接,常被用于负载均衡、网页缓存及反向代理,以提升网站的性能和承载能力。

31、注解(Annotation)和注释(comment)的区别

注解不仅可以给人看,也可以给计算机看,注释只能给人看。注解不是程序本身,但是他却可以对程序做出解释,并且可以被程序使用。

32、中间件是什么

中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。相连接的系统,即使它们具有不同的接口,但通过中间件相互之间仍能交换信息。

执行中间件的一个关键途径是信息传递。通过中间件,应用程序可以工作于多平台或 OS 环境。

中间件是介于操作系统和应用软件之间,为应用软件提供服务功能的软件,有消息中间件,交易中间件,应用服务器等。由于介于两种软件之间,所以,称为中间件。

33、ORM(对象关系映射)是什么

对象-关系映射(Object Relational Mapping,简称ORM,对象关系映射)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。 简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。

34、元数据是什么

就是描述数据的数据,比如我们要填一张个人信息表,个人信息表就是一个数据,而描述个人信息表的比如名字,性别,年龄等数据就成为元数据。

35、API是什么

API 代表:应用程序编程接口,在某些或其他方面,很多大型公司会建立自己的API提供给用户或者内部使用,API就是一种为你客户提供服务的方法。一个API并不是等同于一整个远程服务器,他是服务器用来接受请求和发送响应的一部分

36、开发中有没有用到价格这个数据,你是怎样使用的?

有,我们在写一个物流仓储系统的时候,采购时需要查看价格,我们的做法是把价格单独抽出来做一张表,为什么要这样做呢?因为价格是个敏感字段,它会随着时间有所变动,我们不仅需要拿到当前的价格,也需要知道它的历史价格。方便我们后续对数据进行查询以及对比。

37、VUE中v-model:name与:name的区别(:其实是v-bind)

v-model用于表单元素和组件的双向数据绑定

v-bind用于将vue实例的数据属性绑定到HTML元素的任何属性上,属于单向绑定

38、为什么不推荐使用多表联查

查询性能低

39、为什么不推荐使用外键

因为查询效率很低,并且删除的时候很麻烦。

40、数据库设计三大范式

第一范式:每一列都是原子的,不可再分。这意味着表中的每一列都包含单一类型的值,且每个单元格只包含一个值,不允许有重复组或列表。

第二范式:在满足第一范式的基础上,表中的所有非主键字段(列)必须完全依赖于整个主键,而不是主键的一部分;如果一个表具有复合主键(即由多个列组成的主键),那么所有非主键列都必须依赖于整个复合主键,而不仅仅是其中的一部分

第三范式:在满足第二范式的基础上,表中的任何非主键列不能依赖于其它非主键列,只能直接依赖于主键。这意味着消除传递依赖,即如果存在A → B → C的依赖关系,C应该直接依赖于A,而不是通过B间接依赖

但是,实际开发中我们是可以不遵循第三范式的,也叫反范式化,例如在追求高性能或简化数据访问的场景下

好处是:

1、提高查询性能:避免了多表连接可能带来的性能瓶颈,减少表之间的连接操作,因为数据已经被复制或汇总在单个表中,这可以显著加快查询速度

2、简化查询:反范式化可以使得复杂的多表查询变得简单,因为所需的数据可以在单个表中找到

3、提高数据可用性:在分布式系统或高可用性环境中,反范式化可以减少对远程数据源的依赖,提高数据的本地可用性

4、减少事务处理成本

缺点是:

1、数据一致性问题:由于数据的冗余,当数据发生变化时,需要在多个地方进行更新,否则会导致数据不一致

2、增加存储成本:冗余数据会占用更多存储空间

3、维护难度加大:需要额外的逻辑和程序来确保冗余数据的同步和一致性

41、事务的四大特性

原子性(Atomicity):事务中的所有操作要么全部完成要么全部不完成

一致性(Consistency):完成事务后,数据前后要保持一致

隔离性(Isolation):事务之间互不干扰

持久性(Durability):事务完成后,数据应该永久保存

在 Spring 中,事务管理通常通过 @Transactional 注解来实现,只需要在目标方法上加上这个注解就行

42、spring事务失效的八种场景

1、自调用(Self-Invocation)

即自己调用自己,一个类的方法在调用同一个类的另一种方法的时候,事务会失效。

类内部非直接访问带注解标记的方法 B,而是通过类普通方法 A,然后由 A 调用 B。 自己玩自己

@Service
public class Demo {
  public void A() {

    this.B();
  }
  
  @Transactional
  public void B() {
     ......
  }
}

解决方法是将这些方法拆分到不同的类中,或者通过 Spring 的代理来调用这些方法。

在该Service类中使用AopContext.currentProxy()用AOP上下文获取代理对象,让代理对象去执行目标方法,前提是要在启动类里加上

@EnableAspectJAutoProxy(exposeProxy = true)//导出业务代码

然后在目标方法上加上

@Transactional注解,在执行方法前开启事务,执行成功则提交事务,失败则回滚事务
 @SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
@EnableTransactionManagement
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

}

Java

((ServiceA)AopContext.currentProxy()).doSave(user);

或者

2.非public修饰的方法

@Transactional注解只能在在public修饰的方法下使用。

/**
 * 私有方法上的注解,不生效(因私有方法Spring扫描不到该方法,所以无法生成代理)
 */
@Transactional
private boolean test() {
    //test code
}

3、数据库不支持事务

MySQL中,MyISAM引擎不支持事物,InnoDB 支持事物

4、异常类型不匹配

@Transactional 注解默认只管理运行时异常(如RuntimeException及其子类)和错误(如Error)。当抛出未被捕获的运行时异常(RuntimeException)时,Spring 会触发事务回滚操作,将之前的操作撤销;对于受检异常(即必须被捕获或声明抛出的异常,例如IOExceptionSQLException等),除非你明确配置事务管理器去处理它们,否则事务不会回滚。

可以通过 rollbackFor 和 noRollbackFor 属性来指定需要回滚或不需要回滚的异常类型。

@Transactional(rollbackFor = SQLException.class)

   @Transactional
    public void insertAll(PoMaster master) throws Exception {
        poMasterDao.insert(master);
        if(1 == 1){
            // 故意引发受检异常,事务默认不会回滚
            throw new SQLException("A checked exception occurred.");
        }
        poItemDao.insertList(master.getItems());
    }

5、捕获异常未抛出,自己消化掉了异常

@Transactional
public void A(){
	try{
	   ......
	}catch(Exception e){
	   // 未抛异常
	}
}

6、传播属性设置问题

前两个REQUIRED与SUPPORTS常用些

propagation属性错误

@Transactional默认的事务传播机制是:REQUIRED,若指定成了NOT_SUPPORTED、NEVER事务传播机制,则事物不生效

7、Bean没有纳入Spring IOC容器管理

该类没被Spring管理,事物也是不生效的

8、事务方法内启动新线程进行异步操作

主线程没有拿到子线程的异常,所以代理类以为正常执行了

43、采购的业务周期

44、spring框架中哪里用到了反射

在 Spring 框架中,反射机制被广泛用于以下几个方面:

1. 依赖注入:Spring 使用反射机制获取对象并进行属性注入,从而实现依赖注入。

2. AOP:Spring AOP 使用 JDK 动态代理或者 CGLIB 字节码增强技术来实现 AOP 的切面逻辑,这其中就包含了对被代理对象方法的反射调用。JDK 动态代理适用于实现了接口的目标对象。Spring 使用 java.lang.reflect.Proxy 类来创建代理对象,并且使用 InvocationHandler 接口来定义方法拦截逻辑。

3. MVC 框架:Spring MVC 框架使用反射来调用相应的控制器方法,从而实现请求的处理。

4. 数据库访问框架:Spring 的 JDBC 框架使用反射机制来实现对数据库的访问。

5. 容器管理:Spring 容器也使用了反射机制来管理对象的实例化和依赖注入。

需要注意的是,虽然反射机制为开发者提供了极大的便利性,但是过度使用反射也可能导致性能问题,在使用时需要进行适量控制。

45、spring实现代理的方式有两种

JDK动态代理和CGLIB代理

JDK动态代理是基于Java反射机制实现的,它要求目标对象必须实现一个或多个接口。通过java.lang.reflect.Proxy类和InvocationHandler接口来创建代理对象。Spring AOP使用JDK动态代理来代理实现了接口的bean。

使用条件:

  • 目标对象必须实现至少一个接口。
  • 代理对象实现相同的接口。

CGLIB(Code Generation Library)是一个高性能的代码生成库,它可以在运行时动态地生成一个新的类,并继承原始的目标类。因此,即使目标对象没有实现任何接口,CGLIB也可以为它创建代理。

使用条件:

  • 目标对象不需要实现接口。
  • 代理对象会继承目标对象。

Spring根据目标bean是否实现了接口来决定使用哪种代理方式。如果bean实现了接口,则使用JDK动态代理;如果没有实现接口,则使用CGLIB代理。

46、springsecurity原理

首先,请求会先到我们的listener里再发送到filter,filter不仅只有一个,有多个,filter1到filtern(filter1到filtern是一个过滤链)之间有一个delegatingfilterproxy,他也是一个filter,这个filter里面有一个filterchainproxy,它里面有一个securityfilterchain过滤器链,里面有许多security filter,这个采用的是责任链模式,每个security filter负责各自的权限管理,默认有15种,在这个过滤器链之间我们可以手动添加我们自己的权限管理,比如添加JWTTokenFilter可以用他把roles放到securitycontext上下文里,然后走完到servlet,看我们的方法上面有没有@ReAirchired这个注解,这个注解里看包不包含我们填在securitycontext上下文里的角色,如果包含继续执行方法(这里运用到AOP原理,在执行方法前做一些事情判断包不包含我们写入上下文里的角色),不包含则返回错误403(未授予权限);

47、你们项目权限怎么做的

运用springsecurity实现的,可以先讲一下springsecurity的原理,后端用springsecurity,前端用的手写指令

前端:

在router中的meta属性中提前设置好角色(roles)的属性,角色的属性根据不能的分类划分好

例如:

并将登录用户的角色保存在pinia中,pinia里的代码如下

自定义指令,在src下边建directiv文件夹,在文件夹里写我们自己的指令auth.ts

Array.isArray(role):这是一个类型检查函数,用于确定 role 是否是一个数组。它返回一个布尔值,如果提供的参数是一个数组,则返回 true,否则返回 false

return pinaRoles.some((item: any) => role.includes(item)):这部分代码是一个返回语句,它使用了数组的 some() 方法来检查 pinaRoles 数组中的任何一个元素是否存在于 role 数组中。

  • pinaRoles.some(...)some() 方法测试数组中是否存在至少一个元素满足提供的函数所指定的条件。如果存在这样的元素,some() 方法返回 true;否则,返回 false

  • (item: any) => role.includes(item):这是一个箭头函数,作为 some() 方法的参数。它接收 pinaRoles 数组中的每一个元素作为参数 item,并检查 item 是否存在于 role 数组中。

在菜单中运用指令,当访问的时候获取router中设置的角色,判定设置的角色是否包含登录用户的角色,如果包含就有权限访问,如果不包含就移除,使用remove()方法,就不会显示无权限访问的页面。

后端:

48、你了解的spring设计模式有哪些

责任链模式

适配器模式

代理模式

元模式

49、RBAC相关的表

用户表,

角色表

权限表

用户角色关联表

权限角色关联表

资源表

资源权限关联表

50、mybatis一对多和多对多

一对多:

假设你有两个实体:DepartmentEmployee,其中 Department 可以有多个 Employee,这是一个典型的一对多关系。在 MyBatis 中,你可以使用 <resultMap><collection> 元素来处理这种关系。

实体1:

public class Department {
2    private int deptId;
3    private String deptName;
4    private List<Employee> employees;
5
6    // getters and setters
7}

实体2:

public class Employee {
2    private int empId;
3    private String empName;
4    private int deptId; // 外键
5
6    // getters and setters
7}

Xml:

<resultMap id="DepartmentResultMap" type="com.example.Department">
2    <id property="deptId" column="dept_id"/>
3    <result property="deptName" column="dept_name"/>
4    <collection property="employees" ofType="com.example.Employee">
5        <id property="empId" column="emp_id"/>
6        <result property="empName" column="emp_name"/>
7    </collection>
8</resultMap>
9
10<select id="selectDepartmentWithEmployees" resultMap="DepartmentResultMap">
11    SELECT d.dept_id, d.dept_name,
12           e.emp_id, e.emp_name
13    FROM department d
14    LEFT JOIN employee e ON d.dept_id = e.dept_id
15    WHERE d.dept_id = #{deptId}
16</select>

这段 MyBatis 的 XML 配置代码定义了一个名为 DepartmentResultMap 的结果映射,它用于将数据库查询结果映射到 com.example.Department 类的对象上。让我们逐行解析这个配置:

  1. <resultMap id="DepartmentResultMap" type="com.example.Department">

    • id="DepartmentResultMap": 这是一个唯一标识符,用于区分不同的结果映射。在这个例子中,我们将其命名为 DepartmentResultMap
    • type="com.example.Department": 这指定了与该结果映射关联的 Java 类。在这个例子中,是 com.example.Department 类。
  2. <id property="deptId" column="dept_id"/>

    • property="deptId": 这是指定的 Java 类中对应的属性名称。在这里,deptId 是 Department 类的一个属性。
    • column="dept_id": 这是指定的数据库列名,它将与 Java 属性 deptId 相关联。当 MyBatis 执行 SQL 查询时,它将从结果集中读取名为 dept_id 的列,并将其值映射到 Department 对象的 deptId 属性上。
  3. <result property="deptName" column="dept_name"/>

    • 同样地,property="deptName" 指定了 Department 类中的 deptName 属性,而 column="dept_name" 表示从数据库查询结果中读取 dept_name 列的值,并将其映射到 deptName 属性。

接下来的部分处理 Department 类中的一对多关系:

  1. <collection property="employees" ofType="com.example.Employee">

    • property="employees": 这是 Department 类中集合类型的属性名,用于存放 Employee 对象的列表。
    • ofType="com.example.Employee": 这指定了集合中元素的类型,在这里,元素类型是 com.example.Employee 类。
  2. <id property="empId" column="emp_id"/>

    • 这是针对 Employee 类中的 empId 属性和数据库中的 emp_id 列的映射。
  3. <result property="empName" column="emp_name"/>

    • 这是针对 Employee 类中的 empName 属性和数据库中的 emp_name 列的映射。

通过这种方式,MyBatis 能够将 SQL 查询的结果集映射到 Java 对象中,包括处理复杂的关联关系,如一对多。这使得在应用程序中处理数据库数据更加直观和方便。

多对多:

多对多关系通常需要一个关联表。假设你有两个实体:CourseStudent,它们之间通过一个关联表 course_student 连接。

第一个实体:

public class Course {
2    private int courseId;
3    private String courseName;
4    private List<Student> students;
5
6    // getters and setters
7}

第二个实体:

public class Student {
2    private int studentId;
3    private String studentName;
4
5    // getters and setters
6}

xml文件:

<resultMap id="CourseResultMap" type="com.example.Course">
2    <id property="courseId" column="course_id"/>
3    <result property="courseName" column="course_name"/>
4    <collection property="students" ofType="com.example.Student">
5        <id property="studentId" column="student_id"/>
6        <result property="studentName" column="student_name"/>
7    </collection>
8</resultMap>
9
10<select id="selectCourseWithStudents" resultMap="CourseResultMap">
11    SELECT c.course_id, c.course_name,
12           s.student_id, s.student_name
13    FROM course c
14    LEFT JOIN course_student cs ON c.course_id = cs.course_id
15    LEFT JOIN student s ON cs.student_id = s.student_id
16    WHERE c.course_id = #{courseId}
17</select>

在上述示例中,<collection> 元素用于映射一对多关系,而 LEFT JOIN 语句确保即使没有关联记录,主表的数据也能被返回。此外,MyBatis 允许使用 <association> 元素来处理一对一关系,这在处理反向引用时非常有用。在实际应用中,你可能还需要考虑性能优化,比如使用延迟加载或嵌套查询。

collection用于mybatis一对多或多对多关系映射

51、Vue父子传参以及子组件调用父组件的方法

先写一个子组件,要使用的话先在父组件里导入子组件Son,导入就是import{Son }from‘。。。’,使用的时候直接在要用的位置加上子组件的标签名,例如

先在父组件里定义const parentName=ref("我是父传递的")

<Son v-model:name="parentName"></Son >然后在标签里写入(name是属性变量,parentName是局部变量,不一回事)

父组件里定义一个const parentName=defineModel("name")

v-model后边的name必须要和我们父组件里defineModel里填的名称保持一致,而parentName这个只是一个变量名,可以随意些,这句话是把父组件的parentName的值给子组件的name属性。

在子组件里要定义一个sonName,如

const sonName=defineModel("name"),注意,这里的name必须要和父组件里v-model:后边的name保持一致,在子页面显示就直接这样:名字:{{sonName}},直接调用就行。

父调用子里的方法,把子的方法通过defineExpose把方法暴露出来,在父组件里引入子组件就可以直接使用子的方法,在子组件上加上ref;

而子调用父的方法可以借助vue3中useAttrs,在子组件导入useAttrs

52、DOM元素是什么

DOM 元素,全称为 Document Object Model 元素,是构成网页结构的基础组成部分。DOM,即文档对象模型,是一种用于表示 HTML 或 XML 文档的标准数据结构。在网页开发中,DOM 提供了一种将网页内容和结构表示为树状结构的方式,其中每个节点都是文档的一部分,如元素、属性、文本等。

53、java设计模式

策略模式

根据你的需求,去调到不同的类上边去实现接口,根据请求的类型来判断具体用哪个类给你服务

我们在仓储系统的出库管理中使用到了策略模式,我们的出库方式有很多种,如销售出库,破损出库,调拨出库等等,根据不同的出库类型我们为每种出库类型定义了各自的类,根据请求的不同找到各自对应的类,然后去掉用类的方法实现功能。

观察者模式:

我们在做仓储系统的时候,对于货品的入库和出库有不同的操作,我们自己定义了一个事件,通过使用listener观察者,当一个事件发生变化时,其他依赖于他的事件都会发生改变。观察者模式的主要组成部分是被观察者(事件),观察者(这些对象监视事件的状态,当事件状态发生变化时,每个观察者都会收到通知,通常会调用他们的update方法);主要用的一对多关系的依赖。

观察者模式(Observer Pattern)是一种软件设计模式,属于行为型模式之一。它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。

责任链模式:

适配器模式

单例模式

代理模式

54、mybatis一级缓存与二级缓存

一级缓存(是默认开启的)

概念:它指的是mybatis中的SqlSession对象的缓存。当我们执行完查询之后,查询的结果会存储在SqlSession为我们提供的一块区域中。当我们再次查询同样的数据,mybatis会先去SqlSession中查询是否有,有的话直接拿出来使用。当SqlSession对象消失时,Mybatis的一级缓存也就消失了。

特点:

1、  同一 SqlSession 内部,执行相同查询条件的多次查询,只要在缓存有效期内,只会执行一次数据库查询。

2、  不同的 SqlSession 之间缓存数据互不影响,即一级缓存仅对当前 SqlSession 有效

  • 第一次发起查询用户id为 1 的用户信息,先去找缓存中是否有id为 1 的用户信息,如果没有,从数据库查询用户信息。 得到用户信息,将用户信息存储到一级缓存中。

  • 如果sqlSession去执行 commit操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

  • 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。              

二级缓存(默认关闭)

概念:二级缓存是基于 namespace(即 Mapper XML 文件的命名空间)级别的缓存,可以被多个 SqlSession 共享。这意味着,对于同一 Mapper 下的相同查询请求,即使由不同的 SqlSession 执行,如果二级缓存中已有结果,都可以复用缓存数据,避免了数据库查询

特点:

  • 二级缓存默认是关闭状态,需要在 MyBatis 配置文件(mybatis-config.xml)中开启。

  • 二级缓存可以跨 SqlSession 边界共享数据,适用于多用户间存在大量重复查询且数据相对静态的场景。

不用二级缓存的原因:因为开启二级缓会在内存中开辟一块空间,比如我们有多个应用的话,每个应用都会创建二级缓存会造成内存的大量占用。

总结:

  • 一级缓存:自动管理,无需配置,适用于单个 SQLSession 内的重复查询。
  • 二级缓存:需要显式配置,适用于跨 SQLSession 的重复查询,有助于提高整个应用程序的性能。

55、数据库乐观锁与悲观锁

56、stream流常用操作

在Java中,Stream操作流是Java 8新引入的一个功能,它提供了很多强大的操作,方便我们进行集合的处理和操作。常用的Stream操作方式有:

1.过滤:使用filter()方法可以过滤掉集合中不符合条件的元素。

2.映射:使用map()方法可以对集合中的每一个元素进行映射处理。

3.排序:使用sorted()方法可以对集合中的元素进行排序。

4.去重:使用distinct()方法去掉集合中的重复的元素。

5.统计:使用count()方法可以对集合中的元素进行统计。

6.聚合:使用reduce()方法可以对集合中的元素进行聚合计算。

7.遍历:使用forEach()方法可以遍历集合中的每一个元素。

8.匹配:使用anyMatch()、allMatch()、noneMatch()方法可以对集合中的元素进行匹配判断。

9.分组:使用qroupingBy()方法可以按照某一个属性进行分组。

10.转换:使用collect()方法可以将集合中的元素转换为另一个集合。

11.平均:使用average()方法可以用于计算一组元素的平均值。

12.min:取最小值

13.max:取最大值

14.skip:跳过

57、采购单的状态

新建(采购员)-审核通过(高级管理)-运输中-入库(采购员)-审核不通过(高级管理员可以)-作废(采购人跟高级管理者都可以)

58、数据库懒加载

<mapper namespace="com.by.dao.UserDao">
    <resultMap type="User" id="findAllResultMap">
        <id column="id" property="id"></id>
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
         <!--
          property:属性名
          ofType:泛型
          select: 要调用的 select 映射的 id,select属性指定了要调用的另一个SQL映射ID,用于加载关联对象。
          column : 传递给 select 映射的参数
          fetchType="lazy":懒加载,默认情况下是没有开启延迟加载的,局部配置
         -->
        <collection property="accounts" ofType="Account" fetchType="lazy"
                    select="com.by.dao.AccountDao.findAccountById" column="id">	
        </collection>
    </resultMap>

    <select id="findAll" resultMap="findAllResultMap">
      select * from user
    </select>
</mapper>

懒加载应用场景:

  1. 对象关系映射(ORM):在ORM框架中,比如Hibernate、MyBatis等,当我们从数据库中获取一个对象时,并不一定需要立即加载与之相关的所有关联对象的数据。只有当需要访问这些关联对象时,才会触发数据库查询以加载相关数据。

  2. 分页查询:在处理大量数据时,如果一次性加载所有数据到内存中,可能会导致内存溢出或加载时间过长。因此,可以通过分页的方式只加载当前页面的数据,而其他数据则在用户滚动或请求时按需加载。

  3. 数据缓存:在缓存系统中,也可以使用懒加载的概念,即仅在首次请求时从数据库加载数据到缓存中,之后直接从缓存中读取。

懒加载与急加载区别:

  • 急加载(Eager Loading):指的是在加载主对象的同时也加载其所有相关联的对象。这种方式确保了数据的一致性,但可能导致大量的数据被加载到内存中,从而影响性能。
  • 懒加载(Lazy Loading):只在需要时才加载相关联的对象。这种方式可以显著减少初始加载的时间和内存消耗,但在实际使用中可能会导致额外的数据库查询。

假设有一个实体Employee和一个实体Department,并且一个员工属于一个部门。我们可以使用@ManyToOne注解来定义这种关系,并通过fetch属性设置懒加载策略。

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Department department;

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

@Entity
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

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

在这个例子中,Department对象将会在访问employee.getDepartment()时才被加载。如果没有调用这个方法,则不会触发数据库查询。

59、echarts里的series什么意思

series 是图表配置项中的一个重要部分,它代表了图表中的一系列数据点和相应的可视化设置。series 选项用于定义图表上显示的数据系列,每一个 series 项都代表一组数据及其相关的样式和行为配置。

series 项通常包含以下几个关键属性:

  • type: 数据系列的类型,如 'line'(折线图)、'bar'(柱状图)、'pie'(饼图)等。
  • name: 数据系列的名称,用于图例和其他地方的显示。
  • data: 该数据系列的数据数组,包含具体的数值或数据点。
  • 其他配置: 包括颜色、标签样式、动画效果等,这些配置取决于图表类型。

60、使用echarts的方式

1、先在npm里下载:

npm install echarts

 然后在使用的页面引入:

import * as echarts from 'echarts';

然后在官网上找模板引用即可

注意

legend: {
            data: ['库存量', '销量']//legend属性里data里的名称库存量,销量等必须要和series
//里的name属性的名称一一对应,如
series: [//series是数据
            
        
            {
                name: '库存量',//数据源的名称
                type: 'bar',//示例图的形式,bar是柱形图,line是线形图
                data: data.map((item: any) => item.qty),//要展示的数据源
                markPoint: {
                    data: [
                        { type: 'max', name: '最大值' },//最大值
                        { type: 'min', name: '最小值' }//求最小值
                    ]
                },
                markLine: {
                    data: [{ type: 'average', name: '平均值' }]//求平均值
                }

            },

各种属性:

toolbox是工具盒子

里面第一个可以查看文本样式版的数据,第二个查看折线,第三个查看柱状,第四个更新,第五个下载

xAxis:指X轴

yAxis:指 Y轴

61、使用懒加载的时候,在我们要序列化的实体类上边加上注解

@JsonIgnoreProperties(value = {"handler"})
它的作用是序列化的时候忽略自动生成的handler属性,否则程序会报错

61、强弱软虚引用

1强引用:普通的引用

2软引用:像星巴克避雨一样,如果内存空间充足,允许软引用存在,若内存空间不足,GC删除软引用

3弱引用:势利眼的沙县小吃,不买东西不让你进去休息。不管内存够不够用,GC在清除的时候只要发现弱引用就直接删除

4虚引用

62、半双工与全双工

半双工:在半双工通信模式下,数据可以在两个方向上传输,但不能同时在两个方向上传输。这意味着通信的一方发送数据时,另一方只能接收。类似于对讲机

优点:相对于单工(Simplex)模式,半双工提供了双向通信的能力

缺点:由于数据传输不能同时进行,因此可能会出现通信延迟或效率较低的问题

全双工:在全双工通信模式下,数据可以在两个方向上同时传输。这意味着发送和接收可以同时进行,无需等待对方停止发送。

优点:提供了更高的通信效率和带宽利用率,因为数据可以在两个方向上同时流动

缺点;:成本相对较高

63、nginx是什么

是一个开源的高性能的Web服务器软件,它具有以下几个主要作用:

1. Web服务器:Nginx可以作为一个轻量级的Web服务器,处理和响应HTTP请求。它支持静态文件的快速传输,并且能够处理大量的并发连接。

2. 反向代理服务器:Nginx可以作为反向代理服务器,将客户端请求转发到后端的多个服务器上。这样可以实现负载均衡,提高系统的可靠性和性能。

3. 负载均衡器:Nginx可以将客户端请求分发到多个后端服务器上,实现负载均衡。它可以根据不同的策略(如轮询、IP哈希、最少连接数等)将请求分发到不同的服务器上,以达到优化资源利用和提高系统性能的目的。

4. 静态文件服务器:Nginx可以高效地提供静态文件的访问和传输。通过将静态文件缓存到内存中,可以大大减轻后端服务器的负载。

5. SSL/TLS终端代理:Nginx可以作为SSL/TLS终端代理,对外提供HTTPS服务。它可以负责SSL/TLS握手、证书验证等操作,将加密的请求转发到后端服务器。

总的来说,Nginx是一个功能强大、灵活且高效的服务器软件,可以用于构建高性能的Web应用、负载均衡集群、反向代理等场景。

64、CICD的创建流程

选择数据源——代码扫描、单元测试(并行关系)——jar打包(构建)、docker镜像构建(串行关系)——应用部署——结果通知

65、Redis面试题——缓存穿透,缓存击穿,缓存雪崩

1.穿透:两边都不存在,不管是Redis缓存中还是数据库中都不存在数据

解决方法有返回空值,布隆过滤器,使用黑名单

返回空值:就是我们的请求先去Redis缓存中查找数据,如果不存在就去数据库中查找,如果数据库中不存在就返回一个空值,下次我们再次查找这个数据的时候,就直接从Redis缓存中拿这个空值,这样就降低了数据库的访问压力,不会频繁访问数据库。

布隆过滤器:建一个位数组(Bit Array),初始时所有位都被设为 0,当我们插入元素的时候,这些插入元素的位置索引变为1。

记住一句话如果存在则不一定存在,如果不存在则一定不存在。意思是说我们对于要查询的元素,使用所有选定的哈希函数计算出该元素对应的位索引。如果这些位索引处的位都是 1,则认为该元素可能存在于集合中;如果其中有任意一位是 0,则确定该元素不在集合中。

使用黑名单:如果我们查询一个Redis缓存没有的数据的时候,接着去数据库查询,若数据库没有这个元素,把它的索引放到一个黑名单里,下次我们再次查询的时候,先查询黑名单里的索引,如果这个索引在黑名单里,那么说明我们查询的是不合法数据,就不再去访问缓存区或数据库了。

2.击穿

它的意思是当我们Redis缓存中某个或某几个热点数据项失效后,短时间内大量并发请求发送到后端数据库,造成数据库访问压力增大,可能导致服务不可用。

解决办法:缓存预热、互斥锁、双写策略

缓存预热:在服务启动或者定时任务中,预先加载数据到缓存中,这样避免了启动时大量数据打到后端数据库

3.雪崩

就是Redis缓存中大量的热点数据失效,同一时间大量的请求打到后端数据库,造成数据库压力激增,很可能引发服务不可用

解决办法:数据过期时间随机化

数据过期时间随机化:对热点数据设置一个随机的过期时间,这样可以有效避免同一时间大量热点数据失效

66、Redis数据类型应用场景

String:

分布式锁:小米卖车,如果同一个用户在不同设备上登录自己的账号,然后在同一时间抢商品,我们必须保证只能有一个账号能抢到。

session会话:比如京东的个人账户,我们的session中不可能把个人的所有信息都存进去,例如购物车,商品这些是不会存进去的,这些数据我们会存储在一个Redis缓存中,这个缓存就用到了string类型,我们可以根据session中的用户id进行查找这些数据。

业务缓存:京东的页面,或者京东的网页布局都可以放进去

计数器:库存扣减,下单减商品

限流:我们在防止黑客攻击的时候,给他的ip后边追加一些时间戳,时间戳后边加一个计数器用来记录一秒内访问次数,假如在一秒内他的访问次数超过了20次,那么我们就给他的ip限流

全局唯一ID:

Hash:电商购物车,大K是用户id,小k是商品id,值是购买量或者价格

BitMap:用户签到

Zset:排行榜或者热搜

List:消息队列

Set:

bitmap和int都属于string

67、Resultful Api设计风格

Restfule风格是一种软件架构风格,而不是标准,只是提供了一种设计原则和约束条件。主要适用于客户端和服务器端交互的软件。是基于http协议实现。目的是为了提高系统的可伸缩性,降低应用之间的耦合度,方便框架分布式处理程序。基于这个风格的软件可更加的简单、更有层次,更易于实现缓存的机制。
     在resultful风格中,用户请求的url使用同一个URL而用请求方式:get/post/delete/put等方式对请求的处理方法进行区分。这样可以在前后台分离的开发中让前端开发人员不会对请求的资源地址产生混淆,形成一个统一的接口。

三、特点

(1)每种url代表了一种资源。
(2)客户端和服务器之间,传递这个资源的某种表现层。
(3)客户端通过四个http动词,对服务器资源进行操作。实现表现层状态的转化。

在http协议中,四个表示操作方式的动词:GET/Post/Put/Delete,他们分别对应四种基本操作。
Get用来获取资源。Post用来新建立资源,也可以更新资源。Put用来更新资源。Delete用来删除资源。

68、Redis过期删除策略以及内存淘汰策略

过期删除策略:

1.惰性删除:键如果过期了我们不管,下次我们访问的时候先查询这个键是否过期,如果过期了我们删除这个键,如果不过期我们返回这个键。

2.定期删除:每隔一段时间对数据库的键进行一次扫描,如果找到过期的就删除,每次只查询一部分,不会一下子全部删掉。

内存淘汰策略

如果我们的内存到达一定的临界了,我们会进行内存淘汰策略

LRU:全称Least recently used,淘汰最近最少使用的数据(有时间概念,最近)

LFU:全称Least-frequently used,淘汰使用频率最低的数据(没有时间概念)

Redis提供了八种内存淘汰策略

1.默认策略,noeviction,就是如果内存满了,那么我们不删除数据,不让写入数据,想要写入数据就报错。

2.volatile-lru:只对设置了过期时间的key有效,淘汰最近最少使用的数据

3.volatile-lfu:只对设置了过期时间的key有效,淘汰使用频率最少的数据

4.volatile-random:随机淘汰一个设置了过期时间的数据

5.volatile-ttl:挑选过期速度越快(马上过期)的数据进行删除

6.allkeys-lru:对全部key都有效,整个键空间(即数据库中的所有键),根据lru算法删除最近最少使用的数据

7.allkeys-lfu:对全部key都有效,整个键空间(即数据库中的所有键),根据lfu算法删除使用频率最少的数据

8、allkeys-random:从数据库中所有的键中随机选择一个数据进行删除

69、Redis主从同步机制

首先要知道两个命令:

sync 命令会通知操作系统将内存缓冲区中的数据写回到磁盘,保证了数据的一致性和持久性

bgsave 是 Redis 中的一个命令,用于异步保存数据库的快照(snapshot)到磁盘。这个命令触发了一个后台进程来执行持久化操作,从而不会阻塞客户端的其他请求。

全量复制:

1.从服务器向主服务器发送同步命令sync

2.主服务器接收到请求后使用bgsave命令生成一个rdb文件,然后使用一个缓冲区来记录从现在开始所有的写命令

3.主服务器执行完bgsave命令以后会把生成的rdb文件发送给从服务器

4.从服务器拿到这个rdb文件然后加载到内存中,然后主服务器会把刚刚写进缓存的命令同步给从服务器,从服务器再去执行这些命令

增量复制:之后主数据库每执行一个写命令,都会将写命令发送给从数据库。

70、Redis和MySQL如何保持数据一致性

在实际项目中经常会使用到Redis缓存用来缓解数据库压力,但是当更新数据库时,如何保证缓存及数据库一致性,一般我们采用延时双删策略。

目前系统中常用的做法是一个查询接口,先查询Redis,如果不存在则查询数据库,并将结果放入到Redis中。

为什么是删除缓存,而不是更新缓存呢?主要是如果缓存的内容是带有树型结构或者List,Map,那么更新其中一个内容相对较慢。

常见更新策略

1 先删缓存,再更新数据库

2 先更新数据库,再删除缓存

3 普通双删

4 延迟双删

主要介绍一下延迟双删

它的原理是线程A先删除缓存,在线程A更新数据库之前,线程B去查询数据库,数据库更新后,线程C查询数据库,此时线程B拿的是旧数据,线程C拿到的是新数据,线程B和线程C都会把数据放入缓存中,在更新完数据库后线程B明显缓存中的数据与数据库中的数据不一致,而线程C如果热点数据失效也会出现与线程B相同的问题,所以我们在更新完数据库后延迟三到五秒再删除一次缓存,此时线程B和线程C发送请求的时候发现缓存中的数据被删除变为空了,请求就会打到数据库,此时BC去数据库拿到的都是新数据,避免了缓存数据与数据库数据不一致。

极端情况是线程D,它的意思是在删除缓存后数据库更新前查询数据,此时拿到的数据还是旧数据,但是他在我们第二次删除缓存之后才把从数据库拿到的旧数据放入缓存中,此时请求发送到缓存中拿到的还是旧数据。

71、Redis集群模式主要有

主从复制模式:主当机了其他的都不行了

哨兵模式:主宕机了从也宕机了那数据还是会丢失,管理起来也麻烦,不支持分区

Redis Cluster模式:天生分布式,一个主宕机了其他的都可以担当这个主。

72、后端与前端部门如何进行沟通

使用swagger工具生成文档实时更新

73、项目中静态资源如何存放

阿里云OSS私仓或腾讯云COS私仓,前端资源用nginx,

74、Lua脚本的原子性,Java代码中为什么要使用Lua脚本

使用Lua的原因:

主要就是Lua脚本具有原子性,比如说我们在下单的时候,我们要先判断库存量是否足够,这至少需要两个命令,假如我们看到库存量是刚好够我们用的,如果此时另一个线程先于我们把数据库里的库存量减少了,此时数据库里实际的库存量其实不够我们使用了,那这个时候就可能造成我们数据库库存变为负数,这是不允许的,所以我们使用Lua脚本保证一个线程在工作的时候不被别的线程打断。

Lua 脚本的原子性指的是脚本在整个执行过程中不会被打断,也不会有其他客户端的命令插入其中执行

Lua脚本的原子性表现在:

排他性执行:当一个 Lua 脚本开始执行后,Redis 会阻止其他客户端的命令执行,直到该脚本完全执行完毕,脚本中的所有命令要么全部执行,要么一个也不执行

单线程模型:Redis 本身是一个单线程的服务器,当 Redis 开始执行 Lua 脚本时,其他客户端的所有请求都会被暂时搁置,直到脚本执行完成

事务性:在 Redis 中,事务是指一系列连续执行的命令序列,在这个序列中,命令的执行顺序不会被打断

错误处理:如果 Lua 脚本在执行过程中出现错误,Redis 会停止执行脚本并返回错误给客户端

一致性与隔离性:这种原子性只保证脚本作为一个整体被执行,而不保证数据的一致性或隔离性

75、序列化与反序列化

定义

序列化:把对象转化为字节序列的过程叫做对象的序列化

反序列化:把字节序列转化为对象的过程叫做对象的反序列化

什么时候用到序列化/反序列化

RPC(Remote Procedure Call)即远程过程调用,允许一台计算机调用另一台计算机上的程序得到结果,而代码中不需要做额外的编程,就像在本地调用一样。   

实现序列化和反序列化要实现Serializable接口

在 Java 中实现了 Serializable 接口后, JVM 会在底层帮我们实现序列化和反序列化。

实现 Serializable 接口就算了, 为什么还要显示指定 serialVersionUID 的值?

如果不显示指定 serialVersionUID, JVM 在序列化时会根据属性自动生成一个 serialVersionUID, 然后与属性一起序列化, 再进行持久化或网络传输. 在反序列化时, JVM 会再根据属性自动生成一个新版 serialVersionUID, 然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较, 如果相同则反序列化成功, 否则报错.

如果显示指定了 serialVersionUID, JVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID, 但值为我们显示指定的值, 这样在反序列化时新旧版本的 serialVersionUID 就一致了.

在实际开发中, 不显示指定 serialVersionUID 的情况会导致什么问题? 如果我们的类写完后不再修改, 那当然不会有问题, 但这在实际开发中是不可能的, 我们的类会不断迭代, 一旦类被修改了, 那旧对象反序列化就会报错. 所以在实际开发中, 我们都会显示指定一个 serialVersionUID, 值是多少无所谓, 只要不变就行.

static 属性为什么不会被序列化

因为序列化是针对对象而言的, 而 static 属性优先于对象存在, 随着类的加载而加载, 所以不会被序列化.

mybatis里resultType和resultMap区别

resultTyperesultMap 是用于配置查询结果如何映射到 Java 对象的两个属性

resultType 主要用于简单的查询结果映射。当你使用 resultType 时,MyBatis 期望查询结果中的列名能够与 Java Bean 的属性名相匹配,并且类型兼容。这意味着对于简单的实体类(POJOs),你可以直接使用 resultType 指定一个类的全限定名或者别名,MyBatis 会自动进行列到属性的映射

假设一个User的Javabean对象

public class User {
    private int id;
    private String name;
    private int age;

    // getters and setters
}

xml配置文件这样写

<select id="selectAllUsers" resultType="com.example.User">
    SELECT id, name, age FROM users
</select>

resultMap 则用于更复杂的映射场景。它允许你定义映射规则,包括一对一关系、一对多关系等。resultMap 可以指定列名与属性之间的映射,并支持嵌套结果集、延迟加载等功能

再加一个Address的Javabean对象

public class Address {
    private int id;
    private String street;
    private String city;

    // getters and setters
}

xml文件这与写,我们定义了一个名为 userResultMapresultMap,它包含了对 UserAddress 实体类的映射。association 标签用来处理 UserAddress 之间的一对一关系。

<resultMap id="userResultMap" type="com.example.User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>

    <association property="address" javaType="com.example.Address">
        <id property="id" column="address_id"/>
        <result property="street" column="address_street"/>
        <result property="city" column="address_city"/>
    </association>
</resultMap>

<select id="selectUsersWithAddresses" resultMap="userResultMap">
    SELECT u.id AS user_id, u.name AS user_name, a.id AS address_id, a.street AS address_street, a.city AS address_city
    FROM users u
    JOIN addresses a ON u.address_id = a.id
</select>

76、bean的生命周期

77、布隆过滤器作用

布隆过滤器使用位数组和哈希函数来快速判断元素是否存在于集合中,但会有一定的误报率。它可以用于解决缓存穿透问题,通过预加载数据库中的所有数据到布隆过滤器,来判断数据是否存在(如果存在可能存在,如果不存在一定不存在)。由于布隆过滤器无法删除元素且需对表中的所有数据进行预热,因此通常不首选此方法,而是用黑名单来解决缓存穿透问题。

78、Ribbon中常见的负载均衡算法

①轮询算法(默认):最简单的负载均衡策略,按照顺序循环地将请求分发到每个服务实例。每个服务实例依次接收请求,实现基本的公平分配。(你一次我一次)

②随机:随机选择一个服务实例来处理请求。

③权重:根据每个服务的权重,权重越高被选择概率越高。

④响应时长:根据每个服务实例的历史响应时间,响应时间越快的服务实例被选中的概率越高。

⑤ip哈希

⑥url哈希

79、Ribbon第一次调用为什么很慢

ribbion采用了懒加载的策略,只有第一次访问的时候才会去创建LoadBalanceClient,并且需要通过TCP三次握手建立服务端与客户端的连接,所以第一次比较耗时

解决方法:就是使用饥饿加载

在properties文件里配置

rabbon.eager-load.enabled=true//开启饥饿加载
rabbon.eager-load.clients=user-service//开启饥饿加载的服务,多个服务用逗号隔开

80、Ribbon内核原理

Nacos Servcer:服务端组件

Nacos Client :客户端组件

有两个mall-order服务,我们的mall-user可能会去调用我们mall-order服务,此时我们mall-order已经注册到Nacos Server服务列表了,客户端会定期从我们服务端拉取服务列表,将服务放到缓存里边,通过ribbon里的算法,启动的时候会找到我们其中的一个服务,用一级目录(服务名)替换成对应的ip和端口,然后再去具体调用我们那个服务。

81、为什么要使用双token拦截器

超级管理员下线,用户角色修改的时候,避免单token无感知,及时刷新token;双token系统通常用于提高安全性和分离不同级别的权限。里面主要有两个token

业务token(Access Token):这是用户用来直接访问资源的token,他的特点是有效期比较短,一旦过期,用户需要重新认证来获取token,这样做的好处是即使我们业务token泄露了,攻击者进行不当操作的时间有限。

刷新token(Refresh Token ):这是当业务token过期的时候重新获取业务token的,他的特点是有效期一般比较长,刷新token一般不会发送给客户端,而是保存在服务器端,当需要刷新业务token的时候,服务端通过提供刷新token去请求新的业务token。可以让用户在无感知的情况下获取新的业务token。当我们获取新的业务token的时候,其实刷新token也延长了寿命,相当于刷新token变为永久的了。

另外使用双token的原因还有:

安全性高:通过短期的业务token和长期的刷新token,可以在不影响用户体验的情况下减少安全风险,即使业务token丢失,也可以凭借其有效期短来把损失控制在一定范围内

用户体验好:双token系统可以在不影响用户体验的前提下,实现后台的安全管理和策略调整。用户不需要频繁登录,同时也能保证系统的安全性。

权限管理:双token系统可以更好地管理用户的权限,在开放平台中,第三方可以通过刷新token来获取一个临时的业务token,这样即使不获取用户的用户名和密码也能赋予第三方一定权限,也保证了用户的信息安全

灵活性好:在需要进行更细粒度的权限控制或者策略调整时,双token系统提供了更多的灵活性。例如,可以在不改变refresh token的情况下,调整access token的有效期或权限范围。

82、什么是CAP原则

C:一致性:一致性要求所有节点在同一时刻看到相同的数据。具体而言,如果一个节点成功提交了一个更新,那么随后对该数据的所有访问都应该反映出这个更新。

A:可用性:可用性要求每个请求无论成功还是失败都应该在合理的时间内得到响应。

P:分区容错性:分区容错性是指即使网络分区发生,系统仍然能够正常运行。

一般开发中我们会保证AP或者CP,一致性只需要保证最终一致即可

83、Nacos配置中的动态刷新原理

84、你知道的负载方案有哪些

DNS 负载均衡:利用 DNS 的轮询机制将域名解析到不同的 IP 地址上,从而将请求分发到不同的服务器

硬件负载均衡器:F5

软件负载均衡器分为客户端和服务端

客户端负载均衡器:Ribbon

服务端负载均衡器:Nginx,OpenResty

85、说一下ES及应用场景

ES是非关系型数据库

一、全文搜索

1.电商搜索:快速查找商品信息,支持模糊匹配、关键词高亮显示、过滤、排序等功能。

2.站内搜索:网站内部的页面、文章、博客等内容的搜索,提供类似Google的搜索体验。

3.论坛和社交媒体:用户发表的内容搜索,如帖子、评论、话题等

二、日志分析与监控

ELK日志采集。加上traceId

1.运维监控:收集系统指标、网络流量数据,实时或历史数据分析,可视化展示系统状态和趋势

2.应用日志:跟踪应用程序的行为,帮助开发人员迅速定位错误、诊断问题

3.服务器日志:收集、索引和分析服务器产生的各类日志,用于故障排查、性能优化、安全审计等。

三、数据分析

1.业务分析:实时或批量分析业务数据,生成报表,进行趋势分析、关联分析等

2.时序数据分析:存储和分析时间序列数据,例如设备传感器数据、用户行为数据等

四、搜索推荐

实现个性化搜索和推荐功能,根据用户的搜索历史和行为模式,智能推荐相关内容

86、使用ES为什么要安装分词器

在 Elasticsearch 的 IK Analyzer 中,ik_smartik_max_word 是IK 分词器针对中文分词提供的两种策略,但分词效果和粒度不同:

ik_smart: 这种模式更侧重于保持语义完整性,尽量进行较少的、更有意义的拆分,减少无意义的子词组合,提高搜索准确率,降低误报率。

ik_max_word: 此模式致力于最大化地拆分文本,即尽可能多地生成可能的词语组合,包括单字、双字直至整个短语。它的特点是尽力穷举所有可能的词汇,提高召回率,但在某些情况下可能会造成噪声较多。

参考网址:https://blog.51cto.com/u_15116285/6100979

官方插件下载地址:

https://github.com/medcl/elasticsearch-analysis-ik/releases

87、MDC是什么

MDC是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对,MDC中记录的内容可以被同一线程执行的代码访问,该线程的子线程会继承父线程MDC的内容。

需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据

原理:MDC的工作原理基于当前线程绑定的Map,通过Map存储键值对,每个线程都有自己独立的MDC实例,当你在一个线程中为该线程的MDC添加键值对时,这些键值对只对当前线程有效,保证多线程中每个日志记录互不干扰。当请求来的时候会把请求头里的traceId放在InheritableThreadLocal里,然后打印时去取就行了。

InheritableThreadLocal就是ThreadLocal的增强版,他继承了ThreadLocal并扩展了功能,他支持子线程继承父线程中的值

88、说一下mongondb及应用场景

mongondb非关系型数据库,基于文档存储,mysql使用的时候先建表,而mongondb不需要,只需要存储对象,这样提高了我们的开发效率,我们做过一个

微信小程序拼车,引用高德地图的API接口,将小车的经过的城市存储到mongondb中,这样可以查看定位以及我们的行进路线

商城首页的广告,楼层(首页是灵活多变的方便修改)

89、Http工作原理

当请求来的时候,会先通过TCP三次握手建立连接,建立连接以后客户端会向服务端发送请求,接下来服务端处理请求,然后服务端发出响应,接着客户端处理响应并将数据渲染到页面,最后TCP四次挥手断开连接。

90、spring cloud里目前遇到的有两个bug

1.当我们使用GetMapping去获取对象的时候,正常的springMVC里会把对象转化成URL并在后边用问号拼接参数的值,但是springcloud并不会将GetMapping以及DeleteMapping都不会把他转化成URL,必须在请求的类型前加上@SpringQueryMap这个注解

例如:

 @GetMapping ("/api/order")
    List<Product> select(@SpringQueryMap OrderQuery query);

2.feigen里使用@RequetMapping时,springMVC扫描到这个注解的时候会把这个类当成自己的controller,是一个bug没有和飞跟兼容好

@FeignClient(name = "order-service")
//@RequestMapping("/api/order")//spring mvc看到的@RequestMapping,会把他当成自己应用的controller,
// 但是实际不是的,是个bug,与feign没有兼容好导致的
public interface OrderServiceClient {
    @PostMapping("/api/order")
    OrderingOk createorder(@RequestBody OrderingDto dto);

    @GetMapping ("/api/order")
    List<Product> select(@SpringQueryMap OrderQuery query);

}

91、面试模版

1.自己专业简单介绍,我有几年的Java开发经验,

2.兴趣爱好,生活态度

3.参与过的项目,使用的技术栈,中间件等

4.优势或者对之前公司的贡献

可能问:你的优点:性格,技术,你的缺点

91、@SuppressWarnings 注解

在Java编程中,@SuppressWarnings 是一个注解,用来指示编译器忽略某些警告。当你在一个类、方法或其他程序元素上使用 @SuppressWarnings 注解时,你可以指定一个或多个要抑制的警告类型。这对于那些你知道不会有实际问题,但编译器还是会发出警告的情况特别有用。

@SuppressWarnings({"unchecked", "rawtypes"})

具体来说:

  • unchecked:这个参数告诉编译器忽略所有未经检查的警告。未经检查的警告通常涉及到泛型的使用。例如,当你将一个非泛型类型的对象赋值给一个泛型类型的变量时,编译器会发出警告。这种情况经常发生在处理包含不确定类型的集合时,比如使用 List 而不是 List<T>

  • rawtypes:这个参数用来忽略所有与原始类型的使用相关的警告。原始类型是指没有指定泛型参数的类型,例如使用 List 而不是 List<String>。当编译器看到原始类型的使用时,它会发出警告,因为这可能导致运行时类型安全问题。

@Operation

是 OpenAPI 规范(以前称为 Swagger 规范)的一个注解,通常用于描述 API 接口的功能

92、GitFlow

GitFlow是一种为Git版本控制系统设计的分支管理工作流

分支分类:

master主分支;这个分支代表了随时可以部署到生产环境的代码状态,也就是项目的最新稳定版本。

develop开发分支:作为日常开发的基础分支,用于整合所有即将发布的新功能和改进。它是开发团队合作的主要分支。

feature功能分支:专注于单一功能,每个 feature 分支都对应着要实现的一个具体功能或者改进点。

release发布(测试)分支:在Git版本控制系统中是用来准备新版本发布的专用分支。它介于开发分支(如 develop)和主分支(如 main 或 master)

hotfix维护分支:维护分支(一般叫hotfix)是唯一可以直接从master分支fork出来的特殊分支,用于快速修复生产环境中发现的紧急问题的分支。修复完成后,应马上合并回master和develop分支,同时master分支用新版本号打上Tag。

93、原生的JDBC、MyBatis 与 MyBatis Plus 的区别以及各自的优缺点

MyBatis是一个半自动化的ORM框架,相较于原生的JDBC,mybatis将SQL语句与业务代码解耦,降低了与业务代码的耦合性,并且mybatis能够自动的将

MyBatis 和 MyBatis Plus 是两个用于 Java 应用程序的持久层框架,它们都基于 SQL 映射的概念来实现数据访问

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值