Spring常见面试题

说说你对IOC的理解?

相当于自己找女朋友和婚介公司帮忙找女朋友的过程。IOC可以重点理解以下几个部分:

1.谁控制谁:在之前的编程过程中,都是需要什么对象就自己去创建什么对象,由程序员来控制对象,而有了IOC容器之后,就变成由IOC容器来控制对象。

2.控制什么:在实现过程中所需要的对象和需要依赖的对象

3.什么是反转:在没有IOC容器之前我们都是在对象中主动去创建要依赖的对象,这是正转的,有了IOC之后,依赖的对象直接由IOC容器创建并注入到对象中,由主动创建变成了被动接受,这就是反转。

4.哪些方面被反转:依赖的对象。

1,2,3三级缓存的放置时间和删除时间

三级缓存:createBeanInstance之后,使用的方法是AddSingletonFactory

二级缓存:第一次从三级缓存确定对象是代理对象还是普通对象的时候,同时删除三级缓存,使用的方法是getSingleton

一级缓存:生成完整对象之后放到一级缓存,删除二三级缓存,使用方法addSingleton

5.FactoryBean和BeanFactory的区别是什么

相同点:都是用来创建Bean对象的。

不同点:使用BeanFactory创建对象的时候,必须遵循严格的生命周期流程,比较复杂,相当于批量化的生产。如果想要简单的自定义某个对象的创建过程,同时创建完成的对象想交给Spring进行管理,就需要实现FactoryBean的接口了,相当于定制, BeanFactory的接口要实现三个方法:

isSingleton:是否是单例对象

getObjectType:获取返回对象的类型

getObject:自定义创建对象的过程(不仅可以通过反射,还可以通过new、动态代理等)

6.Spring中用到的设计模式有哪些?

单例模式:bean默认都是单里的

原型模式:制定作用域为protoType的时候

工厂模式:BeanFactory

模版方法:postProcessBeanFactory,onRefresh, initPropertyValue

策略模式:XmlBeanDefinitionReader, PropertiesBeanDefinitionReader

观察者模式:listener, event, multicast

适配器模式:Adapter

装饰者模式:BeanWrapper

责任链模式:使用aop的时候会先生成一个拦截器链

代理模式:动态代理

委托者模式:delegate

7.Spring的AOP的底层实现原理

AOP是IOC的一个扩展功能,先用IOC,后有AOP,AOP 只是再ioc的整个流程中新增的一个扩展点而已:BeanPostProcessor

总:aop的概念,应用场景,动态代理

分:bean的创建过程中有一个步骤可以对bean进行扩展实现,aop本身就是一个扩展功能,所以在BeanPostProcessor的后置处理方法中来进行实现:

1.代理对象的创建过程(advice,切面,切点)

2.通过jdk或者cglib的方式来生成代理对象

3.在执行方法调用的时候,会调用到生成的字节码为减重,直接会找到DynamicAdvisoredInterceptor中的intercept方法,,从此方法开始执行

4.根据之前定义好的通知生成拦截器链

5.从拦截器中依次获取每一个通知开始进行执行,在执行过程中,为了方便找到下一个通知是哪个,会有一个CglibMethodInvocation的对象,找的时候是从-1的位置依次开始查找并且执行的。 

  • JDK动态代理与CGLib动态代理的区别
  1. JDK动态代理只能为接口创建代理类,而CGLib动态代理而可以直接为类创建代理类。
  2. JDK动态代理创建代理类的速度要比CGLib动态代理创建代理类的速度要快。

JDK动态代理创建代理类的速度要比CGLib动态代理创建代理类的速度要快,主要有以下两个原因:

  1. JDK动态代理是基于接口的,而CGLib动态代理是基于继承的。在创建代理类时,JDK动态代理只需要生成接口的代理实现类即可,而CGLib动态代理需要生成目标类的子类作为代理类。因为继承会涉及到一些额外的维护操作,所以CGLib动态代理的代理类创建速度较慢。

  2. JDK动态代理生成代理类的过程中使用了Java原生的反射机制,而CGLib动态代理生成代理类的过程中则使用了更为底层的字节码操作。虽然使用字节码操作可以实现更为灵活的代理功能,但是相比于反射机制,它的执行效率会稍低一些。

3. CGLib动态代理创建代理类的性能要比JDK动态代理创建代理类的性能要高。

CGLib动态代理创建代理类的性能比JDK动态代理高的主要原因在于它采用了不同的实现方式。

JDK动态代理是通过生成一个实现被代理接口的代理类来实现的,因此它需要实现被代理接口的所有方法,包括那些并没有在代理类中实现的方法。这种方式导致了JDK动态代理在创建代理类时需要进行大量的操作,包括创建代理类、编译代理类、加载代理类等,因此它的创建代理类的性能相对较低。

而CGLib动态代理则采用了继承被代理类的方式,它不需要实现被代理接口,也不需要加载和编译代理类。相反,它在运行时通过动态生成一个被代理类的子类来创建代理类。这种方式避免了JDK动态代理所需要的很多操作,因此它的创建代理类的性能相对较高。

