2024年java面试(二)--spring篇

文章目录

  • 1.spring事务传播机制
  • 2.spring事务失效原因
  • 3.Bean的生命周期
  • 4.Bean作用域
  • 5.依赖注入三种方式(Ioc的三种实现方式)
  • 6.实例化bean的三种方式
  • 7.IOC容器初始化加载Bean流程


1.spring事务传播机制

声明式事务虽然优于编程式事务,但也有不足,声明式事务管理的粒度是方法级别,而编程式事务是可以精确到代码块级别的。

要想实现事务管理和业务代码的抽离,就必须得用到 Spring 当中的AOP,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。

①.声明式
在配置文件中设置以下6项

(1) required
如果客户端没有事务 在bean中新起一个事务
如果客户端有事务bean 中就加进去

子事务 主事务 结果
异常 正常,并try-catch异常 均回滚
正常 异常 均回滚
正常 异常,并try-catch异常 不回滚

(2)requiresNew
不管客户端有没有事务服务器段都新起一个事务
如果客户端有事务就将事务挂起

子事务 主事务 结果
异常 正常,并try-catch异常 子回滚,主不回滚
正常 异常 子不回滚,主回滚
异常 正常 均回滚

(3)supports
如果客户端没有事务服务端也没有事务
如果客户端有事务服务端就加一个事务

(4)mandatcry
如果客户端没有事务服务端就会报错
如果客户端有事务服务端就加事务

(5)notSupported
不管客户端有没有事务服务端都没有事务
如果客户端有事务服务端就挂起

(6)never
不管客户端有没有事务服务端都没有事务
如果客户端有事务就报错

(7)NESTED
如果当前存在事务,则在嵌套事务内执行。
如果当前没有事务,则进行与REQUIRED类似的操作

子事务 主事务 结果
异常 正常,并try-catch异常 子回滚,主不回滚
正常 异常 均回滚
异常 正常 均回滚

②.编程式事务
Javax.transaction.UserTranscation
JTA 事务可以精确到事务的开始和结束

2.spring事务失效原因

①.service没有托管给spring
失效原因: spring事务生效的前提是,service必须是一个bean对象
解决方案: 将service注入spring

②抛出受检异常
失效原因: spring默认只会回滚非检查异常和error异常
解决方案: 配置rollbackFor

③业务自己捕获了异常(try-catch)
失效原因: spring事务只有捕捉到了业务抛出去的异常,才能进行后续的处理,如果业务自己捕获了异常,则事务无法感知。
解决方案
(1)将异常原样抛出;
(2)设置TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

④切面顺序导致
失效原因: spring事务切面的优先级顺序最低,但如果自定义的切面优先级和他一样,且自定义的切面没有正确处理异常,则会同业务自己捕获异常的那种场景一样
解决方案
(1)在切面中将异常原样抛出;
(2)在切面中设置TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

⑤非Public方法
失效原因: spring事务默认生效的方法权限都必须为public
解决方案
(1)将方法改为public;
(2)修改TansactionAttributeSource,将publicMethodsOnly改为false

⑥父子容器
失效原因: 子容器扫描范围过大,将未加事务配置的serivce扫描进来
解决方案
(1)父子容器个扫个的范围;
(2)不用父子容器,所有bean都交给同一容器管理

⑦方法用final/static修饰
失效原因: 因为spring事务是用动态代理实现,因此如果方法使用了final修饰,则代理类无法对目标方法进行重写,植入事务功能
解决方案: 方法不要用final修饰

⑧调用本类方法
失效原因: 本类方法不经过代理,无法进行增强
解决方案
(1)注入自己来调用;
(2)使用@EnableAspectJAutoProxy(exposeProxy = true) + AopContext.currentProxy()

⑨多线程调用
失效原因: 因为spring的事务是通过数据库连接来实现,而数据库连接spring是放在threadLocal里面。同一个事务,只能用同一个数据库连接。而多线程场景下,拿到的数据库连接是不一样的,即是属于不同事务

⑩错误的传播行为
失效原因: 使用的传播特性不支持事务

⑪使用了不支持事务的存储引擎
失效原因: 使用了不支持事务的存储引擎。比如mysql中的MyISAM

⑫数据源没有配置事务管理器
: 因为springboot,他默认已经开启事务管理器。

