Spring IOC、DI、AOP以及Spring MVC面试原理

Spring IOC加载流程:

初始化ApplicationContext;ApplicationContext实现类中完成的
通过BeanDefinitionReader加载配置文件,并封装成BeadDefinition
将BeanDefinition包装成BeanWrapper
将BeanWrapper保存至IOC容器中

ApplicationContext实现了BeanFactory接口,ApplicationContext是一个接口,用于加载和管理Spring IoC容器中的bean。它扩展了BeanFactory接口,并提供了更多的功能。通过配置文件支持解析不同的文件
ClassPathXmlApplicationContext
AnnotationConfigApplicationContext
WebApplicationContext

(1)读取配置文件:通过ResourceLoader的getResource方法将类路径,URL,文件系统等方式读为Resource
(2)解析配置文件:解析是在BeanDefinitionReader中完成的,最常用的类是XMLBeanDefinitionReader来完成的,解析涉及到很多步骤,最常见的是将XML文件读取成Document文档,DefaultBeanDefinitionDocumentReader对Document对象进一步解析,DefaultBeanDefinitionDocumentReader又委托给BeanDefinitionParserDelegate进行解析,如果是标准的XML Namespace元素,则会委托合适的NameSpaceHandler进行解析最终解析的结果封装为BeanDefinitionHolder至此解析完成。
(3)Bean的注册:bean的注册是在BeanFactory里完成的,BeanFactory最常见的一个实现类是DefaultListableBeanFactory,,它实现了BeanDefinitionRegistry接口,所以其中的registerBeanDefinition()方法,可以对BeanDefinition进行注册这里附带一提,最常见的XmlWebApplicationContext不是自己持有BeanDefinition的,它继承自AbstractRefreshableApplicationContext,其持有一个DefaultListableBeanFactory的字段,就是用它来保存BeanDefinition所谓的注册,其实就是将BeanDefinition的name和实例,保存到一个Map中。刚才说到,最常用的实现DefaultListableBeanFactory,其中的字段就是beanDefinitionMap,是一个ConcurrentHashMap。
(4)实例化:注册也完成之后,在BeanFactory的getBean()方法之中,会完成初始化,也就是依赖注入的过程

Spring DI加载流程:

Spring DI的基本流程:
(1)循环读取BeanDefinition的缓存信息
(2)调用getBean方法创建对象实例
(3)将创建好的对象实例包装BeanWrapper对象
(4)将BeanWrapper对象缓存到IOC容器
(5)循环IOC容器执行注入

实例化策略根据配置实现不同的策略,将其封装为BeanWrapper
SimpleInstantiationStrategy
实例化有两种情况?
目标类配置了AOP,实例化对象代理类
目标类没有配置AOP,实例化原生对象

为什么要使用BeanWrapper?
统一一个对外访问对象的入口
扩展一些功能,缓存一些配置信息

DI原理:
**FactoryBean.getBeans()**触发依赖注入
createBeanInstance->用反射创建了对象实例->封装成BeanWrapper();
populateBean()–>根据beanName、BeanDefinition、BeanWrapper找到需要赋值的属性,把需要
赋值的属性封装为一个集合,集合的元素是PropertyValue,PropertyValue保存了需要赋值的Bean,赋值需要调用的方法,要赋什么值。
applyPropertyValues() 循环PropertyValue,挨个调用BeanWrapper的setValue()方法,用反射代用setter方法完成赋值。

DI是依赖注入,当IOC容器完成初始化后,针对非懒加载的Bean进行依赖注入或者当第一次getBean时
1)实例化 通过反射机制实例化Bean
2)依赖注入 判断其field是否有需要被注入的属性,如果有则进行注入该依赖的Bean属性
3) 循环依赖的问题,可以通过多级缓存来解决,二级缓存,三级缓存

Spring循环依赖三级缓存是否可以去掉第三级缓存?
在这里插入图片描述
BeanFactory是ApplicationContext的父类

BeanFactory与FactoryBean的区别:
BeanFactory是管理Bean的工厂
FactoryBean是创建对象的工厂Bean

