3.框架
3.1 简单讲讲tomcat结构,以及其类加载器流程,线程模型等。
Apache Tomcat是一个开源的Servlet和JavaServer Pages(JSP)容器,用于部署和运行Java Web应用程序。其主要结构和组件包括:
-
Tomcat结构:
- Catalina:Catalina是Tomcat的核心引擎,负责处理请求与响应,并管理整个生命周期中的Web应用。
- Connector:连接器是Tomcat服务器与客户端之间的通信接口,它监听特定端口上的HTTP请求,并将请求传递给Catalina进行处理。常见的连接器有BIO、NIO和APR等。
- Container:容器层级结构包括Engine、Host、Context和Wrapper,它们共同构成了一个层次化的管理结构,分别对应服务级别、虚拟主机级别、Web应用级别以及Servlet级别。
Engine:顶级容器,代表整个Catalina Servlet引擎。Host:每个Engine下可以包含多个Host,代表服务器上的一个虚拟主机或IP地址+端口组合。Context:每个Host下可以有多个Context,代表一个Web应用程序上下文,即通常所说的部署在服务器上的WAR文件。Wrapper:最底层容器,代表了在Web应用程序中被加载的一个Servlet。
-
类加载器流程:
- Tomcat遵循Java标准的类加载器架构,并在此基础上进行了扩展以支持Web应用的隔离性。
- 类加载器主要有以下几类:
- Bootstrap ClassLoader:由JVM提供,加载核心Java库。
- System ClassLoader 或 Common ClassLoader:加载Tomcat自身的库和配置文件。
- Catalina ClassLoader:加载Tomcat服务器内部使用的类。
- Shared ClassLoader:共享类加载器,用于加载所有Web应用都可以访问的通用类库。
- WebApp ClassLoader(也称为Application ClassLoader):为每个Web应用创建独立的类加载器实例,加载各自WAR包中的类,从而实现不同Web应用间类的隔离。
-
线程模型:
- Tomcat使用多线程来并发处理客户端请求。
- 对于处理请求的线程池,Tomcat可以根据不同的连接器配置不同的线程模型,如:
- BIO(Blocking I/O):每个请求分配一个新的线程,适用于并发请求较少的情况。
- NIO(Non-blocking I/O):通过Selector复用少量线程处理大量连接,适合高并发场景。
- APR (Apache Portable Runtime):使用操作系统级别的API优化网络I/O性能,同样支持异步非阻塞模式。
3.2 tomcat如何调优,涉及哪些参数 。
Apache Tomcat调优是一个涉及多个方面的过程,主要是为了提高服务器性能、减少响应时间以及优化资源使用。以下是一些关键的Tomcat调优参数和策略:
-
内存配置(JVM参数):
-Xms:设置JVM初始堆内存大小。-Xmx:设置JVM最大堆内存大小。-XX:MaxMetaspaceSize(Java 8及更高版本):设置元空间的最大大小,用于存储类元数据等。-Xmn或-XX:NewRatio:调整年轻代与老年代的大小比例。
-
垃圾回收器选择与参数调整:
-XX:+UseG1GC或-XX:+UseConcMarkSweepGC等:选择适合应用特性的垃圾收集器。-XX:MaxTenuringThreshold:控制对象晋升到老年代的年龄阈值。
-
连接器优化:
maxThreads:在server.xml中配置Connector组件,设置最大并发处理线程数。acceptCount:当所有请求处理线程都忙碌时,可以排队等待的最大请求数量。connectionTimeout:设置客户端连接超时时间。enableLookups:禁用DNS查找以加快处理速度(通常设为false)。- 启用压缩 (
compression和compressableMimeType):对特定类型的HTTP响应进行压缩,降低网络传输负载。
-
静态资源缓存:
- 在
server.xml中的Context元素中配置resources标签,并启用静态资源缓存。
- 在
-
Tomcat内部机制优化:
- 考虑是否使用
apr连接器来提升I/O性能。 - 根据实际情况设置线程池相关参数,如
minSpareThreads、maxSpareThreads等。
- 考虑是否使用
-
类加载器优化:
- 控制Web应用程序之间类库共享,合理配置
commonLoader、sharedLoader和webappLoader。
- 控制Web应用程序之间类库共享,合理配置
-
日志输出级别:
- 适当调整日志级别,避免过多的日志输出消耗系统资源。
-
SSL/TLS配置优化:
- 使用更高效的加密算法,减少握手阶段的延迟。
- 配置会话复用,提高HTTPS连接性能。
-
性能监控与分析工具:
- 利用JMX监控Tomcat性能指标。
- 使用JConsole、VisualVM或专门的APM工具(如New Relic、Dynatrace)监控和分析应用性能。
3.3 讲讲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的请求。
3.4 Spring AOP的实现原理。
Spring AOP即面向切面编程,它是基于动态代理的。Spring AOP基于原对象生成一个代理对象,在目标方法前后插入额外的功能,即切面。SpringAOP实现主要包括切面(一个或多个切点定义和相应的通知,即需要增强功能的具体实现类)、切点(定义通知的位置,即哪些方法需要增强)、通知(在某个连接点执行的行为,增强的功能)、连接点(程序执行中明确的点,即目标执行方法)。
3.5 讲讲Spring事务的传播属性。
Spring事务的传播属性定义了在已有事务环境下执行一个方法时,新开启事务的方式和当前事务之间的关系。Spring框架提供了七种不同的事务传播行为:
-
PROPAGATION_REQUIRED: 这是最常见的事务传播设置。如果当前没有事务,那么就新建一个事务;如果已经存在一个事务,则加入到这个事务中。这是默认的事务传播行为。
-
PROPAGATION_SUPPORTS: 如果当前存在事务,则方法将在该事务内运行;如果当前没有事务,则以非事务方式运行该方法。即支持当前事务的存在,但并不强制创建新的事务。
-
PROPAGATION_MANDATORY: 必须在一个已存在的事务内部执行。如果当前没有事务,会抛出异常。这种方式确保了业务方法必须在事务环境中执行。
-
PROPAGATION_REQUIRES_NEW: 总是新建一个新的事务,并且当前事务(如果有)会被挂起。这意味着即使在嵌套调用中,新方法也会拥有自己的独立事务,它与外部事务互不影响。无论外部方法是否回滚,新事务都会被提交或回滚。
-
PROPAGATION_NOT_SUPPORTED: 执行方法时不支持事务环境,如果当前存在事务,会暂停该事务直到方法执行完毕。即总是以非事务方式执行,即便有父事务存在。
-
PROPAGATION_NEVER: 严格禁止在事务上下文中执行,如果当前存在事务,则会抛出异常。这种方法必须在无事务的环境中执行。
-
PROPAGATION_NESTED(仅对部分数据库事务管理器有效): 在现有事务中执行一个嵌套事务,嵌套事务可以独立于外部事务进行提交或回滚,而对外部事务的影响是:如果嵌套事务正常完成(提交),其效果将反映到外部事务中;但如果嵌套事务回滚,外部事务仍可继续进行。
3.6 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表示总是新建一个事务,并挂起当前事务。
3.7 Spring怎么配置事务(具体说出一些关键的xml 元素)。
- 配置事务管理器:在Xml中定义一个实现了PlatformTransactionManager实现类的bean作为事务管理器
- 导入tx命名空间并启动事务代理:开启事务注解驱动
- 配置事务属性:配置通知、传播级别、隔离级别,回滚机制
- 切面编程配置
3.8 说说你对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通过代理模式在调用目标方法前插入相关的通知逻辑,从而实现了对业务逻辑的交叉关注点的分离。
3.9 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请求并将其路由至相应控制器进行处理的前端控制器。
3.10 netty的线程模型,netty如何基于reactor模型上实现的。
Netty 是一个高性能的异步事件驱动网络应用框架,其线程模型基于Reactor模式实现。Reactor模式是一种处理大量并发I/O请求的设计模式,通过一个或多个反应器(Reactor)来监听和分发事件给相应的处理器进行处理。
在Netty中,Reactor线程模型主要有以下几种形式:
-
单线程Reactor模型:
- 仅使用一个NIO线程作为Selector,负责监听所有Channel上的事件。
- 当Channel上有新的连接请求、读写事件时,该线程会负责接收并调度到对应的Handler进行处理。
- 这种模型适用于CPU密集型任务且并发量不高的场景,因为所有事件处理都在同一个线程内完成。
-
多线程Reactor模型(主从Reactor或多线程EventLoopGroup):
- 使用一个Acceptor线程池(通常只有一个线程)监听新的客户端连接请求。
- 当有新的连接到来时,由Acceptor将新建立的SocketChannel注册到另一个I/O线程池中的某个工作线程上,这个线程(即EventLoop)负责后续的读写事件处理。
- 每个EventLoop有自己的Selector,并与一组Channel关联,负责这些Channel的所有I/O事件。
- 这种模型可以充分利用多核CPU资源,提高系统的并行处理能力。
-
主从Reactor多线程模型:
- 主Reactor用于接受新的连接请求,然后将其分配给从Reactor(也就是I/O线程池)。
- 从Reactor负责各自关联的Channel的I/O事件处理,每个从Reactor线程拥有自己的Selector,并在其上进行轮询和事件处理。
- 在高并发场景下,主从Reactor模型能够有效避免单一Reactor成为性能瓶颈。
3.11 为什么选择netty。
-
高性能与高并发: Netty基于NIO技术,采用了事件驱动和异步编程模型,能有效地利用系统资源,支持高并发连接。通过内存池化、零拷贝技术等优化手段,极大地提高了吞吐量和降低了延迟。
-
线程模型优化: Netty提供了灵活且高效的线程模型,如Reactor模式,可以避免频繁的线程上下文切换带来的开销,保证了系统的可伸缩性和稳定性。
-
易于使用和扩展: Netty提供了丰富的API和高度可定制化的组件,开发者可以方便地搭建和扩展网络应用,如Web服务器、游戏服务器、RPC框架等。同时,Netty对各种协议(如HTTP、FTP、WebSocket等)都有很好的支持。
-
健壮性与稳定性: Netty在网络异常处理、流量控制、超时重传等方面做了精心设计,保证了网络通信的健壮性和稳定性。同时,经过大量生产环境的检验,Netty在长期运行下的表现也非常稳定可靠。
-
社区活跃与持续更新: Netty背后有一个活跃的开源社区,不断对其功能进行改进和完善,及时修复bug,同时紧跟行业发展趋势,支持新的协议和特性,保障了其与时俱进的能力。
3.12 什么是TCP粘包,拆包。解决方式是什么。
TCP是一种面向连接、可靠的基于字节流的传输层协议。但在实际应用中,TCP为了提高传输效率,并不保证每个发送的数据包都能够单独作为一个完整的单位送达接收方,这就可能出现“TCP粘包”和“TCP拆包”现象。
-
TCP粘包(Packet Coalescing): 当发送端连续发送多个小的数据包时,TCP有可能会合并这些数据包成为一个较大的数据块进行传输,接收端一次性接收多个数据包的内容,无法区分原本的单个数据包边界。这是因为TCP是以字节流的方式传输数据,而不保证应用层的消息边界。
-
TCP拆包(Packet Fragmentation): 另一方面,如果一个大数据包在网络传输过程中因MTU(Maximum Transmission Unit)限制或其他网络因素被分割成了多个较小的数据包,接收端可能会分多次接收到这些数据包,造成原始数据的拆分,这也称为拆包现象。
解决TCP粘包和拆包问题的方式主要有以下几种:应用层固定长度大小区分、每个数据包末尾特定结束符
-
协议头设计: 设计自定义的应用层协议,加入明确的数据包长度字段,使得接收端可以根据长度字段正确区分不同的数据包。
-
消息边界标识: 在每个数据包末尾添加特定的结束符(delimiter),如换行符、特殊字符序列等,接收端通过检测这些结束符来识别一个完整消息的结束。
-
固定长度消息: 如果应用层消息长度都是固定的,可以直接根据长度来判断每个消息的结束位置。
-
消息帧设计: 设计成帧(framing)协议,例如在每个消息前加上消息头,消息头包含消息长度或序列号等信息,这样接收端可以根据消息头来解析出每个独立的消息。
-
基于协议内容的拆包粘包处理: 在接收端程序中实现逻辑,根据预先约定的协议解析数据,当接收到一定数量的数据后,按照协议格式解析出完整的消息。
3.13 netty的通讯协议是什么样的。
netty 并不是一个具体的通讯协议,而是一个用于简化高性能网络编程的 Java 开源框架。它提供了一系列工具和API,使得开发者能够更容易地实现基于各种传输协议的应用程序,比如TCP、UDP、HTTP等等。Netty 支持自定义协议的设计和实现,同时也提供了对多种标准协议的支持。
在Netty中,开发者可以根据自己的业务需求设计和实现自定义的通讯协议。通常情况下,一个自定义的通讯协议会在Netty中表现为一系列的编码(Encoder)和解码(Decoder)器,用于处理网络传输的数据流,确保数据在发送端按照协议进行编码封装,并在接收端正确解码还原。
3.14 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.15 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 请求,等待客户端连接
3.16 如何解决循环依赖,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博客
3.17 事务失效
事务失效是指在事务执行过程中,未能达到预期的ACID(原子性、一致性、隔离性、持久性)属性的情况。以下是几种常见的事务失效情况及其解决方法:
-
数据不一致:由于并发控制不当或其他原因导致事务操作后数据库状态不符合业务规则。解决方法是使用恰当的事务隔离级别和并发控制机制(如锁机制),以及在事务中确保所有的业务逻辑都能保证数据的一致性。
-
死锁:两个或多个事务互相等待对方释放资源,造成循环等待,无法继续执行。解决方法包括设置合理的事务超时时间、采用乐观锁或者悲观锁策略时避免长时间持有不释放,以及通过检测并解除死锁的机制来避免长期僵持。
-
部分提交/回滚:在网络问题、系统崩溃等异常情况下,事务可能只完成了一部分操作就中断了。为了解决这个问题,可以采用分布式事务管理技术(如两阶段提交、三阶段提交、补偿事务、Saga模式等),确保事务要么全部成功,要么全部失败。
-
脏读、不可重复读、幻读:这是由于不同的事务隔离级别造成的读取数据不准确的问题。针对这些现象,可以通过提高事务隔离级别来解决,例如:
- 脏读:使用“可重复读”(Repeatable Read)或“串行化”(Serializable)隔离级别。
- 不可重复读:使用“串行化”隔离级别,或者在某些数据库系统中,“快照隔离”也可以避免不可重复读。
- 幻读:通常需要使用“串行化”隔离级别才能完全防止。
-
系统故障恢复:当系统发生故障时,可能会丢失未提交的事务修改或者已经提交的事务结果没有持久化到磁盘上。解决方法是利用数据库系统的日志(redo log、undo log)进行事务的前滚和回滚,实现故障恢复。