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
基本概念;
-
控制反转:传统程序设计中,我们自己在对象中主动创建依赖对象,而在IoC模式下,创建被依赖对象的责任交给了IoC容器,由容器在对象初始化时不等对象请求就主动将依赖对象注入到对象中。
-
依赖注入(Dependency Injection, DI):是实现IoC的一种具体方式,即组件之间的依赖关系由容器在运行期决定,形象地说就是“由容器动态地将某个依赖关系注入到组件之中”。Spring框架主要通过构造器注入、setter方法注入等方式来实现依赖注入。
-
Spring IoC容器:是Spring用来管理Bean(即应用程序中的组件或Java对象)的容器。容器负责创建Bean实例,管理Bean之间的依赖关系,以及负责Bean的生命周期管理。
6、springAOP
AOP就是面向切面编程,用于将与业务无关,但是却对多个Bean产生影响的公共逻辑抽取并封装成一个可重用的模块,该模块称为切面,好处如下。
1、重用代码
2、简化代码
3、封装切面逻辑
4、促进松耦合
AOP的关键概念包括:
- 切面(Aspect):切面是关注点的模块化,包含了对横切关注点的实现,比如日志记录切面。
- 通知(Advice):通知定义了切面中的具体动作和执行点,比如在方法执行前、执行后或抛出异常时执行的代码。
- 切点(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类,这个类里有一个selectimports方法
,通过SPI原理,它会遍历META-INF中的spring.factories
文件,文件里是key与value结构的,value是我们的全类名,从中读取并实例化指定的自动配置类
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可以主动设置生命周期
4cookie不安全
我们项目中没用过session,用的是JWT,因为用session要处理session共享问题
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)
服务器认证以后,生成一个 JWT,发回给用户,以后,用户与服务端通信的时候,都要发回这个 JWT(里面包含用户的信息,如用户id,权限等)。服务器完全只靠这个JWT认定用户身份。为了防止用户篡改数据,服务器在生成这个JWT的时候,会加上签名(详见后文)。服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。
17、springboot如何统一返回格式(ApiWapper-ljj-spring-boot-starter)
1、定义统一的响应对象
首先需要创建一个统一的响应对象,例如ApiResponse
或Result
类,它通常包含以下字段:
-
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 发生变化时,Vue Router 会自动更新视图,展示对应的组件,从而实现页面的动态切换。根据URL分配到对应的处理程序
vite:前端构建工具,能够显著提高开发效率,近乎即时的页面加载速度和热模块替换
axios:跨域,调用后端的一些接口;Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 node.js 中的异步通信。它是一个非常流行的选择,用于在前端应用中发送 AJAX 请求到服务器,以获取或发送数据。Axios 的主要优点是它支持多种请求方式,能够拦截请求和响应,转换请求和响应数据,并且可以取消请求;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对象包含了执行SQL语句的所有方法
第四步,sqlsession底层new了一个executor执行器
第五步,excutor执行器找到对应的mappedStatement
第六步,StatementHandler 根据 MappedStatement 中的 SQL 语句及参数映射信息,设置 SQL 参数并执行 SQL。
第七步,执行SQL语句之后,statementhandler处理数据库返回的结果集,并按照mappedstatement中定义的结果映射转换为Java对象。输入参数映射:paramerterHandler会将参数映射到SQL语句上
第八步,输出参数映射:结果集会将结果转化为我们需要的那个方法上的返回对象
最后,mappedstatement将结果映射为Java对象返回到客户端
Mybatis的优点
(1)原生JDBC的缺点
1、使用JDBC连接数据库没有办法实现 java 代码和 sql 语句之间的解耦.
2、使用JDBC连接数据库在接受查询的时候,需要自己手动写对象映射关系,效率低。
3、JDBC默认未使用连接池,如果使用连接池,需要我们自己书写,增加工作量.
MyBatis入门简单,上手快。
1.正是由于半自动化自定义SQL,所以比较灵活,可以更加精确的定义SQL。9
2.SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化。
3.便于程序升级迭代。
25、mybatis四种拦截器
1执行器拦截器(Executor):负责 SQL 语句的执行,包括缓存策略、批处理等
2语句拦截器(StatementHandler):负责预编译 SQL 语句以及执行 SQL 语句(监控表的更新或者修改)
3参数拦截器(ParameterHandler):负责将传入的参数设置到预编译 SQL 语句中(lastupdateby)
结果集拦截器(ResultHandler):负责从数据库返回的结果集中提取数据
-
Executor 拦截器:
- 用于拦截执行器(如 SimpleExecutor、ReuseExecutor 或 BatchExecutor)的行为。
- 可以用来修改 SQL 执行逻辑或添加额外的操作,比如记录 SQL 执行时间。
-
ParameterHandler 拦截器:
- 用于拦截参数处理器,可以改变传入 SQL 的参数值或类型。
- 例如,在参数处理器将参数设置到 PreparedStatement 之前对其进行修改。
-
ResultSetHandler 拦截器:
- 用于拦截结果集处理器,可以改变结果集的处理方式。
- 例如,在从结果集中提取数据之前或之后进行一些操作。
-
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反射的主要用途包括:
- 获取类信息:获取类名、包名、父类、接口等。
获取类名:c1.getName()
获取类的属性:c1.getFields(),只能获取public的属性,getDeclaredFields(),获取全部属性
获取方法:c1.getMethods(),只能获取public的方法,getDeclaredMethodes()获取全部方法
- 创建实例:通过类名或Class对象动态创建实例。
- 访问类成员:获取并操作类的字段、方法和构造器。
- 调用方法:即使方法是私有的,也可以调用。
- 修改字段值:可以读取和设置字段的值,即使字段是私有的,反射可以在程序运行阶段对字段进行修改。
基本概念:
- Class对象:每个类都有一个Class对象,它是Class类的实例。Class对象包含有关类的所有信息。
- Field对象:表示类中的字段(变量)。
- Method对象:表示类中的方法。
- Constructor对象:表示类中的构造器。
效率:通过new获取对象的操作要比通过反射获取对象速度快得多。
反射获取构造方法
反射获取成员变量
反射获取成员方法
反射的使用
MyBatis拦截器处理参数时使用,在做商城项目时,我们的数据表中都有lastUpdatBy这个字段,我们绝大多数业务都需要对lastUpdateBy进行赋值,所以我们就在Mybatis拦截器中通过反射获取参数,然后通过ThreadLocal中的localuser进行统一对其赋值。ThreadLocal为每一个线程提供一个独立的变量副本,使得每个线程在访问给变量时获取的都是自己独有的线程副本,我们定义了一个LocalUser对象存储在ThreadLocal中,然后就可以获取ThreadLocal中的Localuser对象为lastUpdateBy赋值。
JDBC底层:通过反射注册驱动
我们写的starter:AOP的地方:分页,springsecurity,token认证
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变量地址是一样的。
应用场景traceId,存储用户信息
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 特别适合处理高并发连接,常被用于负载均衡、网页缓存及反向代理,以提升网站的性能和承载能力。
「 SSL/TLS终端 」:Nginx可以作为SSL/TLS终端代理,对客户端和后端服务器之间的通信进行加密和解密。通过SSL/TLS终端代理,Nginx可以提供安全的通信通道,保护数据的机密性和完整性。Nginx还支持SSL/TLS协议的加密算法和安全性配置,可以提供高度安全的通信环境。
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 会触发事务回滚操作,将之前的操作撤销;对于受检异常(即必须被捕获或声明抛出的异常,例如IOException
,SQLException
等),除非你明确配置事务管理器去处理它们,否则事务不会回滚。
可以通过 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,解析token里的角色把它放到security上下文里边,看我们的方法上面有没有@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中设置的角色,判定设置的角色是否包含登录用户的角色,如果包含就有权限访问,如果