一,Spring的核心IOC及AOP
二,SpringBoot的自动装配
1.SpringBooot项目启动类注解:@SpringBootApplication
@SpringBootApplication
public class Demo1BaseApplication {
public static void main(String[] args) {
SpringApplication.run(Demo1BaseApplication.class, args);
}
}
2.将@SpringBootApplication 注解拆分:
拆分之后就是这样,下面详细介绍一下:
按住Ctrl,单击该注解,进入SpringBootApplication类中,上面有三个主要注解:
1、@SpringBootApplication注解可拆分为:@Springbootconfiguration、 @ComponentScan、@EnableAutoConfiguration
2、@Springbootconfiguration:这个注解用在某个类上,表示该类是个配置类
3、@ComponentScan:指定扫描包路径
4、@EnableAutoConfiguration:这个注解和自动装配有关,可继续拆分
拆分@EnableAutoConfiguration注解:
1、@EnableAutoConfiguration注解可拆分为:@AutoConfigurationPackage、@Import(AutoConfigurationImportSelector.class)
2、@AutoConfigurationPackage这几毛太复杂了,这个注解可单独当个专题
3、Import(AutoConfigurationImportSelector.class):用@Import注解将AutoConfigurationImportSelector类导入,之后用该类的selectImport()方法,去读取
MATE-INF/spring.factories
文件中配置的组件的全类名,并按照一定的规则过滤掉不符合要求的组件的全类名,将剩余读取到的各个组件的全类名集合返回给IOC容器并将这些组件注册为bean
下面讲一下AutoConfigurationImportSelector类:
顺着这几张图,捋一下怎么读取MATE-INF/spring.factories文件中配置的
1、利用getAutoConfigurationEntry(annotationMetadata)方法给容器中批量导入一些组件
2、调用getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件**,按照条件装配(@Conditional) 最终会按需配置
以上是一个很粗糙自动装配讲解,想了解更多,去搜下别的博客,最终形成自己的面试话术。
三,启动时执行自定义代码
四,Spring事务相关
1、事务介绍:
多个操作,要么同时执行,要么都不执行。目的是保持数据库表结果的一致性。举个最简单的例子:小明还小李100块钱,小明钱包-100,小张钱包+100,两个操作应该都要完成。不然就会造成数据的不一致。
2、事务的特性:
- 原子性:所有操作要么执行,要么不执行;
- 一致性:库表结果的一致性;
- 隔离性:不同事务个同时执行各自的操作,事务之间个不影响
- 持久性:值得是数据的持久化
3、Spring框架支持两种事务方式:①声明式、②编程式
- 声明式方式,使用@Transactional注解实现,放在某方法(类)上,从而给该方法加上事务。其实现原理是Spring的AOP机制,基于AOP动态代理的方式,对目标方法前后进行拦截,方法执行前创建事务,或者添加到到已存在的事务中,之后根据方法的执行情况,判断提交还是回滚事务。
- 编码式:编程式事务管理使用 TransactionTemplate 或者直接使用底层的 PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate
- 声明式是一种无入侵的方式、使用简单,推荐使用
4、事务的传播途径:
Spring事务传播途径有7种,如下表:
PROPAGATION_REQUIRED | (需要事务-Spring默认)当前存在事务,则假如当前事务,不存在事务,则创建新的事物执行。 |
PROPAGATION_SUPPORTS | (支持事务)当前存在事务,则加入当前事务执行,若不存在事务,则以非事务的形式执行。 |
PROPAGATION_MANDATORY | (强制使用事务)当前存在事务,假如当前事务执行,若当前不存在事务,则抛出 IllegalTransactionStateException 异常。 |
PROPAGATION_REQUIRES_NEW | (新建事务)无论当前是否存在事务,都创建新的事务。 |
PROPAGATION_NOT_SUPPORTED | (不支持事务)当前存在事务,将当前事务挂起;当前不存在事务,则以非事务的形式执行。 |
PROPAGATION_NEVER | (不使用事物)当前不存在事务,则以无事务的形式执行;当前存在事务,则抛出IllegalTransactionStateException异常。 |
PROPAGATION_NESTED | (嵌套)当前存在事务,则在嵌套事务内执行;若当前不存在事务,则创建事务执行。 |
5、事务的5种隔离级别:
和数据库一致 | 和数据库的事务隔离级别保持一致 |
读未提交 | 最低级的隔离级别,一个事物可读取另一个事务修改切未提交的数据,另一个事务也可读取该事务修改且为提交的数据,可能导致:脏读、幻读、不可重复读 |
读已提交 | 默认的隔离级别,一个事务只能读取另一个事务提交后的数据,可防止脏读发生。但是当一个事务中查询一次库之后事务并未结束,第二次查询读取到了别的事务已提交的结果,可导致两次读取的结果不一致,还是有可能造成幻读、不可重复读 |
可重复读 | 该事务隔离级别更高一点,他能保证一个事务内多次查询能得到相同的数据集,但是在该事务执行过程中,别的事务添加了数据,则该事务还是能读到添加的数据。能解决脏读、不可重复读,但是幻读还是可能发生 |
串行化 | 最高的隔离级别,保证只有一个事务对库表操作,可防止脏读、幻读、不可重复读,但是效率很慢 |
6、什么是脏读、幻读、不可重复读?
脏读 | 一个事务修改了某数据还未提交,此时另一个事务读取到该事务修改且未提交的数据,并使用了该数据。 |
不可重复读 | 一个事务中两或多次查询某数据集,但中间有另外一个事务在该事务两次查询之间修改了该数据,并已提交,导致该事务两次查询得到不同的数据集。 |
幻读 | 一个事务对表中所有数据修改,但同时另一个事务添加了一条数据行。当该事务执行完毕之后,发现还有数据行违背修改,就像幻觉一样,简称幻读。 |
7、事务失效的情况
事务失效的常见原因:
①访问权限问题:事务只能修饰public修饰的方法上。
②方法使用final修饰:事务是基于AOP,AOP本质实动态代理,很显然会失效。(static一样)
③方法内部调用:同一个类中一个事务方法调用另一个事务方法,同样也是动态代理的原因。
④未被SpringBoot容器管理:事务方法所在的类,没有交给Spring容器管理。比如Service层一个类,没有被@Service注解修饰,没有交给Spring容器管理,自然事务会失效。
⑤多线程调用:假如一个事务方法内,开启另一个线程去处理别的操作,此时两个线程,自然事务失效
⑥表不支持:
⑦错误的传播特性设置:比如说设置的就不支持事务,有事务挂起,或报异常等传播属性。
⑧try/catch吞噬异常:假如说catch补货了异常,但并没有抛出异常,那事务就失效了。Spring框架检测到RunTimeException异常,就会回滚事务。检测不到,自然不会回滚。
⑨抛出了别的异常:步骤⑧中说明,catch捕获到异常之后,抛出的异常不是RunTimeException或其子类
⑩自定义了回滚异常
①①嵌套事务回滚多了:
五,Bean的生命周期
Spring Bean的生命周期,可粗略归纳为4个阶段,分别是:
- 实例化
- 属性赋值
- 初始化
- 销毁
四个大步骤具体细分后,大致如下:
下面对这些步骤进行讲解:
1、实例化对象
当向容器请求一个没有初始化的bean时,或初始化bean的时候需要注入另一个尚末初始化的依赖时,容器就会调用doCreateBean()方法进行实例化,实际上就是通过反射的方式创建出一个bean对象
2、属性赋值
就是给这个Bean的属性赋值,也就是我们常说的DI(依赖注入)过程。
3、初始化:
(1)Aware相关接口检查及相关依赖配置:Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的些资源。如实现BeanNameAware接口可以获取到BeanName,实现BeanFactoryAware接口可以获取到工厂对象BeanFactory等
(2)BeanPostProcessor前置处理:执行BeanPostProcessor的前置处理方法postProcessBeforelnitialization(),对Bean进行一些自定义的前置处理
(3)判断是否实现了lnitializingBean接口:如果实现了,将会执行lnitializingBean的afeterPropertiesSet()初始化方法;
(4)执行用户自定义的初始化方法:比如自定义了init-mothed方法
(5)BeanPostProcessor后置处理:执行BeanPostProcessor的后置处理方法postProcessAfterinitialization()
4、销毁 :
(1)判断Bean是否实现了DestructionAwareBeanPostProcessor接口:如果实现了,则会执行DestructionAwareBeanPostProcessor后置处理器的销毁回调方法。
到这一步,这个Bean已经可以使用了
(2)判断Bean是否实现了DisposableBean接口,如果实现了将会调用其实现的destroy()方法。
(3)判断这个Bean是否配置了dlestroy-method等自定义的销毁方法,如果有的话,则会自动调用其配置的销毁方法。
-------------------------------------完毕!-----------------------------------------------
上面的介绍足以应对大多数面试,但是其中也可以延伸出别的问题。
比如说实例化对象这个环节,这个Bean是怎么实例化的?这个时候涉及到一些Bean工厂的知识。
再比如属性赋值这个环节,实际上就是我们常说的 DI(依赖注入),此时就需要了解依赖注入的基础知识,什么是DI?有那几种注入方式?循环依赖问题?Spring是怎么解决循环依赖问题的?都需要学习。
大佬直通车: DI 、Spring怎么解决循环依赖(三级缓存)