Spring AOP面向切片编程:

基本概念:
(1)切面(Aspect)
 切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。 Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。

(2) 目标对象(Target)
 目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。

(3) 连接点(JoinPoint)
 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。连接点由两个信息确定:

  • 方法(表示程序执行点,即在哪个目标方法)
  • 相对点(表示方位,即目标方法的什么位置,比如调用前,后等)
     简单来说,连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。

(4) 切入点(PointCut)
 切入点是对连接点进行拦截的条件定义。切入点表达式如何和连接点匹配是AOP的核心,Spring缺省使用AspectJ切入点语法。
 一般认为,所有的方法都可以认为是连接点,但是我们并不希望在所有的方法上都添加通知,而切入点的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配连接点,给满足规则的连接点添加通知。

(5) 通知(Advice)
 通知是指拦截到连接点之后要执行的代码,包括了“around”、“before”和“after”等不同类型的通知。Spring AOP框架以拦截器来实现通知模型,并维护一个以连接点为中心的拦截器链。

  • 前置通知(Before)指某连接点之前执行的通知,但这个通知不能阻止连接点之前的代码的执行。
  • 后置通知(After)指当某连接点退出时执行的通知(无论是正常返回还是异常退出)
  • 返回后通知(After Return Advice)是某个连接点正常完成通知后执行的通知,不包括抛出异常的情况。
  • 环绕通知(Around Advice)围绕在连接点前后,比如一个方法调用前后
  • 异常通知(After Throwing Advice)是在方法抛出异常导致退出时执行的通知。

(6) 织入(Weaving)
 织入是将切面和业务逻辑对象连接起来, 并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。

(7) 增强器(Adviser)
 Advisor是切面的另外一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。Advisor由切入点和Advice组成。
 Advisor这个概念来自于Spring对AOP的支撑,在AspectJ中是没有等价的概念的。Advisor就像是一个小的自包含的切面,这个切面只有一个通知。切面自身通过一个Bean表示,并且必须实现一个默认接口。
  简单来讲,整个 aspect 可以描述为: 满足 pointcut 规则的 joinpoint 会被添加相应的 advice 操作。

两个步骤
AOP的入口:AOP的入口是由接入BeanPostProcessor后置处理器开始的,在bean初始化后提供回调入口,在AbstractAutowireCapableBeanFactory中initializeBean()方法中添加BeanPostProcessor后置处理器。
创建代理类:1,PostProcessAfterInitialization()方法中调用了wrapIfNecessary(),如果代理类实现了接口,使用JDK动态代理,如果没有实现接口,使用cglib动态代理。
代码织入:1,在JDK动态代理类中,如果动态代理类会实现InvokeHandler接口重写invoke方法,在Invoke方法中会通过getInterceptorsAndDynamicInterceptionAdvice方法从提供的配置实例config中获取Advisor列表,遍历处理这个Advisor切面,如果是PointCutAdvisor,则判断此Advisor能否应用到目标方法Method上,将满足条件的Advisor通过AdvisorAdapter转化为MethodInterceptor拦截器列表返回。接下来在Invoke中判断拦截器是否为空,如果为空,则直接调用Method.Invoke反射方法,如果拦截器列表不为空,则调用ReflectiveMethodInvocation.proceed()方法触发执行链的执行,Proceed()串其了整个Interceptor调用链。
在这里插入图片描述

两个重要的类:intercept 拦截器,责任链组件 invoke()
invocation 调用器,嗲用通知回调的组件,procceed()

Spring Mvc加载流程:

WebApplicationContext和ApplicationContext的区别

存的Web相关的比如:Listener、Servlet、Filter、Session,Page等

在这里插入图片描述
1、用户发送请求至前端控制器DispatcherServlet。
2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、处理器映射器根据URL找到具体的处理器(可以根据xml配置、注解进行查找),生成包含Handler以及处理器拦截器HandlerIntercepter的HandlerExecuteChain执行链,执行链包括处理器对象及处理器拦截器(如果有则生成)一并返回。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。
5、HandlerAdapter经过适配结果处理Handler,调用对应的业务方法
6、HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet。
7、DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
8、ViewReslover解析后返回具体View.
9、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
10、DispatcherServlet响应用户。

