Spring 面试题汇总

spring的模块有哪些?

Spring Core:基础,可以说Spring其他所有的功能都依赖于该类库。主要提供IOC和DI功能。

Spring Aspects:该模块为与AspectJ的集成提供支持。

Spring AOP:提供面向方面的编程实现。

Spring JDBC:Java数据库连接。

Spring JMS:Java消息服务。

Spring ORM:用于支持Hibernate等ORM工具。

Spring Web:为创建Web应用程序提供支持。

Spring Test:提供了对JUnit和TestNG测试的支持。

说下你对spring的理解?

1.Spring框架帮我们管理对象及其依赖关系

2.是基于POJO的轻量级和最小侵入性编程

3.通过依赖注入进行控制反转

4.通过依赖注入和面向接口来松耦合

spring是如何解决循环依赖的?

首先循环依赖有三种情况:

1.构造函数循环依赖(无法解决)

首先要解决循环依赖就是要先实例化,然后放入三级缓存暴露出来,那么如果是构造函数这一步循环依赖,实例化的时候就会产生无限递归创建,所以不能别解决

如果是延迟加载的话可以解决(另当别论),后面真正用这个对象的时候有没有问题我就不清楚了

2.setter方式的多例的循环依赖(无法解决)

如果是多例的,在容器初始化的时候,不会去创建,所以早期没有放入到三级缓存中暴露出来,所以无法解决循环依赖,会报错

3.setter方式的单例循环依赖(A依赖B,B依赖A)

1.首先第一步会实例化对象A,然后把A放入到三级缓存中提前暴露出来

2.然后去属性注入B,没有B就去创建B,然后回去实例化B,把B放入三级缓存中

3.属性注入A,现在A中是在三级缓存,并把A移到二级缓存,并且存放的是工厂,通过延迟加载,因为不知道A到底是代理对象还是原生对象,所以通过延迟加载的方法创建Bean

4.B就创建完成

5.回到A,回到A后,就B属性已经注入,之后生成A,如果A代理了,就把二级缓存中的代理A赋值给原生的A返回放入到一级缓存中,如果不需要代理,就直接放入一级缓存

二级缓存中放的是创建中的对象

因此,我们可以发现,spring利用三级缓存进行对象的早期暴露,让我们提前去引用他,来完成循环依赖

面试官喜欢问,为什么要用三级,为什么要用二级,为什么用一级缓存?

我们知道循环依赖有几种

一种是:A依赖B,B依赖A,都无代理

就是因为我们要提前暴露循环依赖的对象,三级缓存中不放工厂行不行,可以,但是为了去解决万分之一的循环依赖的情况,就每次都先生成对象,不合理

第二种:A依赖B,B依赖A,有代理

B把A的代理给创建出来了,不用二级缓存,那我们的三级缓存就不干净了

spring的生命周期

1.实例化

2.属性注入

3.初始化

//Bean实例对象包装相关属性,如名称,类加载器,所属容器等信息       

invokeAwareMethods(beanName, bean);
// 前置通知
applyBeanPostProcessorsBeforeInitialization
//文件中通过init-method属性指定的
invokeInitMethods(beanName, wrappedBean, mbd);
// 后置通知生成AOP
applyBeanPostProcessorsAfterInitialization

4.销毁

spring中的AOP是在DI之前还是之后

对于单个对象创建,我们可以看到当前对象AOP是在DI之后

对于属性注入其他的对象,那么先生成其他对象的代理AOP,然后给当前对象注入属性,这种情况就是B的AOP在A的注入之前

A要去注入属性B,然后B又要代理,AOP在DI之前

spring AOP主要是用来干什么,是如何实现的

1.我们需要配置切点,来告诉容器,需要对那些类中的方法进行增强

2.首先是通过动态代理的方式去进行方法的增强,如果代理的类有实现接口就用jdk代理,否则就用cglib代理

3.我们需要配置切面,来告诉容器增强方法有哪些,并且每个方法对应一个拦截器

4.给每个方法绑定拦截器的执行链

5.通过递归加索引去调用

6.如果你要执行在之前,那么你的方法必须在proceed这个方法之前,如果你要执行再之后,你的方法必须在preceed之后

spring注入bean的方式有哪些

1.通过@Configuration+@Bean的方式注入

2.通过实现ImportBeanDefinitionRegistrar接口可以往容器中注入BeanDefinition,从而注入bean

3.通过实现ImportSelector接口可以往spring容器中批量注入Bean

4.通过实现FactoryBean接口可以往Spring容器中自定义Bean

springmvc的流程

