Spring面试题

发布时间 2024-01-09 15:50:27作者: 自学Java笔记本

Spring框架中的单例bean是线程安全的吗?

Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装。所以单例bean是线程不安全的。
如果Bean是有状态的(有状态即有数据存储功能),则需要我们自己来保证线程安全。
最简单的方法即改变Bean作用域,将单例变为原型,即singleton改为protopyte,这样每次请求的Bean都是重新new一个Bean。
或用ThreadLocal来包装变量,将变量变为线程私有的。
或使用synchronized修饰变量来实现线程同步。

image
此时这个成员变量是有状态的,我们就需要通过上诉方式使得线程安全,对于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的生命周期

image

  • 实例化(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的循环依赖?

最简单的例子就是:两个对象之间需要的属性相互依赖
image

大致流程如下:
image

根据Bean的生命周期推算,当初始化A时,看到需要一个B属性,此时容器中没有,就去实例化B,但是此时A是没有完整的new出来,即不在ioc容器中,当初始化B时,发现A没有,此时又去实例化,所以产生的循环依赖问题。

Spring解决循环依赖是通过三级缓存,对应的三级缓存如下所示:
image

缓存名称 源码名称 作用
一级缓存 singletonObjects 单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象
二级缓存 earlySingletonObjects 缓存早起的bean对象(生命周期还没走完)
三级缓存 singletonFactories 缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的

解决方案:
使用@Lazy进行懒加载,什么时候需要对象在进行bean对象的创建

SpringMVC的执行流程

image

  • 用户发送出请求到前端控制器DispatcherServlet
  • DispatcherServlet收到请求调用HandlerMapping处理器映射器
  • HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器,在一起返回给DispatcherServlet
  • DispatcherServlet调用HandlerAdpater(处理器适配器)
  • HandlerAdpater经过适配调用具体的处理器(Handler/Controller)
  • Controller执行完成返回ModelAndView对象
  • HandlerAdpater将 Controller执行结果MondelAndView返回给DispatcherServlet
  • DispatcherServlet将ModelAndView传给 ViewReslover(视图解析器)
  • ViewReslover 解析后返回具体的试图
  • DispatcherServlet根据view进行渲染视图
  • DispatcherServlet响应用户

SpringBoot自动配置原理

image

  • @SpringBootConfiguration :该注解与@Configuration 注解作用相同,用来声明当前也是一个配置类
  • @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包
  • @EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解

image
@EnableAutoConfiguration注解内部,通过@Import注解导入了一个类,这个类会去META-INF目录下找到spring.factories文件中定义的全类名配置类,在各种自动配置类中通过@ConditionalOnXXX去进行约束实现自动配置的功能。

完整回答:

1.在SpringBoot项目中的引导类上有一个注解@SpringBootApplicaiton,这个注解是对三个注解进行了封装,分别是:
1.@SpringBootConfiguration
2.@ComponentScan
3.@EnableAutoConfiguration

2.其中@EnableAutoConfiguration是实现自动化配置的核心注解,通过`@Import`注解导入了一个类,
这个类会去`META-INF`目录下找到`spring.factories`文件中定义的全类名配置类,
在各种自动配置类中通过`@ConditionalOnXXX`去进行约束实现自动配置的功能。