BeanFactory和ApplicationContext的区别

BeanFactory,以Factory结尾,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等。

从表面上看,ApplicationContext如同BeanFactory一样具有Bean定义,Bean关联关系的设置及根据请求分发Bean的功能,但是ApplicationContext在此基础上还提供了其他功能。
(1)MessageSource, 提供国际化的消息访问
(2)统一的资源文件读取方式,如URL和文件
(3)事件传播特性,即支持aop特性
(4)载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层

以下是三种比较常见的ApplicationContext实现方式
(1)ClassPathXmlApplicationContext:从ClassPath的XML配置文件中读取上下文,并生成上线文定义。
(2)FileSystemXMLApplicationContext:由文件系统中的XML配置文件读取上下文
(3)XmlWebApplicationContext:由Web应用的XML文件读取上下文

BeanFactory与FactoryBean

BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。

FactoryBean:是一个Bean,作用是产生其他Bean的实例。

Spring Bean的生命周期

在这里插入图片描述
1.Spring对Bean进行实例化(相当于程序中的new Xx())
2.Spring将值和Bean的引用注入进Bean对应的属性中
3.如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()方法(实现BeanNameAware清主要是为了通过Bean的引用来获得Bean的ID,一般业务中是很少有用到Bean的ID的)
4.如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanDactory(BeanFactory bf)方法并把BeanFactory容器实例作为参数传入。(实现BeanFactoryAware 主要目的是为了获取Spring容器,如Bean通过Spring容器发布事件等)
5.如果Bean实现了ApplicationContextAwaer接口,Spring容器将调用setApplicationContext(ApplicationContext ctx)方法,把y应用上下文作为参数传入.(作用与BeanFactory类似都是为了获取Spring容器,不同的是Spring容器在调用setApplicationContext方法时会把它自己作为setApplicationContext 的参数传入,而Spring容器在调用setBeanDactory前需要程序员自己指定(注入)setBeanDactory里的参数BeanFactory )
6.如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法(作用是在Bean实例创建成功后对进行增强处理,如对Bean进行修改,增加某个功能)
7.如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet方法,作用与在配置文件中对Bean使用init-method声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法。
8.如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessAfterInitialization(后初始化)方法(作用与6的一样,只不过6是在Bean初始化前执行的,而这个是在Bean初始化后执行的,时机不同 )
9.经过以上的工作后,Bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁
10.如果Bean实现了DispostbleBean接口,Spring将调用它的destory方法,作用与在配置文件中对Bean使用destory-method属性的作用一样,都是在Bean实例销毁前执行的方法。

实例化 Instantiation
属性赋值 Populate
初始化 Initialization
销毁 Destruction

Spring生命周期相关的在不同的阶段都有扩展点,
第一大类:影响多个Bean的接口,InstantiationAwareBeanPostProcessor和BeanPostProcessor分别在实例化阶段和初始化阶段有Before和After操作
在这里插入图片描述
第二大类:

  • Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如BeanNameAware可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在初始化阶段之前调用的!

Aware接口众多,这里同样通过分类的方式帮助大家记忆。
Aware接口具体可以分为两组,按照执行顺序可以分为两组,如下排列顺序同样也是Aware接口的执行顺序。
Aware Group1
BeanNameAware
BeanClassLoaderAware
BeanFactoryAware

Aware Group2
EnvironmentAware
EmbeddedValueResolverAware 这个知道的人可能不多,实现该接口能够获取Spring EL解析器,用户的自定义注解需要支持spel表达式的时候可以使用,非常方便。
ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware) 这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前的ApplicationContext对象,因为ApplicationContext是一个复合接口。

第三大类:简单的两个生命周期接口
InitializingBean接口 对应生命周期的初始化阶段
DisposableBean接口 对应生命周期的销毁阶段
在这里插入图片描述