1.用户发送请求至前端控制器DispatcherServlet;
2.DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handler;
3.处理器映射器根据请求url找到具体的处理器Handler,生成处理器对象及处理器拦截器(如果有则生成),一并返回给DispatcherServlet;
4.DispatcherServlet 调用 HandlerAdapter处理器适配器,请求执行Handler;
5.HandlerAdapter 经过适配调用 具体处理器进行处理业务逻辑;
6.Handler执行完成返回ModelAndView;
7.HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
8.DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
9.ViewResolver解析后返回具体View;
10.DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
11.DispatcherServlet响应用户。

对于spring中的bean的懒加载如何实现

1.如果想让单例的bean懒加载,就要加@Lazy注解

2.多例的bean在容器初始化本来就是懒加载的,只有在调用的时候去实例化,所以加@Lazy有没有意义

spring中声明式事务注解中的参数

1.propagation传播机制

2.isolation隔离级别

3.timeout超时时间,如果共用一个事务,只有之前的事务设置的timeout有效

4.rollbackFor回滚异常

5.noRollbackFor不会滚异常

事务的特点

事务应该具有 4 个属性:原子性、一致性、隔离性、持久性

原子性(Automicity)

一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。

一致性(Consistency)

事务必须是使数据库从一个一致性状态变到另一个一致性状态。

一致性与原子性是密切相关的。

隔离性(Isolation)

一个事务的执行不能被其他事务干扰。

即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性(Durability)

持久性也称永久性(Permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

spring的事务有几种传播机制

首先一定是基于嵌套事务来讨论的,如果是一个事务就没什么意义了

比如现在有一个这样的例子::test调用methodB,methodB有Transactional,methodB调用methodA,methodA也有事务 ,这个就是我们的嵌套事务

//是默认的传播机制,如果B方法中调用A,如果B中有事务,则A无论是否开启事务都会用B的事务,任何地方出现异常A和B都回滚
REQUIRED(必须的),
// 如果B调用A
// 1.B中是REQUIRED,A中是REQUIRES_NEW,会新起一个事务,则A,B互相不影响
// 2.B中是REQUIRES_NEW,A中是REQUIRED,那么A和B用同一个事务,会把A的事务挂起
// 3.B中是REQUIRES_NEW,A中是REQUIRES_NEW,那么A和B各用各的事务,互相不影响
REQUIRES_NEW(需要新的),
// 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行,完全依赖最外层事务
SUPPORTS(1),
// 必须运行在事务里面
MANDATORY(强制性的),
// 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NOT_SUPPORTED,
// 以非事务方式执行,如果当前存在事务,则抛出异常。
NEVER,
// 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
NESTED(嵌套的);

spring中事务的隔离级别

// 数据库默认的隔离级别
DEFAULT(-1),
// 读未提交 ru,会导致脏读
// 一事务对数据进行了增删改,但未提交,另一事务可以读取到未提交的数据。如果第一个事务这时候回滚了,那么第二个事务就读到了脏数据。
READ_UNCOMMITTED(1),
// 读已提交 rc 避免脏读,允许不可重复读和幻读
// 不可重复读:一个事务中发生了两次读操作,读到的结果不一致,因为其他的事务往里面插入了一条数据
// 幻读:A查询数据不存在,然后B事务往里面插入一条数据,并提交事务,A继续查询还是不存在,也插入这条数据,但是由于数据存在,就插入失败
READ_COMMITTED(2),
// 可重复读 rr 避免脏读,不可重复读,允许幻读 
REPEATABLE_READ(4),
// 序列化
SERIALIZABLE(8);
// 从上到下,隔离级别越来越高,并发性能就越来越差

脏读:一事务对数据进行了增删改,但未提交,另一事务可以读取到未提交的数据。如果第一个事务这时候回滚了,那么第二个事务就读到了脏数据。

不可重复读:一个事务中发生了两次读操作,读到的结果不一致,因为其他的事务往里面插入了一条数据

幻读:A查询数据不存在,然后B事务往里面插入一条数据,并提交事务,A继续查询还是不存在,也插入这条数据,但是由于数据存在,就插入失败

spring的失效场景

1.非public方法 事务的实现原理是代理增强,非public不能进行代理增强,不能进行JDK或者CGLIB代理

演示: 把methodA改成protect

2.调用本类的方法 (调用methodC方法)

3.抛出捕捉的非RuntimeException,如果想要捕捉所有异常(rollbackFor = {Exception.class})

或者指定@Transactional(rollbackFor = {RuntimeException.class,ClassNotFoundException.class},propagation = Propagation.REQUIRED)  多个异常类

4.我们知道事务就是依赖于数据库的,所以数据库不支持肯定也是失效的!!比如myIsam

5.如果AOP使用了CGLIB代理,事务方法或者类不是public,无法被外部包访问到,或者是final无法继承

你可能感兴趣的:(Spring 面试题汇总)