Spring面试题
Spring框架中的单例bean是线程安全的吗?
Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装。所以单例bean是线程不安全的。
如果Bean是有状态的(有状态即有数据存储功能),则需要我们自己来保证线程安全。
最简单的方法即改变Bean作用域,将单例变为原型,即singleton
改为protopyte
,这样每次请求的Bean都是重新new一个Bean。
或用ThreadLocal来包装变量,将变量变为线程私有的。
或使用synchronized修饰变量来实现线程同步。
此时这个成员变量是有状态的,我们就需要通过上诉方式使得线程安全,对于userService
来说,它是无状态的,即不能修改,那么此时就是线程安全的。
什么是AOP,你们项目中有没有使用到AOP
AOP成为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块命名为切面
,减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。
常见的AOP使用场景:
- 记录操作日志
- 缓存处理
- Spring中内置的事务
前置通知
@Before("execution(* com.example.service.MyService.*(..))")
public void beforeAdvice(JoinPoint joinPoint) {
// 使用JoinPoint获取方法参数等信息
Object[] args = joinPoint.getArgs();
// 执行前置逻辑
}
后置通知
@AfterReturning(pointcut = "execution(* com.example.service.MyService.*(..))", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
// 使用JoinPoint获取方法参数等信息
Object[] args = joinPoint.getArgs();
// 处理返回值
}
异常通知
@AfterThrowing(pointcut = "execution(* com.example.service.MyService.*(..))", throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
// 使用JoinPoint获取方法参数等信息
Object[] args = joinPoint.getArgs();
// 处理异常
}
最终通知
@After("execution(* com.example.service.MyService.*(..))")
public void afterAdvice(JoinPoint joinPoint) {
// 使用JoinPoint获取方法参数等信息
Object[] args = joinPoint.getArgs();
// 执行最终逻辑
}
环绕通知
@Around("execution(* com.example.service.MyService.*(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
// 使用ProceedingJoinPoint获取方法参数等信息
Object[] args = joinPoint.getArgs();
// 执行前置逻辑
Object result = joinPoint.proceed(); // 执行目标方法
// 执行后置逻辑
return result;
}
Spring中事务失效的场景有哪些
- 异常捕获处理
原因:事务通知只有捕捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知晓
解决:在catch块添加throw new RuntimeException(e)抛出 - 抛出检查异常
原因:在Spring中默认只会回滚非检查异常也就是RuntimeException
解决:配置rollbackFor属性@Transactional(rollbackFor=Exception.class)
- 非public方法
- this失效
Spring的bean的生命周期
- 实例化(Instantiation):
当Spring容器启动时,它会根据配置信息或者注解等方式创建Bean的实例。
属性赋值(Population of properties):
在Bean实例创建后,Spring容器会将配置文件或者注解中设置的属性值注入到Bean中,包括通过构造函数注入、setter方法注入等。
- 设置Bean的名字(Setting Bean Name):
如果配置文件或注解中指定了Bean的名字,Spring会将Bean的名字设置到Bean中。
调用BeanPostProcessor的前置初始化方法:
如果在Spring容器中注册了BeanPostProcessor接口的实现类,那么它们的postProcessBeforeInitialization方法将在Bean初始化之前被调用。
- 初始化方法(Initialization):
如果Bean类中定义了初始化方法(通过@PostConstruct注解或者init-method配置),则在Bean初始化之后,这些初始化方法会被调用。
- 调用BeanPostProcessor的后置初始化方法:
如果在Spring容器中注册了BeanPostProcessor接口的实现类,它们的postProcessAfterInitialization方法将在Bean初始化之后被调用。
- Bean可用(Bean is ready):
此时,Bean已经被完全初始化,可以被应用程序使用。
- 使用Bean:
应用程序可以通过Spring容器获取和使用已经初始化的Bean。
- 销毁前调用销毁方法:
如果Bean类中定义了销毁方法(通过@PreDestroy注解或者destroy-method配置),当容器关闭时,这些销毁方法会被调用。
- 销毁:
当Spring容器关闭时,所有的Bean会被销毁。在这个阶段,会调用DisposableBean接口的destroy方法以及destroy-method中指定的销毁方法。
什么是Spring的循环依赖?
最简单的例子就是:两个对象之间需要的属性相互依赖
大致流程如下:
根据Bean的生命周期推算,当初始化A时,看到需要一个B属性,此时容器中没有,就去实例化B,但是此时A是没有完整的new出来,即不在ioc容器中,当初始化B时,发现A没有,此时又去实例化,所以产生的循环依赖问题。
Spring解决循环依赖是通过三级缓存,对应的三级缓存如下所示:
缓存名称 | 源码名称 | 作用 |
---|---|---|
一级缓存 | singletonObjects | 单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象 |
二级缓存 | earlySingletonObjects | 缓存早起的bean对象(生命周期还没走完) |
三级缓存 | singletonFactories | 缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的 |
解决方案:
使用@Lazy
进行懒加载,什么时候需要对象在进行bean对象的创建
SpringMVC的执行流程
- 用户发送出请求到前端控制器
DispatcherServlet
DispatcherServlet
收到请求调用HandlerMapping
处理器映射器HandlerMapping
找到具体的处理器,生成处理器对象及处理器拦截器,在一起返回给DispatcherServlet
DispatcherServlet
调用HandlerAdpater
(处理器适配器)HandlerAdpater
经过适配调用具体的处理器(Handler/Controller)- Controller执行完成返回
ModelAndView
对象 HandlerAdpater
将 Controller执行结果MondelAndView返回给DispatcherServlet
DispatcherServlet
将ModelAndView传给 ViewReslover(视图解析器)- ViewReslover 解析后返回具体的试图
DispatcherServlet
根据view进行渲染视图DispatcherServlet
响应用户
SpringBoot自动配置原理
- @SpringBootConfiguration :该注解与@Configuration 注解作用相同,用来声明当前也是一个配置类
- @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包
- @EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解
在@EnableAutoConfiguration
注解内部,通过@Import
注解导入了一个类,这个类会去META-INF
目录下找到spring.factories
文件中定义的全类名配置类,在各种自动配置类中通过@ConditionalOnXXX
去进行约束实现自动配置的功能。
完整回答:
1.在SpringBoot项目中的引导类上有一个注解@SpringBootApplicaiton,这个注解是对三个注解进行了封装,分别是:
1.@SpringBootConfiguration
2.@ComponentScan
3.@EnableAutoConfiguration
2.其中@EnableAutoConfiguration是实现自动化配置的核心注解,通过`@Import`注解导入了一个类,
这个类会去`META-INF`目录下找到`spring.factories`文件中定义的全类名配置类,
在各种自动配置类中通过`@ConditionalOnXXX`去进行约束实现自动配置的功能。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步