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无法继承