⑬被代理的类过早实例化
失效原因: 当代理类的实例化早于AbstractAutoProxyCreator后置处理器,就无法被AbstractAutoProxyCreator后置处理器增强

3.Bean的生命周期

①默认情况下,IOC容器中bean的生命周期分为五个阶段:
(1)调用构造器 或者是通过工厂的方式创建Bean对象
(2)给bean对象的属性注入值
(3)调用初始化方法,进行初始化, 初始化方法是通过init-method来指定的
(4)使用
(5)IOC容器关闭时, 销毁Bean对象

②当加入了Bean的后置处理器后,IOC容器中bean的生命周期分为七个阶段:
调用构造器 或者是通过工厂的方式创建Bean对象
(1)给bean对象的属性注入值
(2)执行Bean后置处理器中的 postProcessBeforeInitialization
(3)调用初始化方法,进行初始化, 初始化方法是通过init-method来指定的
(4)执行Bean的后置处理器中 postProcessAfterInitialization
(5)使用
(6)IOC容器关闭时, 销毁Bean对象

4.Bean作用域

名称 作用域
singleton 单例对象,默认值的作用域
prototype 每次获取都会创建⼀个新的 bean 实例
request 每⼀次HTTP请求都会产⽣⼀个新的bean,该bean仅在当前HTTP request内有效。
session 在一次 HTTP session 中,容器将返回同一个实例
global-session 将对象存入到web项目集群的session域中,若不存在集群,则global session相当于session

默认作用域是singleton,多个线程访问同一个bean时会存在线程不安全问题

5.依赖注入三种方式(Ioc的三种实现方式)

①构造方法注入
②setter注入
③基于注解的注入

6.实例化bean的三种方式

①无参构造方法实例化(Spring默认,常用,需要bean类中存在无参构造方法);
②静态工厂实例化
③实例化工厂实例化

7.IOC容器初始化加载Bean流程

@Override
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) {
  // 第一步:刷新前的预处理 
  prepareRefresh();
  //第二步: 获取BeanFactory并注册到 BeanDefitionRegistry
  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  // 第三步:加载BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加载器等)
  prepareBeanFactory(beanFactory);
  try {
    // 第四步:完成BeanFactory准备工作后的前置处理工作 
    postProcessBeanFactory(beanFactory);
    // 第五步:实例化BeanFactoryPostProcessor接口的Bean 
    invokeBeanFactoryPostProcessors(beanFactory);
    // 第六步:注册BeanPostProcessor后置处理器,在创建bean的后执行 
    registerBeanPostProcessors(beanFactory);
    // 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析); 
    initMessageSource();
    // 第八步:注册初始化事件派发器 
    initApplicationEventMulticaster();
    // 第九步:子类重写这个方法,在容器刷新的时候可以自定义逻辑 
    onRefresh();
    // 第十步:注册应用的监听器。就是注册实现了ApplicationListener接口的监听器
    registerListeners();
    //第十一步:初始化所有剩下的非懒加载的单例bean 初始化创建非懒加载方式的单例Bean实例(未设置属性)
    finishBeanFactoryInitialization(beanFactory);
    //第十二步: 完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,完成创建
    finishRefresh();
    }
  ……
} 

四个阶段
①实例化 Instantiation
②属性赋值 Populate
③初始化 Initialization
④销毁 Destruction

多个扩展点
①影响多个Bean
BeanPostProcessor
InstantiationAwareBeanPostProcessor
②影响单个Bean
Aware

完整流程

①实例化一个Bean--也就是我们常说的new;
②按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;
③如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,也就是根据就是Spring配置文件中Bean的id和name进行传递
④如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现setBeanFactory(BeanFactory)也就是Spring配置文件配置的Spring工厂自身进行传递;
⑤如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,和4传递的信息一样但是因为ApplicationContext是BeanFactory的子接口,所以更加灵活
⑥如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization()方法,BeanPostProcessor经常被用作是Bean内容的更改,由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术
⑦如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
⑧如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(),打印日志或者三级缓存技术里面的bean升级
⑨以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。
⑩当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,或者根据spring配置的destroy-method属性,调用实现的destroy()方法

你可能感兴趣的:(面试题,java,面试,spring)