请别再问Spring Bean的生命周期了,简书
面试官,请别再问Spring Bean的生命周期了!,CSDN

Spring Bean的作用域

在这里插入图片描述

Spring中所使用的设计模式

Spring设计模式的详细使用案例可以阅读这篇文章:https://blog.csdn.net/a745233700/article/details/112598471

(1)工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象

(2)单例模式:Bean默认为单例模式

(3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略

(4)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术

(5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate

(6)适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller

(7)观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。

(8)桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库

JAVA类的加载和初始化过程

1、类的加载过程
JVM将类的加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)
1)、装载:查找并加载类的二进制数据;
2)、链接:
        验证:确保被加载类的正确性,包括文件格式验证、元数据验证、 字节码验证、符号引用验证等
        准备:为类的静态变量分配内存,并将其初始化为默认值
        解析:把类中的符号引用转换为直接引用
3)、初始化:为类的静态变量赋予正确的初始值

[面试官:请你谈谈Java的类加载过程](https://blog.csdn.net/ln152315/article/details/79223441?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161805086816780274174964%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161805086816780274174964&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~hot_rank-1-79223441.first_rank_v2_pc_rank_v29&utm_term=%E7%B1%BB%E7%9A%84%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B)

2、类的初始化,类什么时候才被初始化
1)、创建类的实例,也就是new一个对象
2)、访问某个类或接口的静态变量,或者对该静态变量赋值
3)、调用类的静态方法
4)、反射(Class.forName("com.vince.Dog"))
5)、初始化一个类的子类(会首先初始化子类的父类)
6)、JVM启动时标明的启动类,即文件名和类名相同的那个类

3、类的加载
值的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的Java.lang.Class对象,用来封装类在方法区类的对象。

对象实例化的先后顺序

1.首先,初始化父类中的静态成员变量和静态代码块;
2.然后,初始化子类中的静态成员变量和静态代码块;
3.其次,初始化父类的普通成员变量和代码块,再执行父类的构造方法;
4.最后,初始化子类的普通成员变量和代码块,再执行子类的构造方法;

spring 事务的传播机制看这篇就够了

为什么会有传播机制?

spring 对事务的控制,是使用 aop 切面实现的,我们不用关心事务的开始,提交 ,回滚,只需要在方法上加 @Transactional 注解,这时候就有问题了。

场景一: serviceA 方法调用了 serviceB 方法,但两个方法都有事务,这个时候如果 serviceB 方法异常,是让 serviceB 方法提交,还是两个一起回滚。
场景二:serviceA 方法调用了 serviceB 方法,但是只有 serviceA 方法加了事务,是否把 serviceB 也加入 serviceA 的事务,如果 serviceB 异常,是否回滚 serviceA 。
场景三:serviceA 方法调用了 serviceB 方法,两者都有事务,serviceB 已经正常执行完,但 serviceA 异常,是否需要回滚 serviceB 的数据。

传播机制生效条件
因为 spring 是使用 aop 来代理事务控制 ,是针对于接口或类的,所以在同一个 service 类中两个方法的调用,传播机制是不生效的

传播机制类型
下面的类型都是针对于被调用方法来说的,理解起来要想象成两个 service 方法的调用才可以。

PROPAGATION_REQUIRED (默认)

  • 支持当前事务,如果当前没有事务,则新建事务
  • 如果当前存在事务,则加入当前事务,合并成一个事务

REQUIRES_NEW

  • 新建事务,如果当前存在事务,则把当前事务挂起
  • 这个方法会独立提交事务,不受调用者的事务影响,父级异常,它也是正常提交

NESTED

  • 如果当前存在事务,它将会成为父级事务的一个子事务,方法结束后并没有提交,只有等父事务结束才提交
  • 如果当前没有事务,则新建事务
  • 如果它异常,父级可以捕获它的异常而不进行回滚,正常提交
  • 但如果父级异常,它必然回滚,这就是和 REQUIRES_NEW 的区别

