6.Spring、SpringMVC、SpringBoot
1.Spring
1.1 讲讲Spring加载流程(注册(将类注册到Spring容器中)、实例化(将注册到BeanDefinition的类实例化一个Bean)、初始化(给实例化的Bean赋值))。
-
启动Spring容器:通过
ClassPathXmlApplicationContext
、FileSystemXmlApplicationContext
或AnnotationConfigApplicationContext
等类创建Spring IoC(控制反转)容器实例,这些类通常在应用程序主入口或者Servlet初始化时被调用。 -
定位配置元数据:定义Bean以及依赖关系的配置信息
-
加载和解析配置元数据:将Xml文件中的Bean和注册Bean注解标注的类转化为BeanDefinition(类信息、作用域、属性、依赖关系),注册到BeanDefinitionRegistry中
-
BeanFactoryPostProcessor:在Bean定义被完全加载并注册后,Spring容器执行所有实现了
BeanFactoryPostProcessor
接口的类,这些处理器可以修改已经注册但尚未实例化的Bean的定义属性。 -
Bean的实例化与依赖注入:
- Spring容器根据BeanDefinition开始实例化Bean,并执行依赖注入(DI)。这个过程包括: 分析Bean的依赖关系,通过
- 。
- 对于singleton作用域的Bean,仅实例化一次,并缓存以供后续请求重复使用。
- 对于prototype作用域的Bean,每次请求都会创建一个新的实例。
-
BeanPostProcessor:
- 在每个Bean实例化之后但在初始化之前以及初始化之后但返回给客户端代码之前,Spring容器会调用实现了
BeanPostProcessor
接口的类对Bean进行预处理和后处理操作。
- 在每个Bean实例化之后但在初始化之前以及初始化之后但返回给客户端代码之前,Spring容器会调用实现了
-
生命周期回调方法:
- 实现了
InitializingBean
接口或声明了@PostConstruct
注解的方法会在Bean实例化并完成依赖注入后自动调用,用于初始化Bean。 - 实现了
DisposableBean
接口或声明了@PreDestroy
注解的方法则在容器关闭时调用,用于资源清理工作。
- 实现了
-
容器启动完成:
- 当所有的初始化工作完成后,Spring容器即处于就绪状态,可以响应客户端对Bean的请求。
1.2 Spring AOP的实现原理。
Spring AOP即面向切面编程,它是基于动态代理的。Spring AOP基于原对象生成一个代理对象,在目标方法前后插入额外的功能,即切面。SpringAOP实现主要包括切面(一个或多个切点定义和相应的通知,即需要增强功能的具体实现类)、切点(定义通知的位置,即哪些方法需要增强)、通知(在某个连接点执行的行为,增强的功能)、连接点(程序执行中明确的点,即目标执行方法)。
1.3 讲讲Spring事务的传播属性。
Spring事务的传播属性定义了在已有事务环境下执行一个方法时,新开启事务的方式和当前事务之间的关系。Spring框架提供了七种不同的事务传播行为:
-
PROPAGATION_REQUIRED: 这是最常见的事务传播设置。如果当前没有事务,那么就新建一个事务;如果已经存在一个事务,则加入到这个事务中。这是默认的事务传播行为。
-
PROPAGATION_SUPPORTS: 如果当前存在事务,则方法将在该事务内运行;如果当前没有事务,则以非事务方式运行该方法。即支持当前事务的存在,但并不强制创建新的事务。
-
PROPAGATION_MANDATORY: 必须在一个已存在的事务内部执行。如果当前没有事务,会抛出异常。这种方式确保了业务方法必须在事务环境中执行。
-
PROPAGATION_REQUIRES_NEW: 总是新建一个新的事务,并且当前事务(如果有)会被挂起。这意味着即使在嵌套调用中,新方法也会拥有自己的独立事务,它与外部事务互不影响。无论外部方法是否回滚,新事务都会被提交或回滚。
-
PROPAGATION_NOT_SUPPORTED: 执行方法时不支持事务环境,如果当前存在事务,会暂停该事务直到方法执行完毕。即总是以非事务方式执行,即便有父事务存在。
-
PROPAGATION_NEVER: 严格禁止在事务上下文中执行,如果当前存在事务,则会抛出异常。这种方法必须在无事务的环境中执行。
-
PROPAGATION_NESTED(仅对部分数据库事务管理器有效): 在现有事务中执行一个嵌套事务,嵌套事务可以独立于外部事务进行提交或回滚,而对外部事务的影响是:如果嵌套事务正常完成(提交),其效果将反映到外部事务中;但如果嵌套事务回滚,外部事务仍可继续进行。
1.4 Spring如何管理事务的。
Spring提供了声明式事务,通过AOP技术实现对事务的管理和控制。以下是Spring管理事务的主要步骤和原理:
1.配置事务管理器:
Spring提供了多种PlatformTransactionManager实现类,如DataSourceTransactionManager、HibernateTransactionManager等,用于与具体的持久化技术进行交互并管理事务。
2.使用@Transactional注解:
在Service层或DAO层的方法上添加@Transactional
注解,以指示该方法需要在事务中执行。可以指定事务传播行为、隔离级别、只读属性和回滚规则等属性。
3.事务拦截器:
- Spring通过一个实现了MethodInterceptor接口的TransactionInterceptor类来处理带有@Transactional注解的方法调用。
- 当调用被@Transactional注解标记的方法时,Spring AOP会织入TransactionInterceptor,并在方法前后分别执行开启事务、提交/回滚事务的操作。
4.事务生命周期管理:
- 开启事务:当调用方法前,根据@Transactional注解的配置创建新的事务或者加入到当前已存在的事务中。
- 提交事务:如果方法执行过程中没有抛出异常,则在方法结束时提交事务。
- 回滚事务:如果方法执行过程中抛出了未捕获的异常,并且符合@Transactional注解的回滚规则(默认情况下是运行时异常和Error),则事务会被回滚。
5.事务传播行为:
通过设置@Transactional注解的propagation属性,可以定义事务如何在嵌套方法调用之间传播。例如PROPAGATION_REQUIRED表示无论是否存在当前事务都应参与事务;PROPAGATION_REQUIRES_NEW表示总是新建一个事务,并挂起当前事务。
2.5 Spring怎么配置事务(具体说出一些关键的xml 元素)。
- 配置事务管理器:在Xml中定义一个实现了PlatformTransactionManager实现类的bean作为事务管理器
- 导入tx命名空间并启动事务代理:开启事务注解驱动
- 配置事务属性:配置通知、传播级别、隔离级别,回滚机制
- 切面编程配置
1.6 说说你对Spring的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop的实现原理,说说aop中的几个术语,它们是怎么相互工作的。
非单例注入原理:
在Spring中,默认情况下大部分bean是单例的,即在整个应用上下文中只有一个实例。然而,如果需要为每次请求创建一个新的bean实例,可以通过@Scope("prototype")
注解或者在XML配置中指定bean的作用域为prototype
来实现。当一个原型作用域的bean被请求时,Spring容器会每次都生成一个新的bean实例,并且执行相应的初始化逻辑和依赖注入过程。
Bean的生命周期:
- 实例化:通过构造器创建Bean的实例。
- 设置属性值/依赖注入:Spring根据Bean定义信息填充其属性值。
- 初始化:如果Bean实现了InitializingBean接口或定义了init-method,会在所有必需的属性设置完成后调用初始化方法。
- 使用:Bean准备就绪后,可以由客户端代码或其它bean通过容器获取并使用。
- 销毁:在容器关闭或Bean不再需要时,如果Bean实现了DisposableBean接口或定义了destroy-method,则会调用销毁方法进行资源清理。
循环注入原理: 循环依赖是指两个或多个bean之间相互依赖形成闭环。Spring通过三级缓存机制处理循环依赖问题,对于构造器注入不支持循环依赖,但对于setter注入可通过“提前暴露引用”的方式解决。具体来说,在Spring容器完成bean实例化但尚未进行属性注入阶段,将已经实例化的bean放入到一个专门用于处理循环依赖的早期曝光缓存中,这样在后续注入属性时就可以从这个缓存中拿到依赖项。
AOP实现原理: Spring AOP主要基于动态代理(JDK Proxy 或 CGLIB)来实现。其原理如下:
- 切点(Pointcut):定义了哪些连接点(如方法调用)会被切面增强。这通常通过正则表达式或自定义注解等方式来标识。
- 通知(Advice):包含了切面的具体功能,分为前置通知(Before)、后置通知(AfterReturning)、环绕通知(Around)、异常抛出通知(AfterThrowing)和最终通知(After)等五种类型。
- 切面(Aspect):由切点和通知组成,描述了横切关注点的功能模块。
- 织入(Weaving):将切面应用到目标对象的过程。在运行时,Spring通过代理模式在调用目标方法前插入相关的通知逻辑,从而实现了对业务逻辑的交叉关注点的分离。
1.7 如何解决循环依赖,Spring的三级缓存如何解决循环依赖,为什么二级缓存不能解决?
Spring中三大循环依赖场景:
- 构造器注入循环依赖
- filed属性注入(setter方法注入)循环依赖
- prototype属性注入循环依赖
原因:
- 构造器构成的循环依赖无法解决,只能抛出。原因:Spring解决循环依赖是利用Bean的“中间态(已实例化但未初始化)”,而构造器负责的是实例化,故无法解决构造器构成的循环依赖。
- filed字段注入是最常见的依赖注入,不存在循环依赖问题。
- 非单例Bean默认不会初始化,而是使用时才会初始化。手动 getBean() 或者在一个单例 Bean 内 @Autowired A 或 B 就会出错。
-
一级缓存(singletonObjects):当一个单例bean的完整实例化过程完成(即构造函数调用和所有属性注入完毕),该实例会被放入一级缓存中。
-
二级缓存(earlySingletonObjects):在处理循环依赖时,Spring首先尝试从一级缓存获取bean,如果未找到,则会继续查找二级缓存。二级缓存存放的是已实例化但尚未进行属性注入或初始化的“半成品”bean对象引用。如果存在循环依赖,A需要B,而B又需要A,那么在创建B的过程中,Spring会将已实例化但未注入属性的A放入二级缓存中,以便B可以获取到A的引用并继续其自身的初始化。
-
三级缓存(singletonFactories):如果在二级缓存中依然没有找到bean,此时就涉及到三级缓存了。三级缓存存储的是能产生对应bean实例的对象工厂(ObjectFactory)。当Spring检测到循环依赖时,它会在实例化A的过程中记录下能够生产A的工厂,并将其放入三级缓存。然后在创建B时,发现对A的依赖,通过三级缓存中的工厂直接生成一个未完全初始化的A实例,再放入二级缓存供B使用。这样,即使在初始化过程中也能打破循环依赖。
为何二级缓存不能单独解决循环依赖: 二级缓存虽然能够存储部分初始化的bean引用,但它无法解决初次实例化bean时的循环依赖问题。例如,在A和B同时初始化且相互依赖的情况下,如果先初始化A,在创建B之前A还未能被放入二级缓存中,因为A的初始化过程还在等待B的注入,这就形成了死锁状态。而三级缓存的存在则可以在A实例化阶段,通过存储一个能够立即生产A实例的工厂来突破这一瓶颈,使得即使在A还未完成初始化时,也能向B提供其所需的部分初始化引用,从而成功解决循环依赖问题。
Spring中的“三级缓存”_spring三级缓存-CSDN博客
1.8 事务失效
事务失效是指在事务执行过程中,未能达到预期的ACID(原子性、一致性、隔离性、持久性)属性的情况。以下是几种常见的事务失效情况及其解决方法:
-
数据不一致:由于并发控制不当或其他原因导致事务操作后数据库状态不符合业务规则。解决方法是使用恰当的事务隔离级别和并发控制机制(如锁机制),以及在事务中确保所有的业务逻辑都能保证数据的一致性。
-
死锁:两个或多个事务互相等待对方释放资源,造成循环等待,无法继续执行。解决方法包括设置合理的事务超时时间、采用乐观锁或者悲观锁策略时避免长时间持有不释放,以及通过检测并解除死锁的机制来避免长期僵持。
-
部分提交/回滚:在网络问题、系统崩溃等异常情况下,事务可能只完成了一部分操作就中断了。为了解决这个问题,可以采用分布式事务管理技术(如两阶段提交、三阶段提交、补偿事务、Saga模式等),确保事务要么全部成功,要么全部失败。
-
脏读、不可重复读、幻读:这是由于不同的事务隔离级别造成的读取数据不准确的问题。针对这些现象,可以通过提高事务隔离级别来解决,例如:
- 脏读:使用“可重复读”(Repeatable Read)或“串行化”(Serializable)隔离级别。
- 不可重复读:使用“串行化”隔离级别,或者在某些数据库系统中,“快照隔离”也可以避免不可重复读。
- 幻读:通常需要使用“串行化”隔离级别才能完全防止。
-
系统故障恢复:当系统发生故障时,可能会丢失未提交的事务修改或者已经提交的事务结果没有持久化到磁盘上。解决方法是利用数据库系统的日志(redo log、undo log)进行事务的前滚和回滚,实现故障恢复。
2.SpringMVC
2.1 Springmvc 中DispatcherServlet初始化过程。
-
加载和实例化: 当Web应用启动时,容器(如Tomcat、Jetty等)会根据
web.xml
或Java配置方式中定义的Servlet信息来创建DispatcherServlet
实例。通常在Servlet规范下,容器会调用Servlet的默认构造器创建对象,并调用其生命周期方法之一——init()
。 -
初始化父类HttpServletBean:
DispatcherServlet
继承自HttpServletBean
,因此它的初始化流程首先经过父类的初始化。在HttpServletBean
的init()
方法中,会调用initServletBean()
方法来进行Spring Bean风格的初始化。该方法是在FrameworkServlet
中实现的,DispatcherServlet
是FrameworkServlet
的一个子类。 -
初始化WebApplicationContext: 在
DispatcherServlet
的initServletBean()
方法内部,它会创建或获取一个WebApplicationContext
(Web应用上下文)。WebApplicationContext
是Spring ApplicationContext的一个特殊变体,专门设计用于Web应用程序,可以与ServletContext关联起来。如果通过XML或者Java配置指定了上下文配置文件的位置,那么此时就会加载并解析这些配置文件,构建出完整的Spring Bean容器。 -
载入配置: SpringMVC框架会读取与
DispatcherServlet
关联的Spring配置信息,包括<mvc:annotation-driven>
、<context:component-scan>
、HandlerMapping、HandlerAdapter等配置,将它们注册到WebApplicationContext中。 -
初始化处理器映射器和适配器: 初始化过程中,Spring MVC会自动装配或根据配置加载各种处理器映射器(如RequestMappingHandlerMapping)和处理器适配器(如RequestMappingHandlerAdapter),它们负责将HTTP请求与Controller中的处理方法进行匹配和执行。
-
初始化拦截器链: 如果有配置的拦截器(Interceptor),则会在初始化阶段被添加到一个全局的拦截器链中,以便在请求处理的过程中被执行。
-
完成初始化: 所有的初始化工作完成后,
DispatcherServlet
就处于准备接收并处理HTTP请求的状态。
总结来说,DispatcherServlet
的初始化是一个综合了Servlet生命周期管理和Spring IoC容器初始化的过程,最终形成一个能够处理HTTP请求并将其路由至相应控制器进行处理的前端控制器。
2.2 springmvc用到的注解,作用是什么,原理。
-
@RequestMapping
- 作用:用于映射HTTP请求到控制器的方法上。它可以放在类级别上,表示该类中所有处理方法都将共享相同的请求映射规则;也可以放在方法级别上,精确指定处理某个HTTP请求的方法。
- 原理:Spring MVC的DispatcherServlet会根据@RequestMapping注解的属性(如value、method、params、headers等)来决定哪个Controller的方法应该处理接收到的请求。
-
@GetMapping, @PostMapping, @PutMapping, @DeleteMapping
- 作用:这些注解是@RequestMapping的特殊变体,分别用于映射GET、POST、PUT、DELETE等不同HTTP方法的请求。它们简化了对HTTP方法的指定。
- 原理:与@RequestMapping一样,根据HTTP方法筛选请求,并映射到相应的处理方法。
-
@RequestParam
- 作用:用于将请求参数绑定到控制器方法的形参上,可以从请求的查询参数或POST表单数据中获取值。
- 原理:Spring MVC在解析请求时,会自动查找@RequestParam注解的参数,并从请求中提取对应的值。
-
@PathVariable
- 作用:用于映射URL模板中的占位符到控制器方法的形参上,常用于RESTful风格的路由。
- 原理:在解析请求URL时,根据URL模板中的占位符名称与@PathVariable注解的参数名称相匹配,将实际路径参数值传递给方法参数。
-
@RequestBody
- 作用:用于将HTTP请求体的内容转换并绑定到控制器方法的形参上,主要用于处理JSON、XML等非表单数据的POST、PUT请求。
- 原理:通过HttpMessageConverter接口实现将请求体内容转换为Java对象的过程。
-
@ResponseBody
- 作用:用于将方法返回的对象转化为HTTP响应体的内容,通常配合@ControllerAdvice、@RestController等使用,实现REST API的数据返回。
- 原理:通过HttpMessageConverter接口将方法返回的对象序列化为客户端可识别的格式(如JSON、XML等),然后写入到HTTP响应体。
-
@ControllerAdvice
- 作用:全局异常处理和统一响应处理,可以定义一个类,其中的方法可以处理所有Controller抛出的异常或对所有Controller的响应进行统一格式化处理。
- 原理:Spring MVC框架在执行过程中,如果捕获到符合@ControllerAdvice注解的类中定义的异常处理方法签名匹配的异常,便会调用该方法进行处理。
-
@ModelAttribute
- 作用:用于表示一个方法或方法参数应该从模型中获取,或者表示一个方法应该往模型中添加一个对象,通常用于处理表单数据和模型数据的绑定。
- 原理:在请求处理过程中,Spring MVC会自动调用标有@ModelAttribute的方法,将返回的对象添加到模型中,然后在后续的处理方法中可通过该注解的参数名称从模型中获取数据。
这些注解的处理是在Spring MVC的DispatcherServlet处理请求的过程中完成的,涉及到了一系列的处理器映射、数据绑定、视图渲染等中间环节。Spring MVC通过一系列的拦截器链和策略模式实现了这些注解的功能。
3.SpringBoot
3.1 springboot启动机制。
SpringBoot的启动机制基于Java内置的Servlet容器(如Tomcat、jetty、Undertow),并通过Spring Framework和自身的自动配置简化Web应用的开发。
1.启动类:
Spring Boot 应用通常会包含一个带有 @SpringBootApplication
注解的主类,这个注解是复合注解,包含了 @SpringBootConfiguration
(相当于 @Configuration
,表示类是配置类)、@EnableAutoConfiguration
(开启自动配置)和 @ComponentScan
(组件扫描)注解。
2.引导加载:
当运行主类的 main
方法时,Spring Boot 会启动一个新的 JVM 进程。SpringApplication.run(Application.class, args)
方法被调用,该方法负责初始化 Spring 上下文并启动应用。
3.创建上下文:
- Spring Boot 初始化
SpringApplication
类并创建一个ApplicationContext
(应用上下文)实例,它是 Spring IoC 容器的入口点。 SpringApplication
会根据类路径下的 META-INF/spring.factories 文件中定义的SpringApplicationRunListeners
来监听应用启动过程中的事件。
4.自动配置:
Spring Boot 通过 EnableAutoConfiguration
注解扫描类路径下的jar包,根据jar包中的 META-INF/spring.factories
文件中的 EnableAutoConfiguration
条目来自动配置 Bean。自动配置的目的是基于应用类路径中的类和 jar 包,推断出应用可能需要的 Bean,并自动创建它们。
5.组件扫描:
@ComponentScan
注解用于扫描包含在主类包及其子包下的所有标记了 @Component
、@Service
、@Repository
、@Controller
或 @Configuration
的类,并将它们作为 Bean 加载到 Spring 容器中
6.加载额外配置:
Spring Boot 会加载 application.properties 或 application.yml 中的配置信息,并将它们绑定到 Spring 环境(Environment
)中,供应用使用。
7.启动嵌入式容器:
Spring Boot 会加载 application.properties 或 application.yml 中的配置信息,并将它们绑定到 Spring 环境(Environment
)中,供应用使用
8.启动应用:
当所有 Bean 都被加载和初始化完成后,Spring Boot 应用就准备好了,嵌入式容器开始监听 HTTP 请求,等待客户端连接