此外,CGLib动态代理还支持对非接口类型的类进行代理,而JDK动态代理只能代理接口类型的类。这也使得CGLib动态代理更加灵活和强大。

总的来说,CGLib动态代理创建代理类的性能比JDK动态代理高主要是因为它采用了不同的实现方式,避免了许多额外的操作。

所以在为单例对象创建代理类时,因为不需要频繁的创建代理对象,所以优先考虑用CGLib动态代理来创建,这样该代理类的执行时的性能比较高,反之则采用JDK动态代理创建代理类。

8.Spring的事务是怎么回滚的?

总:Spring的事务是由aop实现的,首先要生成具体的代理对象,然后按照aop的整套流程来执行具体的逻辑操作,正常情况下要通过通知来完成核心功能,但是事务不是通过通知来实现的,而是通过一个TransactionInterceptor来实现的,然后调用invoke来实现具体的逻辑

分:1.先做准备工作,解析各个方法上事务相关的属性,根据具体的属性来判断是否开始新事务。

        2.当需要开启新的事务的时候,获取数据库连接,关闭自动提交功能,开启事务。

        3.执行具体的sql逻辑操作。

        4.在操作的过程中,如果执行失败了,那么就会通过completeTransactionAfterThrowing来完成事务的回滚操作,回滚的具体逻辑是通过doRollback方法来实现的,实现的时候也是先获取连接对象,通过连接对象来回滚。

        5.如果执行过程中,没有任何意外情况的发生,那么通过commitTransactionAfterRetrunning来完成事务的提交操作,提交的具体逻辑是通过doCommit方法来实现的,实现的时候也是要获取数据库连接对象,通过连接对象来提交

        6.当事务执行完毕之后需要清除相关的事务信息,通过cleanupTransactionInfo方法来实现

可以关注几个对象,例如TransactionInfo\TransactionStatus

9.谈一下Spring的事务传播特性

一共有7种传播特性

重点注意:Required,Requires_new, nested, Support, Not_support, Never, Mandatory

PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。

PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。

NESTED:如果业务方法在一个事务中执行,就在这个事务中嵌套,如果没有事务按着required执行,开启单独的事务,这种事务有多个事务的保存点,内部事务的回滚对外部事务没有影响

 如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者回滚;如果当前没有事务,则同required

但是如果父事务提交,则会携带子事务一起提交。如果父事务回滚,则子事务会一起回滚。相反,子事务异常,则父事务可以不会滚(捕获子事务的异常),也可以回滚。

光看这几个概念啥也不清楚,就是看着玩玩而已,我们还是得需要自行去测试总结才能更加深入理解

某一个事务嵌套另一个事务的时候怎么办?

A方法调用B方法,AB方法都有事务,那么A如果有异常,B怎么半,如果B有异常,A怎么办?

总:事务的传播特性指的是不同方法的嵌套过程中,事务应该如何进行处理,是用同一个事务还是不同的事务,当出现异常的时候是回滚还是提交,两个方法之前的相互影响,日常工作中使用比较多的是Required,requires_new, nested

分:1.先说事务不同分类有三类:支持当前事务、不支持当前事务、嵌套事务

        2.如果外层方法是required,内层方法是:required、requires_new, nested

        3.如果外层方法是requires_new,内层方法是:required、requires_new, nested       

        4.如果外层方法是nested,内层方法是:required、requires_new, nested 

核心处理逻辑非常简单:

1.判断内外方法是否为同一个事务(不适用于NESTED)

是:异常统一在外层方法处理

否:内层方法有可能影响到外层方法,但是外层方法不会影响内层

外层方法事务传播特性内层方法事务传播特性执行情况
REQUIREDREQUIRED如果程序正常执行,那么内层事务不会提交,在外部事务中统一进行事务提交,
如果内层事务,或者外层事务中出现异常情况,那么会在外层事务的处理中统一进行异常回滚
REQUIRED NEW如果外层方法中存在事务,内层方法在运行的时候会挂起外层事务并开启一个新的事务,如果程序正常执行,则内层方法优先事务提交,然后外层方法再提交;如果内层方法中存在异常,内层事务会优先回滚,外层方法事务也会回滚,如果外层方法中存在异常,那么内层事务正常正常提交,而外层方法会进行回滚操作 
NESTED如果外层方法中有事务,那么直接创建一个保存点,后续操作中如果没有异常情况,
那么会清除保存点信息,并且在外层事务中进行提交操作,
如果内层方法中存在异常情况,那么会回滚到保存点,外层方法事务会直接进行回滚,
如果外层方法中存在异常情况,
那么会内层方法会正常执行,并且执行完毕之后释放保存点,并且外层方法事务会进行回滚
 REQUIRED NEW  REQUIRED  如果程序正常执行,那么内层事务不会提交,在外部事务中统一进行事务提交,如果内层事务,或者外层事务中出现异常情况,那么会在外层事务的处理中统一进行异常回滚