SUPPORTS

  • 如果当前存在事务,则加入事务
  • 如果当前不存在事务,则以非事务方式运行,这个和不写没区别

NOT_SUPPORTED

  • 以非事务方式运行
  • 如果当前存在事务,则把当前事务挂起

MANDATORY

  • 如果当前存在事务,则运行在当前事务中
  • 如果当前无事务,则抛出异常,也即父级方法必须有事务

NEVER

  • 以非事务方式运行,如果当前存在事务,则抛出异常,即父级方法必须无事务

一点小说明
一般用得比较多的是 PROPAGATION_REQUIRED , REQUIRES_NEW;
REQUIRES_NEW 一般用在子方法需要单独事务,暂时找不到例子,以后补充 。

spring 事务的传播机制看这篇就够了

死锁产生的四个必要条件

•互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
•不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
•请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
•循环等待条件::若干进程之间形成一种头尾相接的循环等待资源关系。
以上给出了导致死锁的四个必要条件,只要系统发生死锁则以上四个条件至少有一个成立。事实上循环等待的成立蕴含了前三个条件的成立,似乎没有必要列出然而考虑这些条件对死锁的预防是有利的,因为可以通过破坏四个条件中的任何一个来预防死锁的发生。

Bean的生命周期

Spring的事务传播

在这里插入图片描述

@Transactional
public void trans(){
   sub();  // 记录流水,数据库操作
   log(); 
   query();
}


@Transactional   SUPPORTS
public info query(){
    ..... 
}

@Transactional REQUIRES_NEW
public void log(){
   .....   // 记录日志 
}


在这里插入图片描述
事务传播行为的类型:
REQUIRED(默认):例如A方法调用了B方法,A方法加了事务,B方法无论加不加事务,都默认合并到A方法的事务中,也就是说越等于在一个事务中,只要A,B有一个失败,两个都失败。

SUPPORTS:例如A方法调用B方法,B方法不会开启一个新的事务。如果A方法加了事务,如果B方法没加,则整体没有事务。如果A方法加了事务,则合并到A方法的事务上,也就是说约等于在一个事务中,只要A,B有一个失败,两个都失败。(与REQUIRED的区别是,外部不存在事务的时候,不会开启新的事务)。

REQUIRES_NEW:例如A方法调用B方法,B方法,无论A方法是否存在事务,B方法都会新开启一个事务,假如B方法中记录的是日志,其业务逻辑与A没有关联,这样A,B方法的成功互不影响。

携程二面:什么是Spring事务?spring事务传播行为有哪些?

spring 事务的传播机制看这篇就够了

Spring中事务的原理

在Spring里边,一般通过@Transactional注解开启事务,这个注解本质上是使用AOP的思想,在事务开启的时候,把当前链接保存在ThreadLocal里边,从而保证了线程的隔离性,就是每个线程里边自己玩自己的。

数据库通过InnoDB存储引擎实现事务。‌ MySQL中,事务是由存储引擎实现的,而InnoDB是支持事务的最广泛的存储引擎。事务的实现依赖于InnoDB提供的ACID属性,包括原子性、一致性、隔离性和持久性‌。

‌原子性‌:原子性是指事务中的操作要么全部执行,要么全部不执行。如果其中一个操作失败,整个事务会被回滚到开始前的状态。InnoDB存储引擎通过undo log(回滚日志)来实现这一机制,当事务需要回滚时,undo log会记录所有修改操作的反向操作,确保数据可以恢复到事务开始前的状态‌。

‌一致性‌:一致性确保事务处理前后数据库的状态满足所有的完整性约束。InnoDB通过在事务开始时创建一个版本链来保证数据的一致性‌。

‌隔离性‌:隔离性是指多个事务并发执行时,一个事务的修改对其他事务不可见,以避免数据竞争条件。MySQL通过锁定机制和隔离级别来控制事务的隔离性‌。

