Spring面试题

目录

spring是什么?

谈谈你对IOC的理解

IoC容器的技术剖析

谈谈你对AOP的理解

如何实现AOP,项⽬哪些地⽅⽤到了AOP

说⼀下Spring的事务机制

什么时候@Transactional失效

Spring中后置处理器的作⽤

说说常⽤的SpringBoot注解,及其实现

Spring HTTP请求过程


spring是什么?

Spring 是一个轻量级的控制反转( IoC)和面向切面(AOP 的容器框架
        --从大小与开销两方面而言 Spring 都是轻量级的。
        --通过控制反转 (IoC) 的技术达到松耦合的目的
        --提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
        --包含并管理应用对象 (Bean) 的配置和生命周期,这个意义上是一个容器。
        --将简单的组件配置、组合成为复杂的应用,这个意义上是一个框架。(比如说把mybatis / hibernate、redis等等 粘合在一起运用,可以让我们的企业开发更快、更简洁)

谈谈你对IOC的理解

引入IoC容器,利用依赖关系注入的方式,实现对象之间的解耦

IOC控制反转名称由来

软件系统在没有引入IoC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

软件系统在引入IoC容器之后,这种情形就完全改变了,由于IoC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IoC容器会主动创建一个对象B注入到对象A需要的地方。

通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。

IoC容器的技术剖析

IoC中最基本的技术就是“反射(Reflection)”编程

我们可以把IoC容器的工作模式看做是工厂模式的升华,可以把IoC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

谈谈你对AOP的理解

系统是由许多不同的组件所组成的,每一个组件各负责一块特定功能。除了实现自身核心功能之外,这些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去。这些系统服务经常被称为横切关注点,因为它们会跨越系统的多个组件。
当我们需要为分散的对象引入公共行为的时候,OOP(面向对象) 则显得无能为力。也就是说, OOP 允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。 日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。
OOP 设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP :将程序中的交叉业务逻辑(比如安全,日志,事务,授权判断等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP 可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事情
技术: 动态代理

如何实现AOP,项⽬哪些地⽅⽤到了AOP

利⽤动态代理技术来实现AOP,⽐如JDK动态代理或Cglib动态代理,利⽤动态代理技术,可以针对某个类 ⽣成代理对象,当调⽤代理对象的某个⽅法时,可以任意控制该⽅法的执⾏,⽐如可以先打印执⾏时间, 再执⾏该⽅法,并且该⽅法执⾏完成后,再次打印执⾏时间。
项⽬中,⽐如事务、权限控制、⽅法执⾏时⻓⽇志都是通过AOP技术来实现的,凡是需要对某些⽅法做统 ⼀处理的都可以⽤AOP来实现,利⽤AOP可以做到业务⽆侵⼊。

说⼀下Spring的事务机制

1. Spring事务底层是基于数据库事务AOP机制的
2. ⾸先对于使⽤了@Transactional注解的Bean,Spring会创建⼀个代理对象作为Bean
3. 当调⽤代理对象的⽅法时,会先判断该⽅法上是否加了@Transactional注解
4. 如果加了,那么则利⽤事务管理器创建⼀个数据库连接
5. 并且修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮常重要的⼀步
6. 然后执⾏当前⽅法,⽅法中会执⾏sql
7. 执⾏完当前⽅法后,如果没有出现异常就直接提交事务
8. 如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务
9. Spring事务的隔离级别对应的就是数据库的隔离级别
10. Spring事务的传播机制是Spring事务⾃⼰实现的,也是Spring事务中最复杂的
11. Spring事务的传播机制是基于数据库连接来做的,⼀个数据库连接⼀个事务,如果传播机制配置为需要新开⼀个事务,那么实际上就是先建⽴⼀个数据库连接,在此新数据库连接上执⾏sql

spring事务传播机制

概念:多个事务方法相互调用时 , 事务如何在这些方法间传播 (如事务A调用事务B,事务如何影响)
种类(指的是被调用方的策略):
REQUIRED(Spring默认的事务传播类型)

如果当前存在事务,则加入这个事务,

如果当前没有事务,则自己新建一个事务

SUPPORTS

当前存在事务,则加入当前事务,

如果当前没有事务,就以非事务方法执行,

MANDATORY(强制有事务)

当前存在事务,则加入当前事务,

如果当前事务不存在,则抛出异常。

REQUIRES_NEW

创建一个新事务,

如果存在当前事务,则挂起该事务(互不干扰)。

NOT_SUPPORTED
以非事务方式执行 , 如果当前存在事务,则挂起当前事务
NESTED(嵌套)如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务) 
NEVER不使用事务,如果当前事务存在,则抛出异常

NESTED和REQUIRES_NEW的区别:
       REQUIRES_NEW是新建一个事务并且新开启的这个事务与原有事务无关,而NESTED则是当前存在事务时(我们把当前事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。 在NESTED情况下父事务回滚时,子事务也会回滚,而在REQUIRES_NEW情况下,原有事务回滚,不会影响新开启的事务。
NESTED和REQUIRED的区别:
       REQUIRED情况下,调用方存在事务时,则被调用方和调用方使用同一事务,那么被调用方出现异常时,由于共用一个事务,所以无论调用方是否catch其异常,事务都会回滚 而在NESTED情况下,被调用方发生异常时,调用方可以catch其异常,这样只有子事务回滚,父事务不受影响
总结:NESTED(嵌套):父调用子,父回滚则子回滚;子回滚,父可以捕获处理

什么时候@Transactional失效

  1. 因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有是被代理对象调⽤时,那么这个注解才会⽣效,所以如果是被被代理对象来调⽤这个⽅法,那么@Transactional是不会⽣效的。(配置了aop的类或者类中方法上有@Transactional注解的是代理类
  2. 同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是基于⽗⼦类来实现的, ⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效  

怎么解决?注入自己的对象,即引用自己的代理对象

Spring中后置处理器的作⽤

Spring中的后置处理器分为BeanFactory后置处理器和Bean后置处理器,它们是Spring底层源码架构设计中⾮常重要的⼀种机制,同时开发者也可以利⽤这两种后置处理器来进⾏扩展。BeanFactory后置处理器
表示针对BeanFactory的处理器,Spring启动过程中,会先创建出BeanFactory实例,然后利⽤BeanFactory处理器来加⼯BeanFactory,⽐如Spring的扫描就是基于BeanFactory后置处理器来实现的。
Bean后置处理器也类似
Spring在创建⼀个Bean的过程中,⾸先会实例化得到⼀个对象,然后再利⽤Bean后置处理器来对该实例对象进⾏加⼯,⽐如我们常说的依赖注⼊就是基于⼀个Bean后置处理器实现的,通过该Bean后置处理器来给实例对象中加了@Autowired注解的属性⾃动赋值,还⽐如我们常说的AOP,也是利⽤⼀个Bean后置处理器来实现的,基于原实例对象,判断是否需要进⾏AOP,如果需要,那么就基于原实例对象进⾏动态代理,⽣成⼀个代理对象。

说说常⽤的SpringBoot注解,及其实现

1. @SpringBootApplication注解:这个注解标识了⼀个SpringBoot⼯程,它实际上是另外三个注解的组 合,这三个注解是:
         a. @SpringBootConfiguration:这个注解实际就是⼀个@Configuration,表示启动类也是⼀个配置类
         b. @EnableAutoConfiguration:向Spring容器中导⼊了⼀个Selector,⽤来加载ClassPath下 SpringFactories中所定义的⾃动配置类,将这些⾃动加载为配置Bean
        c. @ComponentScan:标识扫描路径,因为默认是没有配置实际扫描路径,所以SpringBoot扫描的 路径是启动类所在的当前⽬录
2. @Bean注解:⽤来定义Bean,类似于XML中的<bean>标签,Spring在启动时,会对加了@Bean注解的⽅法进⾏解析,将⽅法的名字做为beanName,并通过执⾏⽅法得到bean对象
3. @Controller、@Service、@ ResponseBody、@Autowired都可以说

Spring HTTP请求过程

(1)浏览器的http请求,被Tomcat容器的监听器监听

(2)请求通过Filter链,到达前置分发器DispatcherServlet

(3)前置分发器DispatcherServlet接收到HTTP请求之后,通过解析HTTP请求的URL获取URI,根据URI从处理器映射HandlerMappings当中获取请求对应的处理器Handler和处理器拦截器HandlerInterceptor

(4)前置分发器DispatcherServlet根据获取得到的Handler选择合适的适配器HandlerAdapter。如果成功获取适配器HandlerAdapter,先调用HandlerInterceptor#preHandler,然后调用处理器Handler,也就是Controller方法

(5)Controller Handler调用Service的业务处理方法

(6)Service调用DAO的数据处理方法

(7)最后依次返回结果
链接:https://www.jianshu.com/p/4e82f77d857f

spring 和springboot 的区别

java的循环依赖怎么解决

图片

面试官:”Spring是如何解决的循环依赖?“

答:Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。

当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。

当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取:

第一步,先获取到三级缓存中的工厂;

第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。

当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

面试官:”为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?“

答:如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。

图片

图片

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论

打赏作者

风一般的程序媛

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值