REQUIRED NEW 如果外层方法中存在事务,内层方法在运行的时候会挂起外层事务并开启一个新的事务,如果程序正常执行,则内层方法优先事务提交,然后外层方法再提交;如果内层方法中存在异常,内层事务会优先回滚,外层方法事务也会回滚,如果外层方法中存在异常,那么内层事务正常正常提交,而外层方法会进行回滚操作
NESTED如果外层方法中有事务,那么直接创建一个保存点,如果外层方法没有事务,那么就创建一个新的事务,后续操作中如果没有异常情况,那么会清除保存点信息,并且在外层事务中进行提交操作,如果内层方法中存在异常情况,那么会回滚到保存点,外层方法事务会直接进行回滚,如果外层方法中存在异常情况,那么会内层方法会正常执行,并且执行完毕之后释放保存点,并且外层方法事务会进行回滚 
NESTEDREQUIRED 如果程序正常执行,那么内层事务不会提交,在外部事务中统一进行事务提交,如果内层事务,或者外层事务中出现异常情况,那么会在外层事务的处理中统一进行异常回滚
REQUIRED NEW 如果外层方法中存在事务,内层方法在运行的时候会挂起外层事务并开启一个新的事务,如果程序正常执行,则内层方法优先事务提交,然后外层方法再提交;如果内层方法中存在异常,内层事务会优先回滚,外层方法事务也会回滚,如果外层方法中存在异常,那么内层事务正常正常提交,而外层方法会进行回滚操作
NESTED如果外层方法中有事务,那么直接创建一个保存点,如果外层方法没有事务,那么就创建一个新的事务,后续操作中如果没有异常情况,那么会清除保存点信息,并且在外层事务中进行提交操作,如果内层方法中存在异常情况,那么会回滚到保存点,外层方法事务会直接进行回滚,如果外层方法中存在异常情况,那么会内层方法会正常执行,并且执行完毕之后释放保存点,并且外层方法事务会进行回滚

现在面试常问的两个问题:

1、REQUIRED和NESTED回滚的区别
两种方式区别,最大的问题在于保存点的设置,很多同学会认为内部设置REQUIRED和NESTED效果是一样的,其实在外层方法对内层方法的异常情况在进行捕获的时候区别很大,两者报的异常信息都不同,使用REQUIRED的时候,会报Transaction rolled back because it has been marked as rollback-only信息,因为内部异常了,设置了回滚标记,外部捕获之后,要进行事务的提交,此时发现有回滚标记,那么意味着要回滚,所以会报异常,而NESTED不会发证这种情况,因为在回滚的时候把回滚标记清除了,外部捕获异常后去提交,没发现回滚标记,就可以正常提交了。
2、REQUIRES_NEW和NESTED区别
这两种方式产生的效果是一样的,但是REQUIRED_NEW会有新的连接生成,而NESTED使用的是当前事务的连接,而且NESTED还可以回滚到保存点,REQUIRED_NEW每次都是一个新的事务,没有办法控制其他事务的回滚,但NESTED其实是一个事务,外层事务可以控制内层事务的回滚,内层就算没有

异常,外层出现异常,也可以全部回滚。

REQUIRED(默认的传播机制):

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

REQUIRES_NEW:

  • 新建事务,如果当前存在事务,则把当前事务挂起

NESTED 

  • 如果当前没有事务,则新建事务
  • 如果当前存在事务,则创建一个当前事务的子事务(嵌套事务),子事务不能单独提交,只能和父事务一起提交。

REQUIRED

一个类的A方法调用另一个类的B方法。
假设在A方法存在一个当前事务,B方法的事务传播机制为REQUIRED,则B方法会合并到A方法的事务里执行。

A、B任意一个方法异常(默认是RuntimeException和Error)都会导致A、B的操作被回滚。

Spring事务管理器不会吞异常。
B异常后会抛给A,A如果没有catch这个异常,会继续向上抛。如果A catch住了,Spring事务管理器会替A向上抛一个UnexpectedRollbackException。总之,一旦A、B回滚,A的调用方一定能收到一个异常感知到回滚。

REQUIRES_NEW

一个类的A方法调用另一个类的B方法。
假设在A方法存在一个当前事务,B方法的事务传播机制为REQUIRES_NEW,则B方法会新建一个事务并把A所在的事务挂起。A事务等到B事务执行完后,恢复执行。

这种传播机制下,需要小心死锁问题。A事务被挂起了,如果B事务要加的锁被A占用了就会发生死锁。

  • 如果B发生异常,B事务一定回滚,B的异常随后会抛给A,如果A catch住了这个异常,A不会回滚,否则A也会回滚。
  • 如果A发生异常,则只会回滚A,不会回滚B。

NESTED

一个类的A方法调用另一个类的B方法。
假设在A方法存在一个当前事务,B方法的事务传播机制为NESTED,则B方法会作为A方法所在事务的一个子事务执行。

子事务的底层实现:B方法执行前会在A所在事务中创建一个savepoint,B异常后回滚到此savepoint。

  • 如果B异常,B一定回滚,B的异常随后会抛给A,如果A catch住了这个异常,A不会回滚,否则A也会回滚。这种情况和REQUIRES_NEW一样。
  • 如果A发生异常,则A、B都会回滚。


 

Spring的事务是如何回滚的

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值