‌持久性‌:持久性确保一旦事务提交,其所做的修改会永久保存到数据库中,即使系统故障也不会丢失。InnoDB通过redo log(重做日志)来实现持久性,redo log记录了事务执行的所有修改操作,确保在系统故障后能够恢复数据‌。

如果在事务里边开启多线程,事务会失效。

Spring事务@Transactional失效的场景

1.数据库引擎是否支持事务(MySql的MyIsam引擎不支持事务)
2.注解所在的类是否被加在为Bean
3.注解所在方法是否为public修饰的
4.是否发生了类自调用问题
5.所有数据源是否加载了事务管理器
6.@Transactinal的扩展配置propagationsh是否正确
7.事务中的异常被catch吞了,捕获异常未抛出
8.异常抛出类型错误 这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:

@Transactional(rollbackFor = Exception.class)
这个配置仅限于 Throwable 异常类及其子类。

9.事务方法被final修饰或者加了static关键字
因为Spring事务是用动态代理实现的,如果方法被final修饰,则代理类无法对目标方法进行重写,无法植入事务功能。

10.多线程情况下事务会失效

Spring事务失效的 8 大原因,这次可以吊打面试官了!

Spring事务失效场景

Spring多线程解决方案

既然Spring提供的声明式事务注解@Transactional无法满足多线程间的事务一致性,那可以试试通过编程式事务的方式来解决。主要思路:每个线程都开启各自的事务,待所有线程的业务执行完成,统一提交或回滚。

分布式事务最经典的解决方案:

  • 两阶段提交->三阶段提交
  • TCC
  • 本地消息表
  • 最大努力通知

分布式事务最经典的七种解决方案

分布式事务两阶段提交和三阶段提交有什么区别?

Spring中Bean的生命周期

请别再问Spring Bean的生命周期了!

跨域问题

跨域问题产生的背景:前端分离的大背景下,对于同样的域名、协议,例如:对于同样一个请求,前后端可能使用不同的端口号,前端请求http://www.aaa.com:80,后端请求 http://www.aaa.com.8080,则会抛出异常。

同源策略是指一种浏览器安全机制,它规定只有当请求的源(协议、域名和端口)与目标源完全匹配时,才能互相访问资源。这意味着如果两个页面的协议、域名或端口有任何一项不同,它们之间的请求将会受到限制,以防止未授权的资源访问和潜在的安全威胁。

只有通过前端页面访问后端的时候,才会出现这种情况。假如后端通过RestTemplate和HttpClient则会正常访问。就是因为浏览器的同源策略。

解决方案:
1.设置@CrossOrigin注解,其可以加在Controller层,也可以加在方法上,可以在注解里边配置跨域的来源地址。
2.实现了WebMvcConfigurer借口的实现类,在addCorsMappings的接口中配置相关的路径,可以添加跨域方法请求类型GET,POST,PUT,DELETE,跨域来源(域名)
3.使用CorsFilter进行全局跨域配置
4.使用HttpServletResponse设置响应头(局部跨域配置)

详解SpringBoot应用跨域访问解决方案

Spring boot的原理

1、main方法中SpringApplication.run(HelloBoot.class,args)的执行流程中有refreshContext(context)。

2、而这个refreshContext(context)内部会解析,配置类上自动装配功能的注解@EnableAutoConfiguration中的,@EnableAutoConfiguration中的,引入类AutoConfigurationImportSelector。

3、AutoConfigurationImportSelector这个类中的方法getAutoConfigurationEntry其最终会调用SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader())会读取jar包中的/项目中的META-INF/spring.factories文件。

4、spring.factories配置了自动装配的类,过滤出所有AutoConfigurationClass类型的类,最后通过@Condition排除无效的自动配置类,自动装配Bean。

SpringBoot | 自动配置原理

最近一场面试(Spring Boot的自动装配原理及流程)

如何手写一个Start?

1.首先先建立META-INF/spring.factories将新实现的Start名称配置到org.springframework.boot.autoconfigure.EnableAutoConfiguration

2.Start名称加上@Configuration,根据实际情况,决定是否添加@ConditionOn注解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值