常用框架
阅读提示
建议先看题目目录,再按“概念 -> 原理 -> 场景 -> 优化”顺序复习。
每题先讲结论,再补关键机制和项目实践,回答会更稳。
Spring
1、SpringBean的作用域
参考回答: singleton:这是Spring框架的默认作用域。Spring IoC容器只会创建该Bean的唯一实例。所有对该Bean的请求都将返回同一个实例。这个Bean是全局共享的,适用于无状态的Bean或者需要在多个组件之间共享数据的情况。
prototype:每次对该作用域下的Bean的请求都会创建新的实例。获取Bean(即通过applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是新的对象实例。没有共享状态,适用于有状态的Bean或者需要频繁创建新实例的情况。
request:每次HTTP请求都会创建一个新的Bean实例。每个请求的Bean实例对于该请求是唯一的。这个作用域仅在基于Web的Spring ApplicationContext中有效。适用于处理HTTP请求的控制器或服务。
session:在一个HTTP会话(Session)中,定义一个Bean实例。对于同一用户的所有请求,都将使用相同的Bean实例。这个作用域也仅在基于Web的Spring ApplicationContext中有效,适用于保存用户特定的数据或状态。
global session:在整个应用程序的全局会话中创建一个新的Bean实例。这个作用域主要用于在Portlet环境中定义Bean,因为Portlet规范定义了全局会话的概念,而普通的Web应用(只包含Servlet)中并没有这个概念。在普通的Web应用中,global session作用域等同于session作用域。
2、SpringBean的生命周期
参考回答: 实例化:当Spring IoC容器接收到对某个Bean的请求时,IoC容器会先通过反射机制实例化该Bean。这个阶段完成了BeanFactoryPostProcessor的操作(如果有的话)。
属性赋值:Spring IoC容器在创建Bean的过程中,会将Bean在XML文件中配置的属性值和BeanDefinition中的属性值设置到Bean中。这个过程也称为依赖注入(DI),可以通过构造方法注入、Setter方法注入等方式完成。
Bean后处理器(BeanPostProcessor)处理:在Bean的实例化和属性赋值之后,Spring会调用BeanPostProcessor的postProcessBeforeInitialization方法,对Bean进行进一步的处理。这是一个可选的步骤,开发者可以通过实现BeanPostProcessor接口并覆盖相应的方法来定义自己的处理逻辑。
初始化:如果Bean在容器中配置了init-method,那么这个方法将会被调用,完成Bean的初始化操作。此外,如果Bean实现了InitializingBean接口,那么Spring会调用其afterPropertiesSet方法进行初始化。这也是一个可选的步骤,但通常用于在Bean的所有属性设置完毕后执行一些初始化操作。
实例化后处理:在Bean完成初始化之后,Spring会再次调用BeanPostProcessor的postProcessAfterInitialization方法,对Bean进行最后的处理。这也是一个可选的步骤,开发者可以在这里添加一些额外的逻辑,例如对Bean进行代理等。
使用:此时Bean已经准备就绪,可以被应用程序使用了。Spring容器会管理Bean的生命周期,并在需要的时候将其提供给应用程序。
销毁:当Spring容器关闭时,它会调用所有实现了DisposableBean接口的Bean的destroy方法,并触发在XML配置文件中定义的destroy-method。这是Bean生命周期的最后阶段,用于释放Bean占用的资源。
3、Spring AOP原理
参考回答: Spring AOP(Aspect Oriented Programming,面向切面编程)的原理主要基于动态代理模式,用于在不修改原有业务代码的前提下,为多个目标对象统一管理并增加额外功能。其核心概念包括切面(Aspect)、连接点(Join Point)、通知(Advice)等。
切面(Aspect):切面是织入到目标类中的功能代码,对于Java来说可以是方法、构造器或者类的特定点上的代码。切面可以包括前置通知、后置通知、环绕通知、异常通知和最终通知等,具体可以根据业务需求自定义实现。
连接点(Join Point):连接点表示切面将会被织入到目标对象的哪个位置,对于Java来说通常是某个方法的执行点或者某个构造器的创建点。
通知(Advice):通知包括各种类型的切面代码,如前置通知、后置通知、环绕通知、异常通知和最终通知等。这些通知会在连接点执行时被触发。
Spring AOP以动态代理技术为主要方式进行织入。具体来说,当目标对象的方法被调用时,Spring AOP会检查该方法是否是一个连接点。如果是,则会在方法执行前后(或者方法执行过程中)织入相应的通知代码。这些通知代码可以在不修改原有业务代码的情况下,为目标对象增加额外的功能。
4、Spring事务传播机制
参考回答: REQUIRED(默认):
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
SUPPORTS:
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
MANDATORY:
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
REQUIRES_NEW:
创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说,无论外部方法是否开启事务,REQUIRES_NEW 修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。
NOT_SUPPORTED:
以非事务方式运行,如果当前存在事务,则把当前事务挂起。
NEVER:
以非事务方式运行,如果当前存在事务,则抛出异常。
NESTED:
如果当前存在事务,则在嵌套事务内执行。嵌套事务是独立的事务,但它可以依赖于外部事务的提交或回滚。如果外部事务提交,则嵌套事务也会提交;如果外部事务回滚,则嵌套事务可以选择性地回滚(这取决于它自己的状态)。
5、什么是Spring的循环依赖问题?(了解)
参考回答: Spring的循环依赖问题是指在Spring容器中,两个或多个Bean之间相互依赖,形成了一个循环引用的情况。具体来说,当一个Bean A依赖于另一个Bean B,而Bean B又依赖于Bean A时,就可能出现循环依赖的问题。
6、什么是spring三级缓存以及如何解决循环依赖(重要)
参考回答: 一级缓存(singletonObjects):
也称为“单例池”,用于存储所有单例bean的实例对象。
当应用程序需要某个bean时,Spring首先会在一级缓存中查找,如果找到则直接返回该bean的实例。
二级缓存(earlySingletonObjects):
存放早期暴露出来的bean对象,即实例化以后但属性还未填充的bean对象。
二级缓存在解决循环依赖中只是整个三级缓存机制的一部分,单独依赖二级缓存无法完全解决循环依赖问题。当A bean依赖于B bean,而B bean又依赖于A bean时,Spring可以在创建A bean的过程中将其提前放入二级缓存,以供B bean在实例化后但未填充属性前引用。
三级缓存(singletonFactories):
也称为“单例工厂池”,用于存储用于创建单例bean的ObjectFactory。
在创建单例bean时,如果发现该bean依赖于其他的bean,则需要先创建该依赖的bean实例。此时,Spring会将用于创建该依赖的ObjectFactory保存到三级缓存中。当依赖的bean创建完成后,Spring会使用这个ObjectFactory来生成最终的bean实例,并将其放入一级缓存中。
通过三级缓存机制,Spring可以更加高效地管理bean的创建和引用过程,特别是在处理循环依赖和懒加载等场景时。此外,缓存机制还可以减少对数据库等持久层资源的访问次数,提高系统的资源利用率和稳定性。
详细内容请看:Srping 一级、spring二级、三级缓存是如何解决循环依赖的
6、spring事务失效的原因?
参考回答: 事务注解添加在非public方法上:Spring事务管理是基于AOP(面向切面编程)实现的,而AOP对于JDK动态代理或CGLib动态代理只会代理public方法。因此,如果@Transaction注解添加在非public方法上,事务将不会生效。
类没有被Spring管理:Spring事务是由AOP机制实现的,AOP机制的本质就是动态代理。从Spring IOC容器获取bean时,Spring会为目标类创建代理,从而支持事务。如果类没有被Spring容器管理(例如,没有添加@Service、@Component等注解),则Spring无法为目标类创建代理,事务也会失效。
异常被捕获且未正确处理:默认情况下,Spring只有在方法抛出运行时异常或者错误时才会回滚事务。如果事务方法内部抛出了异常,但该异常被catch语句捕获且没有重新抛出,或者捕获的异常类型不是运行时异常,那么事务将不会回滚。
事务属性设置不当:如果在Spring的事务管理中,事务的传播特性(propagation)或回滚规则(rollbackFor)设置不当,也可能导致事务失效。例如,如果在一个支持当前事务的方法中调用了需要新事务的方法,并且后者方法抛出了异常,但异常并未被Spring识别为需要回滚事务的异常,那么后者的事务将不会回滚。
数据库配置问题:如果数据库配置不支持事务(例如,使用了不支持事务的数据库引擎),或者数据库本身就是不支持事务的,那么Spring事务也会失效。
调用同一个类中的方法:在Spring中,如果一个事务方法内部调用了同一个类中的另一个方法,而这个方法没有被Spring代理(即没有被标记为@Transactional),那么事务将不会生效。这是因为Spring是通过代理模式来管理事务的,只有在代理对象上调用方法时,事务才会生效。
事务嵌套使用不当:在一个已经开启了事务的方法中再次开启事务,对于Spring的PROPAGATION_REQUIRED模式(默认模式),它只会用原来的事务,这可能导致预期之外的行为。
7、spring bean是否为线程安全的?
参考回答: Spring容器本身并没有提供Bean的线程安全策略,所以可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体情况还是要结合Bean的作用域来讨论。对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题。对于singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此是存在线程安全问题的。
8、spring中用到的设计模式?
参考回答: 工厂模式(Factory Pattern):
Spring使用工厂模式通过BeanFactory、ApplicationContext等接口创建并管理对象实例。这种方式将对象的创建与使用解耦,使得程序更加灵活和可扩展。
单例模式(Singleton Pattern):
Spring中的Bean默认都是单例的,即在整个Spring IoC容器中,每个Bean只会有一个实例。这通过Bean的scope属性进行控制,当scope为singleton时,即表示使用单例模式。
代理模式(Proxy Pattern):
在AOP(面向切面编程)和remoting中被广泛使用。Spring AOP的实现基于动态代理模式,可以在运行时动态地将代码切入到类的指定方法、指定位置上。
模板方法模式(Template Method Pattern):
解决代码重复问题。Spring中例如JdbcTemplate就是模板方法模式的体现,它封装了数据访问的底层操作,定义了数据访问的骨架,使得开发者可以只关注自己的业务SQL。
前端控制器模式(Front Controller Pattern):
Spring MVC中的DispatcherServlet就是前端控制器模式的实现,它负责接收请求,并根据请求信息调用相应的处理器(Controller)进行处理。
依赖注入模式(Dependency Injection Pattern):
贯穿于BeanFactory和ApplicationContext接口的核心理念。通过依赖注入,Spring可以在运行时动态地将一个对象所需要的外部依赖注入到该对象中,从而降低了代码的耦合度。
装饰器模式(Decorator Pattern):
在Spring中,BeanWrapper是一个很好的装饰器模式的例子。它允许你在运行时动态地给一个对象添加一些额外的职责。
委派模式(Delegation Pattern):
在Spring MVC中,DispatcherServlet就是一个典型的委派模式的例子。它接收请求,但并不处理请求,而是将请求委派给相应的控制器进行处理。
策略模式(Strategy Pattern):
在Spring中,HandlerMapping接口的实现类就使用了策略模式。它定义了一个处理请求的接口,具体的处理策略由实现类来完成。
适配器模式(Adapter Pattern):
在Spring MVC中,HandlerAdapter接口的实现类就使用了适配器模式。它将不同的处理器(Controller)适配成统一的接口,使得DispatcherServlet可以统一调用。
观察者模式(Observer Pattern):
在Spring的事件驱动模型中,使用了观察者模式。当某个事件发生时,会通知所有注册的观察者(Listener)进行处理。
能说一部分就行了
9、Spring IOC
参考回答: Spring IoC(Inversion of Control,控制反转)是Spring框架的核心特性之一。它的基本思想是将对象的创建和管理过程从应用程序代码中抽离出来,由Spring容器负责创建和管理对象之间的依赖关系。通过这种方式,应用程序代码不再需要直接创建和管理对象,而是向Spring容器发出请求,由容器负责创建和返回所需的对象。
Spring IoC的主要优点包括:
降低耦合性:通过IoC容器来管理对象之间的依赖关系,使得系统更加模块化、可测试和易于扩展。
提高灵活性和可维护性:由于对象的创建和管理由Spring容器负责,因此可以更加灵活地配置和修改对象的行为。同时,由于代码不再包含对象的创建和管理逻辑,因此可以更加专注于业务逻辑的实现,提高代码的可维护性。
支持多种配置方式:Spring IoC支持多种配置方式,包括XML配置、注解配置和Java配置等。这些配置方式可以根据项目的实际需求进行灵活选择和使用。
在实现Spring IoC时,有三种主要的方式:
使用XML实现Spring IoC:通过编写XML配置文件来定义和配置Bean,将配置信息集中在一个XML文件中进行管理。
使用注解方式实现Spring IoC:通过在类和方法上添加注解来定义和配置Bean,使得配置更加简洁、直观,并且减少了对XML文件的依赖。
使用JavaConfig实现Spring IoC:通过编写Java配置类来定义和配置Bean,将配置信息集中在一个Java类中,提供了一种更加面向对象和类型安全的配置方式。
10、spring AOP在项目中的应用场景?
参考回答: 日志记录:在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能。通过在方法执行前、执行后或异常抛出时记录日志,可以方便地跟踪系统的运行情况和问题排查。
事务管理:在数据库操作或其他需要事务管理的操作中,使用AOP可以将事务管理的逻辑与业务逻辑分离,使得事务的控制更加简单和集中。通过AOP,可以在方法开始前开启事务,在方法执行完毕后提交或回滚事务,以确保数据的一致性和完整性。
安全控制:在系统中包含某些需要安全控制的操作,如登录、修改密码、授权等,可以使用AOP来实现安全控制的功能。通过AOP,可以在方法调用前进行安全性检查,例如检查用户的权限或身份验证,以确保只有授权用户可以访问特定的方法或资源。
性能监控:在系统运行过程中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈并进行优化。通过AOP,可以在方法调用前后记录方法的执行时间,以监控和优化系统的性能。
缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能。通过AOP,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中。
异常处理:使用AOP,可以集中处理方法中抛出的异常,例如将异常转换为统一的错误码或进行日志记录。这有助于更好地管理和监控异常,并降低异常对系统的影响。
参数校验和转换:使用AOP,可以在方法调用前对方法的参数进行校验和转换,以确保参数的有效性和符合业务要求。这有助于减少因参数问题导致的错误和异常,并提高系统的健壮性。
12、Spring中如何开启事务?
参考回答: 编程式事务:
编程式事务是通过编程的方式自己去实现事务,比如事务的开启、提交、回滚操作,需要开发人员自己调用commit()或者rollback()等方法来实现。
这种事务管理方式需要在逻辑代码中手动书写事务控制逻辑,因此是侵入性的。它的缺点是代码复用性差,每一个事务都需要自己编写事务逻辑,可能会造成代码冗余。然而,它的优点在于事务边界更容易控制,可以在业务的任意位置进行事务提交或者回滚操作。
声明式事务:
声明式事务则是通过配置或注解的方式,将事务管理逻辑与业务逻辑进行分离,由Spring框架来自动处理事务的开启、提交和回滚。
使用声明式事务时,通常会在业务层(service层)的方法上、类上或接口上使用@Transactional注解。当方法执行前,Spring会自动开启事务;如果方法成功执行完毕,Spring会提交事务;如果方法执行过程中出现异常,Spring会回滚事务。
除了使用@Transactional注解外,还需要在主配置类(通常是应用的入口类)上使用@EnableTransactionManagement注解来启用Spring的事务管理功能。
13、Spring ioc在项目中的应用场景?(了解)
参考回答: 解耦与模块化:
在复杂的系统中,各个组件之间可能存在复杂的依赖关系。使用Spring IoC,可以将这些依赖关系交由Spring容器管理,使得组件之间的耦合度降低,提高了系统的模块化程度。
依赖注入:
Spring IoC支持依赖注入(Dependency Injection, DI),这意味着Spring容器会自动将所需的依赖项注入到对象的实例中。这使得开发者无需在代码中手动创建和管理依赖对象,从而简化了代码并提高了可读性。
配置集中管理:
Spring IoC允许开发者将配置信息(如数据库连接信息、第三方服务接口地址等)集中在一个或多个配置文件中(如XML文件、Java配置类或YAML文件)。这样,当配置信息需要变更时,只需修改配置文件而无需修改代码。
插件化开发:
在一些大型系统中,可能需要支持多种插件或扩展。通过使用Spring IoC,可以将这些插件或扩展作为独立的组件进行开发,并通过配置文件或注解将它们集成到系统中。这使得插件的添加、删除和替换变得更加容易。
测试支持:
在测试过程中,可能需要模拟或替换某些组件的依赖项。Spring IoC提供了方便的测试支持,允许开发者在测试环境中注入模拟对象或测试数据,从而验证系统的正确性。
动态代理:
Spring IoC结合AOP(Aspect-Oriented Programming,面向切面编程)可以实现动态代理。动态代理允许开发者在不修改原始代码的情况下,为类添加额外的功能(如日志记录、事务管理、性能监控等)。这使得系统的功能扩展变得更加灵活和方便。
容器化应用:
在云原生和微服务架构中,应用通常以容器的形式部署。Spring IoC作为Spring框架的核心特性之一,可以与容器化技术(如Docker、Kubernetes等)很好地结合使用。通过Spring IoC管理应用的依赖关系,可以确保应用在容器化环境中能够正常运行并与其他服务进行交互。
14、SpringBoot和Spring的区别是什么?(了解)
参考回答: 设计理念:Spring是一个全面的框架,它提供了许多功能,但同时也增加了项目的复杂度。相反,Spring Boot旨在简化Spring应用程序的初始搭建和开发过程,尽可能自动配置Spring应用程序。基于“约定优于配置”的原则,Spring Boot简化了项目的配置流程。
创建独立应用:Spring Boot可以创建独立的Spring应用程序。它集成了Tomcat、Jetty和Undertow等服务器,并且不需要部署它们。这意味着开发者可以直接打包他们的应用程序为一个可执行的JAR或WAR文件,从而简化了部署过程。
配置:在Spring中,我们需要进行大量的XML配置、JavaConfig和注解处理,以整合各种组件。而在Spring Boot中,它使用了特定的方式来进行配置,极大地简化了这些文件的配置过程。例如,Spring Boot引入了“starters”的概念,这些是预先配置好的Maven依赖项,可以简化Maven配置。
嵌入式容器:Spring Boot嵌入了一个Web服务器(如Tomcat、Jetty或Undertow),这使得应用程序可以作为一个独立的可执行JAR或WAR文件运行,而不需要单独的服务器或容器。
自动配置:Spring Boot的强大之处在于其自动配置功能。它通过检查项目的类路径、属性设置和其他条件,自动为你配置Spring应用程序。例如,如果你在类路径中添加了H2数据库,Spring Boot会自动为你配置一个内存数据库。
依赖管理:在Spring Boot中,你只需要引入相应的starter依赖,就可以快速地整合第三方库和框架。这种依赖管理方式使得项目结构更加清晰,减少了版本冲突的可能性。
15、springBean的初始化过程?
参考回答: Bean的定义:
在Spring配置中定义Bean的信息,包括类名、属性、作用域等。
Bean的加载和解析:
Spring容器启动时,加载并解析配置文件或注解,生成BeanDefinition对象。
实例化Bean:
根据BeanDefinition,通过反射创建Bean实例。如果是单例Bean,会被缓存起来。
依赖注入:
为Bean注入所需的依赖,方式包括构造器注入、setter方法注入和字段注入。
初始化前处理:
如果存在BeanPostProcessor,调用其postProcessBeforeInitialization方法,对Bean进行初始化前的定制化处理。
调用初始化方法:
执行Bean的初始化方法,可能包括:
实现InitializingBean接口的afterPropertiesSet方法。
在配置中指定的init-method方法。
使用@PostConstruct注解的方法。
初始化后处理:
如果存在BeanPostProcessor,调用其postProcessAfterInitialization方法,对Bean进行初始化后的定制化处理,如AOP代理等。
Bean的就绪和使用:
经过上述步骤,Bean已完全初始化,可以通过getBean()方法获取并使用。
16、如何在Spring启动过程中做缓存预热?
参考回答: 监听应用启动事件:Spring框架提供了多种方式来监听应用启动事件,以便在应用启动后执行特定的逻辑。你可以使用@EventListener注解或实现ApplicationListener接口来监听ApplicationReadyEvent事件。当这个事件被触发时,意味着Spring应用已经启动并准备好接受请求了。
使用CommandLineRunner或ApplicationRunner:如果你不想直接监听ApplicationReadyEvent事件,你也可以使用CommandLineRunner或ApplicationRunner接口。这两个接口都定义了一个run方法,Spring Boot会在所有Spring Beans都初始化完成之后调用这个方法。
使用@PostConstruct注解:使用 @PostConstruct 注解标注一个方法,该方法将在 Bean 的构造函数执行完毕后立即被调用。在这个方法中执行缓存预热的逻辑。
17、spring中常用注解?
参考回答: 组件注解:
@Component:用于标识一个类为Spring的组件,可以被自动扫描并注册为Bean。
@Controller:用于标识一个类为控制器层(Controller)组件,通常用于处理HTTP请求。
@Service:用于标注业务逻辑层组件,即Service层。
@Repository:用于标识一个类为数据访问层(DAO)组件,用于操作数据库。
@RestController:结合@Controller和@ResponseBody,用于标识一个类为RESTful风格的控制器,用于返回JSON或XML数据。
注入注解:
@Autowired:由Spring提供,用于自动注入依赖。它默认按类型匹配的方式在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入到标注的变量中。
@Qualifier:与@Autowired配合使用,用于在存在多个相同类型的Bean时,指定要注入的Bean的名称或限定符。
@Resource:与@Autowired类似,但除了按类型匹配外,还支持按名称匹配。
@Value:用于注入配置文件中的值或表达式的结果,例如注入一个字符串或数字常量。
Java配置类相关注解:
@Configuration:用于标识一个类为配置类,通常与@Bean一起使用,以替代传统的XML配置文件。
@Bean:注解在方法上,声明当前方法的返回值为一个Bean,用于在配置类中定义Bean。
切面(AOP)相关注解:
@Aspect:声明一个切面,用于定义通知(Advice)和切点(Pointcut)。
@Before、@After、@AfterReturning、@AfterThrowing、@Around:这些注解用于定义通知,分别表示在方法执行前、后、返回后、抛出异常后和环绕方法执行通知。
其他常用注解:
@RequestMapping:用于映射Web请求到特定的处理器方法。
@PathVariable:用于从URI模板变量中解析值,并绑定到方法参数上。
@RequestParam:用于从请求参数中解析值,并绑定到方法参数上。
@RequestBody:用于将HTTP请求正文转换为Java对象。
@ResponseBody:用于将Java对象转换为HTTP响应正文。
18、spring中注入方式?
参考回答: set()方法注入;
构造器注入:①通过index设置参数的位置;②通过type设置参数类型;
静态工厂注入;
实例工厂;
19、spring中单例Bean是线程安全的吗?
参考回答: 不是,因为所有线程共享一个单例Bean,存在资源的竞争所以是线程不安全的,实际上大部分时间Bean是无状态的,所以说在某种程度上来说Bean其实是安全的。如果是有状态,那就需要开发人员修改bean的作用域。singleton改为prototype。
20、SpringMVC的执行流程?
用户发送请求至前端控制器 DispatcherServlet。
DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
处理器映射器找到具体的处理器(可以根据 xml 配置、注解进行查找),生成处理器及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
DispatcherServlet 调用 HandlerAdapter 处理器适配器。
HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)
Controller 执行完成返回 ModelAndView。
HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet。
DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。
ViewReslover 解析后返回具体 View。
DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中)。
DispatcherServlet 响应用户。
21、解释一下DispatcherServlet的作用?(了解)
DispatcherServlet是Spring MVC的前端控制器,它负责接收所有的HTTP请求,并根据请求的类型和内容,将请求分发到相应的Controller进行处理。同时,它也负责将Controller返回的结果传递给视图进行渲染。
22、Spring MVC的主要组件?(了解)
Spring MVC的主要组件包括DispatcherServlet(前端控制器)
HandlerMapping(处理器映射器)
HandlerAdapter(处理器适配器)
Controller(控制器)
ModelAndView(模型和视图)
ViewResolver(视图解析器)。
23、SpringMVC 的常用注解有哪些?
@RequestMapping :用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径;
@RequestBody :注解实现接收 HTTP 请求的 json 数据,将 json 转换为 Java对象;
@ResponseBody :注解实现将 Controller 方法返回对象转化为 json 对象响应给客户
24、@Controller注解的作用?(了解)
在Spring MVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。在Spring MVC 中提供了一个非常简便的定义Controller 的方法,你无需继承特定的类或实现特定的接口,只需使用@Controller 标记一个类是Controller ,然后使用@RequestMapping 和@RequestParam 等一些注解用以定义URL 请求和Controller 方法之间的映射,这样的Controller 就能被外界访问到。此外Controller 不会直接依赖于HttpServletRequest 和HttpServletResponse 等HttpServlet 对象,它们可以通过Controller 的方法参数灵活的获取到。
@Controller 用于标记在一个类上,使用它标记的类就是一个Spring MVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。单单使用@Controller 标记在一个类上还不能真正意义上的说它就是Spring MVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要我们把这个控制器类交给Spring 来管理。有两种方式:
在Spring MVC 的配置文件中定义MyController 的bean 对象。
在Spring MVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。
25、Spring事务的实现方式和原理以及隔离级别?
在使用Spring框架时,可以有两种使用事务的方式,一种是编程式的,一种是申明式的, @Transactional注解就是申明式的。
首先,事务这个概念是数据库层面的,Spring只是基于数据库中的事务进行了扩展,以及提供了一些能让程序员更加方便操作事务的方式。
比如我们可以通过在某个方法上增加@Transactional注解,就可以开启事务,这个方法中所有的sql都会在一个事务中执行,统一成功或失败。
在一个方法上加了@Transactional注解后,Spring会基于这个类生成一个代理对象,会将这个代理对象作为bean,当在使用这个代理对象的方法时,如果这个方法上存在@Transactional注解,那么代理逻辑会先把事务的自动提交设置为false,然后再去执行原本的业务逻辑方法,如果执行业务逻辑方法没有出现异常,那么代理逻辑中就会将事务进行提交,如果执行业务逻辑方法出现了异常,那么则会将事务进行回滚。
当然,针对哪些异常回滚事务是可以配置的,可以利用@Transactional注解中的rollbackFor属性进行配置,默认情况下会对RuntimeException和Error进行回滚。
spring事务隔离级别:
ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。
ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据。
ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据。
ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的。
ISOLATION_SERIALIZABLE:所有事务逐个依次执行。
Srping 一级、spring二级、三级缓存是如何解决循环依赖的
Srping 一级、spring二级、三级缓存是如何解决循环依赖的
一、先理解什么是 循环依赖
假设有两个班级:
班级A 的学生说:"我们班的口号要和班级B的口号保持一致!"
班级B 的学生说:"我们班的口号要和班级A的口号保持一致!"
这就形成了一个 循环依赖:A依赖B,B又反过来依赖A。双方都无法独立确定自己的口号。
二、Spring创建Bean的基本流程(简化版)
Spring创建Bean(对象)的过程分为3步:
实例化:相当于在内存中开辟一块空间(类似空房子建好了,但家具还没搬进去)。
属性填充:给对象设置属性值(给房子搬家具)。
初始化:执行一些初始化方法(比如打开水电)。
三、循环依赖会引发什么问题?
假设Spring要创建两个Bean:A 和 B,且它们互相依赖:
Spring先开始创建A:
完成 实例化(A的空房子建好了)。
开始 属性填充,发现A依赖B,于是暂停A,去创建B。
Spring开始创建B:
完成 实例化(B的空房子建好了)。
开始 属性填充,发现B依赖A,于是又回去处理A。
此时问题来了:A还没有完成属性填充和初始化(房子还没家具),但B需要依赖一个完整的A。如果Spring直接返回未完成的A,程序可能会出错。
四、三级缓存的作用
Spring用 三级缓存 解决这个问题,你可以把它想象成三个“临时仓库”:
缓存级别作用
一级缓存(成品库)存放 完全初始化好的Bean(精装修的房子,可以直接住)。
二级缓存(半成品库)存放 提前暴露的Bean(毛坯房,但地址告诉别人了,可以后期装修)。
三级缓存(工厂库)存放 生成Bean的工厂(施工队,能根据需求装修房子)。
五、三级缓存如何解决循环依赖?
我们以A和B的循环依赖为例:
创建A的流程
Step 1:实例化A(空房子建好了)。
Step 2:把A的 工厂(施工队)放入 三级缓存。
Step 3:开始给A填充属性,发现需要B。
创建B的流程
Step 1:实例化B(B的空房子建好了)。
Step 2:把B的 工厂 放入三级缓存。
Step 3:开始给B填充属性,发现需要A。
解决B对A的依赖
Step 1:B去 一级缓存 找A(成品库)→ 找不到。
Step 2:去 二级缓存 找A(半成品库)→ 找不到。
Step 3:去 三级缓存 找到A的工厂(施工队),工厂立刻把A的半成品(毛坯房)放到 二级缓存,并返回这个半成品A。
Step 4:B拿到半成品A,完成属性填充和初始化,变成一个完整的B,放入 一级缓存(成品库)。
继续完成A的创建
Step 1:A拿到完整的B,完成属性填充。
Step 2:A执行初始化,变成一个完整的A,放入 一级缓存(成品库)。
六、为什么需要三级缓存?(而不是两级)
三级缓存的关键:存放的是 工厂对象,而不是Bean本身。
解决的问题:如果Bean需要被代理(比如AOP),工厂可以决定返回原始对象还是代理对象。如果只有二级缓存,无法处理这种动态代理的情况。
"如果Bean需要被代理(比如AOP),工厂可以决定返回原始对象还是代理对象。如果只有二级缓存,无法处理这种动态代理的情况。" 关于这点的拓展
分步解释:为什么二级缓存无法处理动态代理
Spring 的三级缓存机制
Spring 使用三级缓存解决循环依赖问题:
一级缓存(singletonObjects):存放完全初始化好的 Bean。
二级缓存(earlySingletonObjects):存放提前暴露的“半成品” Bean(已实例化但未初始化)。
三级缓存(singletonFactories):存放 Bean 的工厂对象,用于动态生成原始对象或代理对象。
动态代理的生成时机
当一个 Bean 需要被代理(如应用了 AOP)时,代理对象的生成发生在 Bean 的初始化阶段。但循环依赖会导致 Bean 在未完全初始化前就被其他 Bean 依赖,此时需要提前暴露 Bean 的引用。
二级缓存的局限性
假设只有二级缓存:
直接暴露原始对象:当 Bean A 被提前暴露时,二级缓存直接存储原始对象。
后续无法替换为代理:如果后续发现 Bean A 需要被代理,由于二级缓存中已经是原始对象,无法动态替换成代理对象。
AOP 失效:其他依赖 Bean A 的 Bean 获取到的是原始对象,而不是代理对象,导致切面逻辑(如事务、日志)无法执行。
三级缓存的核心作用
三级缓存存储的是工厂对象(ObjectFactory),而不是直接存储 Bean 实例。 当需要获取提前暴露的 Bean 时,工厂对象会动态决定返回原始对象还是代理对象:
如果 Bean 需要代理,工厂生成代理对象。
如果不需要代理,工厂返回原始对象。
场景模拟
循环依赖场景:Bean A 依赖 Bean B,Bean B 依赖 Bean A。 流程:
创建 Bean A,实例化后将其工厂对象存入三级缓存。
填充 Bean A 的属性时,发现需要注入 Bean B。
创建 Bean B,实例化后将其工厂对象存入三级缓存。
填充 Bean B 的属性时,发现需要注入 Bean A,于是从三级缓存获取 Bean A 的工厂对象。
关键点:工厂对象检查 Bean A 是否需要代理:
如果需要代理,生成代理对象并返回。
如果不需要代理,返回原始对象。
Bean B 初始化完成后,Bean A 继续完成初始化,最终替换为代理对象(如果需要)。
若只有二级缓存: 在第4步中,二级缓存直接返回原始对象,后续即使发现 Bean A 需要代理,也无法替换,导致 Bean B 持有的是原始对象而非代理对象。
结论
三级缓存的工厂机制提供了动态生成代理的能力,而二级缓存只能存储固定的对象(原始对象或代理对象),无法在循环依赖中灵活处理代理逻辑。因此,三级缓存是 Spring 解决动态代理与循环依赖共存问题的关键设计。
对于一些名词解释
什么是“存放 Bean 的工厂对象”?
Spring 的 三级缓存 中有一个叫 singletonFactories 的区域,这里存储的是 Bean 的工厂对象(ObjectFactory)。
工厂对象:可以理解为一个“生产 Bean 的机器”,它保存了如何创建 Bean 的指令(比如调用构造方法、注入属性)。
关键作用:不直接存 Bean 实例,而是存生成 Bean 的“方法”。当需要提前暴露 Bean 时,工厂对象会被调用,动态决定返回原始对象还是代理对象(比如需要 AOP 时)。
举个栗子: 想象你去奶茶店点单,店员(三级缓存)手里有一个“奶茶配方”(工厂对象)。当有人问“我的奶茶做好了吗?”时,店员可以按配方现场制作一杯(生成 Bean),而不是提前做好一堆奶茶放桌上(直接存 Bean 实例)。
什么是“原始对象”?
原始对象:通过 new 关键字直接创建的 Bean 实例,未经过任何加工(比如未注入属性、未初始化、未应用 AOP 代理)。
特点:一个“半成品”,比如刚盖好毛坯房,还没装修。
代码示例:
什么是“代理对象”?
代理对象:在原始对象基础上,由 Spring 动态生成的“增强版对象”。比如:
应用了 AOP 的事务管理(@Transactional)。
方法调用前后添加日志、权限校验等逻辑。
特点:代理对象会“包裹”原始对象,调用方法时实际执行的是代理逻辑。
代码示例:
什么是“提前暴露 Bean 引用”?
在 Spring 创建 Bean 的过程中,如果遇到循环依赖(比如 Bean A 依赖 Bean B,Bean B 又依赖 Bean A),Spring 的解决方法是:
提前暴露引用:在 Bean A 刚创建完原始对象(未初始化)时,就把这个原始对象的引用存到缓存中。
其他 Bean 可以拿到这个引用:比如 Bean B 在创建时,就能直接注入 Bean A 的原始对象引用,打破循环依赖。
举个栗子:
你(Bean A)和同事(Bean B)合作完成一个项目,但你需要同事的文档才能开工,同事也需要你的代码才能继续。
解决办法:你先写个代码框架(原始对象),把框架发给同事(提前暴露引用),同事基于你的框架写文档,你再基于文档完善代码。
为什么需要三级缓存来存工厂对象?
如果只有二级缓存(直接存原始对象),会遇到一个致命问题:
代理对象无法替换原始对象:假设 Bean A 需要被代理(比如加事务),但提前暴露的是原始对象,后续即使生成了代理对象,其他 Bean(如 Bean B)拿到的还是原始对象,导致 AOP 失效。
三级缓存的解决方案:
存工厂对象:工厂对象可以动态生成原始对象或代理对象。
按需生成:当其他 Bean 需要注入 Bean A 时,调用工厂对象,检查 Bean A 是否需要代理,再返回对应对象。
流程对比:
完整流程示例
以循环依赖(Bean A → Bean B → Bean A)且 Bean A 需要代理为例:
创建 Bean A:
实例化 Bean A(原始对象)。
将 Bean A 的工厂对象存入三级缓存(未初始化)。
填充 Bean A 的属性:发现需要注入 Bean B。
创建 Bean B:
实例化 Bean B(原始对象)。
将 Bean B 的工厂对象存入三级缓存。
填充 Bean B 的属性:发现需要注入 Bean A。
关键点:从三级缓存拿到 Bean A 的工厂对象,调用工厂生成 Bean A 的实例。
工厂检查 Bean A 是否需要代理:如果需要,生成代理对象;否则返回原始对象。
Bean B 初始化完成:存入一级缓存。
Bean A 继续初始化:最终生成的 Bean A(代理对象)替换掉三级缓存中的工厂对象,存入一级缓存。
总结
原始对象:刚创建好的“毛坯房”,未加工。
代理对象:装修好的“精装房”,包含增强功能(如 AOP)。
提前暴露引用:解决循环依赖的“临时借条”,让其他 Bean 能先拿到半成品。
三级缓存:通过工厂对象动态决定返回原始对象还是代理对象,确保最终注入的是正确的对象。
工厂对象的结构与作用
在 Spring 中,三级缓存(singletonFactories)存储的工厂对象是一个 ObjectFactory<?>,它的核心逻辑是 延迟生成 Bean 的早期引用。这个工厂对象本质上是一个“生成器”,用于在需要时动态创建 Bean 的实例(可能是原始对象或代理对象)。
工厂对象长什么样子?
在 Spring 源码中,工厂对象是一个 Lambda 表达式,具体代码如下(简化版):
这段代码的含义是:
addSingletonFactory:将工厂对象存入三级缓存。
Lambda 表达式 () -> getEarlyBeanReference(...):工厂对象的具体逻辑,当调用它时,会执行 getEarlyBeanReference 方法生成 Bean 的早期引用。
工厂对象的核心逻辑
getEarlyBeanReference 方法的作用是:
检查是否需要生成代理对象:遍历所有 BeanPostProcessor,如果某个后置处理器(如 AbstractAutoProxyCreator)需要生成代理,则返回代理对象。
不需要代理则返回原始对象:如果无需代理,直接返回原始 Bean 实例。
工厂对象的实际内容
用伪代码表示,工厂对象的工作流程如下:
实际场景示例
假设有一个 Bean A 需要被 AOP 代理:
创建原始对象:UserService rawUserService = new UserService()。
生成工厂对象:
存入三级缓存:singletonFactories.put("userService", factory)。
当其他 Bean 需要注入 userService 时,Spring 会调用这个工厂对象:
为什么需要 Lambda 表达式?
延迟执行:工厂对象不会立即生成 Bean 的引用,只有在其他 Bean 需要注入它时才会触发。
动态决策:在调用工厂对象时,Spring 已经完成了后置处理器的初始化,可以准确判断是否需要生成代理。
总结
工厂对象的结构:是一个 ObjectFactory,内部通过 Lambda 表达式调用 getEarlyBeanReference。
核心作用:延迟生成 Bean 的早期引用,并根据后置处理器动态决定返回原始对象还是代理对象。
解决的关键问题:在循环依赖中,确保其他 Bean 注入的是最终形态的 Bean(可能是代理对象)。
就像你去餐厅点餐时,服务员先给你一个“订单号”(工厂对象),厨房根据订单号动态决定是做原味牛排还是加酱汁的牛排(代理对象),保证你最终拿到的是正确的菜品。
七、总结
三级缓存 像一个协调员,确保在循环依赖时,双方都能拿到一个“临时可用”的对象。
一级缓存 放最终成品,二级缓存 放临时半成品,三级缓存 放能生成半成品的工厂。
这种设计解决了“先有鸡还是先有蛋”的问题,让互相依赖的对象逐步完善自己。
八、生活中的类比
想象你在组装一台电脑:
你需要先安装主板(类似实例化)。
但安装主板时发现需要电源(依赖另一个组件)。
电源还没装好,但你可以先把主板的“位置”预留出来(类似放入三级缓存)。
接着去装电源,电源需要连接主板的位置,此时主板虽然没完全装好,但位置已经确定(从缓存拿到半成品)。
最后完成所有组件的安装。
SpringBoot
1、什么是 SpringBoot?SpringBoot的特征是什么(了解)
通过 Spring Boot ,可以轻松地创建独立的,基于生产级别的 Spring 的应用程序,您可以 “ 运行 ” 它们。大多数 Spring Boot 应用程序需要最少的 Spring 配置。
特征:
简化配置:通过约定优于配置的方式,减少了大量的XML配置。
快速开发:提供了丰富的“启动器”,可以快速集成第三方库。
内置服务器:支持内嵌Tomcat、Jetty等,简化了部署过程。
监控和健康管理:提供了多种监控和健康管理功能,如Actuator。
易于测试:支持多种测试框架,如JUnit、Spring Test等。
2、Springboot配置文件什么时候加载到内存中?
解析
1.Spring Boot启动流程的阶段性划分
Spring Boot的启动过程可分解为以下关键阶段:
初始化SpringApplication实例:通过SpringApplication.run(主类.class, args)触发。
环境准备(Environment Preparation):
加载配置文件:解析所有配置源(如application.properties、环境变量、命令行参数)。
构建Environment对象:存储所有配置属性,供后续流程使用。
创建ApplicationContext:根据应用类型(Servlet/Reactive)初始化上下文。
执行ApplicationContext的刷新:
加载自动配置类:基于@EnableAutoConfiguration和Environment中的条件注解。
实例化并注入Bean:依赖Environment中的配置属性完成Bean的初始化。
启动内嵌服务器:如Tomcat、Netty等,监听端口并进入运行状态。
2.配置文件加载的精确时机
配置文件加载发生在环境准备阶段,具体由以下步骤实现:
触发入口:SpringApplication的prepareEnvironment()方法。
核心组件:
ConfigFileApplicationListener:监听ApplicationEnvironmentPreparedEvent事件,负责加载配置文件。
PropertySourceLoader:解析.properties和.yml文件,将其转换为键值对。
加载顺序与优先级:
默认配置文件:classpath:application.properties或classpath:application.yml。
Profile-specific文件:如classpath:application-dev.yml(需通过spring.profiles.active激活)。
外部配置文件:按优先级从file:./config/、file:./、classpath:/config/、classpath:/加载。
命令行参数:通过--key=value传递的参数,优先级最高。
系统环境变量:操作系统环境变量中的配置。
属性存储:所有解析后的属性被封装为PropertySource对象,按优先级顺序添加到Environment的MutablePropertySources中。
3.关键技术组件与机制
Environment接口:
继承自PropertyResolver,提供统一API访问配置属性。
包含Profiles(当前激活的环境标识)和PropertySources(所有配置源集合)。
@PropertySource注解:
允许开发者显式导入自定义配置文件,但这些文件的加载仍遵循环境准备阶段的流程。
条件化配置(Conditional Configuration):
自动配置类(如DataSourceAutoConfiguration)通过@ConditionalOnProperty等注解,依赖Environment中的属性决定是否生效。
4配置文件加载的底层流程
事件驱动机制:
SpringApplication启动时发布ApplicationStartingEvent。
初始化环境后发布ApplicationEnvironmentPreparedEvent。
ConfigFileApplicationListener监听到该事件,触发配置文件加载。
文件解析过程:
遍历所有支持的配置文件路径(如classpath:/、file:./config/)。
使用PropertiesPropertySourceLoader或YamlPropertySourceLoader解析文件内容。
将解析结果封装为PropertySource并添加到Environment。
属性覆盖规则:
后加载的PropertySource覆盖先加载的同名属性。
命令行参数(通过CommandLinePropertySource)始终处于最高优先级。
面试回答话术
Spring Boot的配置文件加载发生在应用启动的环境准备阶段,这一阶段的核心目标是构建完整的Environment对象,为后续的自动配置和Bean初始化提供配置数据支持。下面是具体细节:
触发时机:
当执行SpringApplication.run()方法时,框架首先初始化Environment对象。
在prepareEnvironment()方法中,通过ConfigFileApplicationListener监听ApplicationEnvironmentPreparedEvent事件,触发配置文件的加载。
加载流程:
默认配置:优先加载classpath下的application.properties或application.yml。
环境专属配置:若通过spring.profiles.active指定了Profile(如dev),则加载application-{profile}.properties。
外部化配置:按优先级加载项目外部的配置文件(如./config/application.yml)。
动态参数:最后处理命令行参数(--key=value)和系统环境变量,确保它们覆盖文件中的同名配置。
存储与使用:
所有配置属性被封装为PropertySource对象,按优先级顺序存入Environment。
自动配置类(如DataSourceAutoConfiguration)通过@Conditional注解从Environment中读取属性,决定是否启用特定配置。
Bean的@Value注入或@ConfigurationProperties绑定均依赖Environment中的值。
优先级规则示例:
假设application.yml定义server.port=8080,但命令行传递--server.port=8081,最终端口为8081。
若同时存在application-dev.yml和application-prod.yml,只有激活的Profile对应的文件会被加载。
3、SpringBoot 自动配置原理 ?
Spring Boot的自动配置是通过@EnableAutoConfiguration注解实现的。它基于类路径中的jar依赖、已定义的bean和环境变量等因素来尝试自动配置应用程序。这意味着开发者只需添加特定的依赖项,Spring Boot就能自动配置相关的bean和属性。
4、 SpringBoot 的核心注解是哪个?它主要由哪几个注解组成的?
@SpringBootApplication ,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration :组合了 @Confifiguration 注解,实现配置文件的功能。
@EnableAutoConfiguration :打开自动配置的功能,也可以关闭某个自动配置的选项, 例如: java 如关闭数据源自动配置功能: @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) 。
@ComponentScan : Spring 组件扫描。
5、SpringBoot的启动流程是怎么样的?
加载启动类:
当SpringBoot项目启动时,它会在当前工作目录下寻找有@SpringBootApplication注解标识的类,并把这个类作为应用程序的入口点。这个注解包含了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解的功能。
如果找不到这样的主类,则会打印错误信息并退出。
加载配置文件:
SpringBoot会自动扫描当前项目的resources目录,并加载其中的application.properties或application.yml等配置文件。这些配置文件中包含了应用程序的各种参数,例如数据库连接信息、日志级别等等。
在这个过程中,SpringBoot会读取配置文件中的参数,并将它们转换成Environment对象中的属性。
同时,它还会扫描当前项目中的所有Bean,并将它们注册到ApplicationContext中。
创建Spring容器:
SpringBoot使用SpringApplication类创建Spring容器。SpringApplication类是SpringBoot的核心类,它提供了配置和管理Bean的方法。
初始化配置:
通过类加载器读取classpath下所有的spring.factories配置文件,创建一些初始化配置对象。
通知监听器应用程序启动开始,创建环境对象Environment,用于读取环境配置,如application.yml。
创建应用程序上下文和Bean工厂对象:
创建应用程序上下文,并创建Bean工厂对象。
刷新上下文(启动核心):
这是一个关键步骤,包括工厂对象配置、Bean处理器配置、类的扫描、解析、Bean定义、Bean类信息缓存、服务器(如Tomcat)创建、Bean实例化、动态代理对象创建等。
注册并实例化Bean工厂发布处理器:
注册并实例化Bean工厂发布处理器,并且调用这些处理器,对包扫描(主要是class)文件进行处理。
应用程序启动完成:
一旦所有的Bean都创建并初始化完成,Spring容器就准备就绪,应用程序启动完成,开始等待接收请求。
6、Springboot如何实现异步的?
线程Thread
Future
异步框架CompletableFuture
Spring注解@Async
Spring ApplicationEvent事件
消息队列
7、后端是如何处理跨域的?
CORS(跨源资源共享): CORS 是一种 W3C 规范,全称是“跨来源资源共享”(Cross-Origin Resource Sharing)。它定义了一种浏览器和服务器交互的方式来确定是否允许跨源请求。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
服务器端设置 Access-Control-Allow-Origin 头部字段来指定允许哪些源进行跨域访问。可以设置为具体的源(如 http://example.com),或者设置为 * 来允许所有源。
服务器端还可以设置其他CORS相关的头部字段,如 Access-Control-Allow-Methods(允许哪些HTTP方法,如GET, POST, PUT等)、Access-Control-Allow-Headers(允许哪些HTTP头部字段)等。
在Spring Boot中,可以通过配置WebMvcConfigurer来全局设置CORS规则,或者在控制器方法上使用@CrossOrigin注解来单独设置。
JSONP(JSON with Padding): JSONP 是一种跨域通信的技巧,通过动态插入 <script> 标签来加载跨域的JavaScript资源。由于 <script> 标签不受同源策略的限制,所以可以利用这个特性来实现跨域数据请求。但是,JSONP 只支持GET请求,并且需要在服务器端对数据进行处理,将数据封装在一个函数调用中返回。
反向代理: 通过配置反向代理服务器(如Nginx、Apache等),将来自客户端的跨域请求转发到后端服务器,然后将后端服务器的响应返回给客户端。这样,客户端和服务器之间的跨域请求就被代理服务器所“隐藏”了,从而实现了跨域通信。
使用中间件: 在某些后端框架中,如Django、Express等,可以使用现成的中间件来处理跨域问题。这些中间件会自动为响应添加CORS相关的头部字段,从而简化了跨域配置的过程。
SpringCloud&&SpringCloudAlibaba
1、SpringBoot和SpringCloud有何区别?
定位与关注点不同:
Spring Boot 是一个用于快速构建单个、独立、生产级别的 Spring 应用的框架。它专注于简化 Spring 应用的开发过程,提供自动配置、嵌入式服务器等特性,使开发者能够快速、方便地创建单个微服务。
Spring Cloud 是一套基于 Spring Boot 的工具集,用于构建分布式微服务架构。它关注全局的微服务协调和治理,提供了微服务之间的配置管理、服务发现、断路器、路由、负载均衡、全局锁、分布式会话等集成功能。
依赖关系:
Spring Boot 可以独立使用,开发者可以使用它来开发和部署单个微服务应用。
Spring Cloud 依赖于 Spring Boot,是在其基础上构建的。Spring Cloud 必须依赖 Spring Boot 才能运行,属于依赖关系。
功能侧重点:
Spring Boot 专注于快速、便捷地开发单个微服务实例,简化配置和开发流程,提高开发效率。
Spring Cloud 关注微服务架构的整体治理,将由 Spring Boot 构建的各个微服务整合并管理起来,解决分布式系统中的常见问题。
总结:
Spring Boot 是用于创建单体微服务的基础框架,旨在简化开发过程。
Spring Cloud 是基于 Spring Boot 的微服务治理框架,提供构建和管理分布式系统所需的全套解决方案。
2、什么是Spring Cloud?(了解)
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如 服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
Spring Cloud的主要目标是解决分布式系统中的常见问题,例如服务发现、负载均衡、配置管理、断路器、消息总线等。
3、Spring Cloud的主要组件有哪些?
Eureka:服务注册与发现
Zuul:服务网关
Ribbon:客户端负载均衡
Feign:声明性的Web服务客户端
Hystrix:断路器
Confifig:分布式统一配置管理
4、 Spring Cloud的优缺点?
优点:
产出于Spring大家族,Spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善
组件丰富,功能齐全。Spring Cloud 为微服务架构提供了非常完整的支持。例如、配置管理、服务发现、断路器、微服务网关等;
Spring Cloud 社区活跃度很高,教程很丰富,遇到问题很容易找到解决方案
服务拆分粒度更细,耦合度比较低,有利于资源重复利用,有利于提高开发效率
可以更精准的制定优化服务方案,提高系统的可维护性
减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发
微服务可以是跨平台的,可以用任何一种语言开发
适于互联网时代,产品迭代周期更短
缺点:
微服务过多,治理成本高,不利于维护系统
分布式系统开发的成本高(容错,分布式事务等)对团队挑战大
5、什么是Eureka?
Eureka作为SpringCloud的服务注册功能服务器,他是服务注册中心,系统中的其他服务使用Eureka的客户端将其连接到Eureka Service中,并且保持心跳,这样工作人员可以通过EurekaService来监控各个微服务是否运行正常。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如 服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
6、Eureka自我保护机制是什么?
当Eureka Server 节点在短时间内丢失了过多实例的连接时(比如网络故障或频繁启动关闭客户端)节点会进入自我保护模式,保护注册信息,不再删除注册数据,故障恢复时,自动退出自我保护模式。
7、Rest和RPC有什么区别?
RPC最主要的缺陷就是服务提供方和调用方式之间依赖太强,我们需要为每一个微服务进行接口的定义,并通过持续继承发布,需要严格的版本控制才不会出现服务提供和调用之间因为版本不同而产生的冲突。
而REST是轻量级的接口,服务的提供和调用不存在代码之间的耦合,只是通过一个约定进行规范,但也有可能出现文档和接口不一致而导致的服务集成问题,但可以通过swagger工具整合,使代码和文档一体化得以解决,所以REST在分布式环境下比RPC更加灵活。
8、什么是Zuul网关,有什么作用?
Zuul网关(也称为Zuul Gateway)是Spring Cloud生态系统中的一个组件,用于提供动态路由、监控、弹性、安全等功能的API网关。Zuul在Spring Cloud Netflix栈中是一个重要的组件,但在Spring Cloud的后续版本中,Netflix栈的部分组件(包括Zuul)逐渐被Spring Cloud Gateway所取代,因为Spring Cloud Gateway是基于WebFlux构建的,提供了更好的性能和非阻塞的API。
Zuul网关的作用:
路由和转发:Zuul网关可以作为请求的入口点,接收来自客户端的请求,并根据配置的路由规则将请求转发到相应的微服务实例。
动态路由:Zuul支持动态路由,这意味着你可以在不重启Zuul网关的情况下更改路由规则。
负载均衡:Zuul可以与Ribbon等负载均衡器结合使用,将请求分发到多个服务实例上,以提高系统的可扩展性和可用性。
安全:Zuul可以集成Spring Security等安全框架,实现身份验证、授权、限流等功能,保护后端服务免受非法访问。
监控和跟踪:Zuul可以与Spring Cloud的监控和跟踪组件(如Spring Cloud Sleuth和Zipkin)结合使用,收集和分析请求数据,帮助开发人员了解系统的运行情况和性能瓶颈。
静态响应:在某些情况下,Zuul可以直接返回静态响应,而无需将请求转发到后端服务。
压力测试:Zuul可以配置为模拟后端服务的响应,以便进行压力测试或故障模拟。
请求过滤:Zuul提供了请求过滤器(Pre、Post和Error),可以在请求被路由到后端服务之前或之后执行自定义逻辑。
服务发现:Zuul可以与Eureka等服务发现组件结合使用,自动发现和路由到可用的服务实例。
9、什么是Ribbon,有什么作用?
Ribbon是Netflix开源的一个负载均衡器,它有助于控制HTTP和TCP客户端的行为。在微服务架构中,Ribbon通常与Spring Cloud一起使用,以实现客户端侧的负载均衡。
Ribbon的主要作用和功能包括:
客户端负载均衡:Ribbon可以作为一个客户端负载均衡器,它允许你在HTTP和TCP客户端上执行负载均衡。与传统的服务端负载均衡器(如Nginx)不同,Ribbon是在客户端实现的,这意味着每个服务消费者都会维护一份服务提供者的清单,并自己进行服务调用。
服务调用:在微服务架构中,服务之间经常需要进行远程调用。Ribbon可以与RestTemplate结合使用,为服务调用提供负载均衡功能。通过配置Ribbon,你可以指定服务调用的策略(如轮询、随机等),以及负载均衡的其他参数。
容错处理:Ribbon内置了多种容错处理机制,如快速失败、重试等。当某个服务提供者出现故障时,Ribbon可以根据配置的策略自动进行容错处理,避免因为某个服务的故障而导致整个系统的崩溃。
监控和统计:Ribbon还提供了监控和统计功能,可以收集服务调用的数据并进行统计和分析。这些数据可以帮助你了解系统的运行状况、发现潜在的性能问题并进行优化。
10、什么是Feign,有什么作用?
Feign是一个声明式的Web服务客户端,它使得编写Web服务客户端变得更加容易。Feign的主要作用如下:
简化HTTP客户端开发:Feign通过创建接口并使用注解的方式,简化了对HTTP请求的封装和处理。开发者只需要定义接口并添加相应的注解,就可以实现对远程服务的调用,而无需编写大量的HTTP请求代码。
支持负载均衡:Feign可以与Ribbon等负载均衡器结合使用,自动从服务注册中心获取服务提供者的地址列表,并根据负载均衡策略选择一个服务提供者进行调用。这有助于平衡各个服务提供者的负载,提高系统的整体性能和可用性。
支持服务调用:Feign可以用于微服务架构中的服务调用。通过Feign,服务消费者可以轻松地调用其他服务提供者提供的接口,实现服务之间的通信和协作。
支持容错处理:Feign内置了熔断器和降级机制,可以在某个服务提供者出现故障或响应超时时,自动切换到其他可用的服务提供者,或者返回一个缺省值,避免因为某个服务的故障而导致整个系统的崩溃。
声明式调用:Feign的声明式调用使得开发者可以像调用本地方法一样调用远程方法,无需关注与远程服务的交互细节,降低了开发的复杂性和难度。
11、什么是Hystrix,有什么作用?
Hystrix是Netflix开源的一款针对分布式系统的延迟和容错库,它的主要作用是提供容错能力,防止分布式系统中的雪崩效应。具体来说,Hystrix通过以下方式实现其功能:
服务隔离:通过线程池或信号量隔离策略,限制对某个依赖服务的并发请求数量,防止因单一依赖服务的故障导致整个系统资源耗尽。线程池隔离为每个依赖服务分配独立的线程池,信号量隔离则限制同时执行的请求数量。
服务降级:当一个服务无法正常工作时,Hystrix可以通过返回一个默认值或者执行预先定义的逻辑,代替原本的服务响应。这样可以避免整个系统因为一个服务的故障而崩溃。
熔断器模式:当一定时间内某个服务的失败次数达到一定阈值时,Hystrix会主动断开对该服务的调用,从而防止对该服务的连续调用,减轻服务器压力和响应时间。
实时监控和统计:支持实时监控和统计服务调用的性能指标,帮助开发人员及时发现和解决问题。
12、说一说什么是服务雪崩?
服务雪崩:多个服务相互调用时,A调B,B调C,C调D等等更多调用,那么如果中间调用需要很长时间,然后再去调用A,那么占用的资源就越来越多,导致系统崩溃。
13、什么是服务降级、服务熔断、服务隔离?
服务降级: 当出现请求超时、资源不足时(线程或者信号量),会进行服务降级,就是去返回fallback方法的结果。
服务熔断: 当失败率(网络故障或者超时造成)达到阈值自动触发降级,是一种特殊的降级。
服务隔离: 为隔离的服务开启一个独立的线程,这样在高并发情况下,也不会影响该服务。
14、在微服务中,如何保护服务?(了解)
一般使用hystrix框架,实现服务隔离来避免出项服务的雪崩效应,从而达到保护服务的效果。当微服务中,高并发的数据库访问量导致服务线程阻塞,使单个服务宕机,服务的不可用会蔓延到其他服务,引起整体服务灾难性的后果,使用服务降级能有效为不同得服务分配资源,一旦服务不可用则友好提示,不占用其他服务资源,从而避免单个服务崩溃引发整体服务的不可用。
15、服务降级底层是如何实现的?(了解)
Hystrix实现服务降级的功能是通过重写HystrixCommand中的getFallback()方法,当Hystrix的run方法或construct执行发生错误时转而执行getFallback()方法。
16、什么是Spring Cloud Config?有什么作用?
Spring Cloud Config是一个基于Spring Cloud的配置管理工具,它的主要作用包括:
集中管理配置:在微服务架构中,服务数量众多,每个服务可能需要不同的配置信息。Spring Cloud Config 允许将所有的配置文件集中存储在一个地方(通常是一个 Git 仓库),方便地进行管理和维护。当配置发生变化时,只需要在 Git 仓库中修改配置文件,所有的服务都可以实时获取到最新的配置信息。
支持多种环境和配置格式,实现不同环境的配置隔离:Spring Cloud Config 支持多种环境(如开发、测试、生产环境等)和多种配置格式(如 properties 文件、YAML 文件等)。它能够根据不同的环境加载对应的配置文件,避免了手动修改配置的繁琐过程,从而实现配置的集中化和环境隔离。
动态调整配置:在服务运行期间,可能需要调整一些配置参数。使用 Spring Cloud Config 可以实现配置的动态调整,而不需要重启服务,提高了系统的灵活性和可维护性。
加密和解密功能:为了保护敏感信息不被泄露,Spring Cloud Config 提供了对配置信息的加密和解密功能,确保配置的安全性。
17、Eureka保证是AP吗?(了解)
Eureka是Netflix开发的服务发现框架,它主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。Eureka的设计原则之一就是保证系统的高可用性(Availability)和分区容忍性(Partition tolerance),即AP。
Eureka通过以下几种机制来保证AP:
客户端缓存:Eureka Client在启动时会从Eureka Server获取服务注册表的信息,并将其缓存在本地。这样,即使Eureka Server出现故障,客户端仍然可以利用缓存中的信息消费其他服务的API,保证了系统的可用性。
心跳检测:Eureka Client会定期向Eureka Server发送心跳,以证明其仍然存活。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,它将会从服务注册表中移除这个服务节点。这种机制有助于Eureka Server及时发现并处理失效的服务节点,保证了系统的分区容忍性。
自我保护模式:Eureka Server在运行期间,会统计心跳失败的比例在阈值范围内,不会剔除服务提供者;一旦超过了阈值,Eureka Server会将这些实例保护起来,让这些实例不会马上被剔除。这种机制可以避免因为网络分区或其他原因导致的误判,进一步保证了系统的可用性。
18、什么是Spring Cloud Alibaba?(了解)
Spring Cloud Alibaba是一组基于Spring Cloud的开源框架,用于构建分布式微服务应用和微服务架构。它提供了一系列工具和组件,包括服务注册与发现、配置管理、熔断降级、限流、分布式事务等,以简化微服务应用的开发和部署。
19、Spring Cloud Alibaba与Spring Cloud有什么区别?
Spring Cloud Alibaba是Spring Cloud的一个子项目,它扩展了Spring Cloud,添加了一些针对微服务应用的特定功能。区别主要体现在以下几个方面:
Spring Cloud Alibaba提供了更多与阿里巴巴云生态系统集成的组件,如Nacos、Sentinel、RocketMQ等。
Spring Cloud Alibaba引入了更多分布式系统的解决方案,如分布式事务、分布式锁等。
Spring Cloud Alibaba在微服务应用的开发和部署方面提供了更多便捷性。
20、Spring Cloud Alibaba有哪些组件?
服务注册与发现组件(Nacos):Nacos 是用于服务发现、配置和服务管理的平台。它支持动态配置服务、服务发现、服务健康检查、流量管理等功能。
服务降级与熔断组件(Sentinel):Sentinel 是面向分布式、云原生系统的流量防卫兵。它提供了流量控制、熔断降级、系统自适应保护等多个维度保护服务的稳定性。
分布式配置中心组件(Nacos Config):Nacos Config是一个用于实现分布式配置管理的组件。它可以集中管理应用程序的配置信息,并将其动态地推送到所有相关的服务实例。Nacos Config支持多种配置参数的管理和监控,可以帮助开发人员更好地管理和调试分布式系统。
消息驱动组件(RocketMQ):RocketMQ是一个用于实现消息驱动的组件。它提供了可靠的消息传递机制,支持高吞吐量和低延迟的消息处理。RocketMQ支持多种消息模式的选择,如点对点模式和发布订阅模式,可以根据应用程序的需求来进行灵活调整。
阿里云对象存储服务(OSS):是一个海量、安全、低成本、高可靠的云存储服务,提供99.999999999%的数据持久性。Spring Cloud Alibaba 提供了与 OSS 集成的组件,方便开发者在微服务应用中使用 OSS 存储数据。
Seata: 是一个开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。它支持 AT、TCC、SAGA 和 XA 等事务模式,为微服务架构下的分布式事务提供一站式解决方案。
SchedulerX:SchedulerX 是阿里云提供的分布式任务调度平台,支持定时、周期、依赖触发等多种任务调度模式。Spring Cloud Alibaba 提供了与 SchedulerX 集成的组件,方便开发者在微服务应用中进行任务调度和管理。
21、什么是Nacos?
Nacos(前身为Alibaba注册中心和配置中心)是Spring Cloud Alibaba生态系统的核心组件之一。它是一个开源的动态服务发现、配置管理和服务管理平台。Nacos支持服务注册与发现、配置管理、DNS和HTTP服务、流量管理等功能,为微服务应用提供了中心化的管理和配置。
22、Nacos 服务注册与发现的原理?
服务注册:在微服务架构中,服务实例启动时会将自己的网络位置(包括 IP 地址和端口号)注册到服务注册中心。以下是服务注册的过程:
服务提供者注册:服务提供者(例如一个微服务实例)在启动时,向 Nacos 服务器发送一个注册请求,请求通常包含服务标识、服务实例的地址、端口和元数据信息等。
服务清单更新:Nacos 服务器接收到注册请求后,会将该服务实例的信息添加到服务清单中。Nacos 使用内存中的数据结构来维护服务清单以便快速读写。
服务发现:服务消费者(例如另一个微服务)使用服务注册中心来发现可用服务的网络位置。以下是服务发现的过程:
服务消费者查询:服务消费者向 Nacos 服务器查询它所需要的服务提供者的信息。查询可以是基于服务名的静态查询,也可以使用更复杂的筛选规则。
服务信息响应:根据服务消费者的查询,Nacos 服务器返回一组服务提供者实例的信息,服务消费者可以根据这些信息调用服务提供者。
心跳检测:为了维护服务清单的准确性,Nacos 实现了心跳机制:
心跳机制:注册在 Nacos 服务器上的服务提供者需定期发送心跳。心跳是一个轻量级的网络请求,表明服务实例仍处于活跃状态。
健康检查:如果 Nacos 服务器在预定的时间内没有收到服务实例的心跳,那么它会将这个实例从服务列表中移除。
23、什么是GateWay?有什么作用?
Gateway,又称为网关,是一个网络通向其他网络的IP地址。在计算机网络中,它主要起着以下作用:
协议转换:网关可以将一种协议转换为另一种协议,以便不同类型的设备和服务可以相互通信。例如,将HTTP协议转换为FTP协议,以便在Web浏览器和FTP客户端之间传输文件。
安全性:网关可以提供安全性,例如防火墙和入侵检测系统,以保护网络免受恶意攻击和未经授权的访问。
负载均衡:网关可以实现负载均衡,通过将流量分配到多个服务器上,以提高网络性能和可靠性。
缓存:网关可以缓存静态内容,例如图片和视频,以减少对源服务器的请求,从而提高网站的性能。
24、什么是Sentinel?有什么作用?
Sentinel是一个开源的分布式系统的流量控制和熔断降级组件,主要用于保护服务的稳定性。它提供了实时的流量控制、熔断降级、系统负载保护等功能,帮助开发者有效地保护应用程序,防止故障和异常情况的发生。
Sentinel的主要作用包括:
流量控制:通过配置不同的规则,对请求流量进行限制,以防止系统因流量过大导致崩溃或服务降级。
熔断降级:当系统出现异常情况时,Sentinel可以自动熔断系统,防止故障进一步扩散,保证系统的可用性。
系统负载保护:在系统负载高峰期间,Sentinel可以限制请求流量,避免系统资源耗尽,从而保护系统的稳定性。
实时监控:Sentinel可以实时监控系统的请求流量、响应时间、错误率等指标,帮助开发者及时发现并解决潜在问题。
25、讲述微服务架构下 Spring Cloud Alibaba 的最佳实践?(了解)
Spring Cloud Alibaba 是 Spring Cloud 体系结构的一部分,提供了一系列解决方案,用于快速构建分布式系统的微服务架构。它整合了阿里巴巴开源的中间件,如 Nacos、Sentinel、RocketMQ、Dubbo 等,以提供服务发现、配置管理、消息传递和流量控制等功能。以下是在微服务架构下使用 Spring Cloud Alibaba 的一些最佳实践:
服务发现和配置中心 - Nacos
动态服务发现:使用 Nacos 作为服务注册和发现中心,帮助微服务实例在启动时自动注册,并且在调用其他服务时可以通过服务名获取实时的服务列表。
集中化配置管理:通过 Nacos 实现集中化配置管理,动态调整微服务配置而无需重启,从而提高运维效率。
流量控制、熔断器和系统保护 - Sentinel
流量控制策略:配置合理的流量控制规则以处理突发流量和防止系统过载。
应用降级策略:当服务出现不稳定的行为时,自动进行服务降级,保护系统的整体稳定性。
实时监控和报警:结合 Sentinel 控制台实现实时监控,及时响应系统异常。
消息驱动 - RocketMQ
异步通信:使用 RocketMQ 异步处理消息,解耦服务间的通信,提高系统的扩展能力和可维护性。
可靠的消息传递:确保消息的可靠性和一致性,比如利用事务消息来实现最终一致性。
分布式事务管理 - Seata
分布式事务解决方案:对于需要跨服务共享事务状态的复杂业务流程,使用 Seata 管理分布式事务的提交或回滚。
服务间一致性保证:确保各个微服务之间数据一致性,防止系统出现数据不一致的现象。
微服务架构设计
服务拆分原则:基于业务领域对系统进行服务划分,每个微服务应有清晰的职责和边界。
API 版本管理:设计可扩展的 API 版本管理策略,确保前后端分离和服务之间的兼容性。
安全和权限控制
身份验证和授权:使用 Spring Security、JWT 或 OAuth2 等机制实现微服务的安全访问控制。
敏感数据加密:对接口的敏感信息进行加密,确保数据传输的安全性。
弹性设计和监控
服务监控和日志:利用 Spring Boot Actuator、Spring Cloud Sleuth、ELK Stack 等工具记录、监控和分析服务运行状况和日志信息。
备份和灾难恢复:制定和实行灾难恢复计划,包括数据备份和服务快速恢复。
DevOps 和持续集成/部署
持续集成/持续部署(CI/CD):整合 CI/CD 系统如 Jenkins,自动化测试和部署流程,快速迭代产品功能。
容器化和编排:使用 Docker 和 Kubernetes 管理服务的容器化部署和自动化编排。
26、分布式事务实现方式?(可根据自身情况熟悉其中一种)
两阶段提交(2PC):这是最常见的分布式事务实现方式之一。它包括两个阶段:在第一阶段,协调者向所有参与者发送prepare请求,每个参与者执行操作并返回ready消息;在第二阶段,协调者向所有参与者发送commit或者abort请求,参与者根据请求执行操作并返回结果。
TCC(Try-Confirm-Cancel):这种模式将分布式事务分为Try、Confirm和Cancel三个阶段。Try阶段执行业务,Confirm阶段提交事务,Cancel阶段在业务执行出错时回滚事务。在Java中实现TCC模式的分布式事务可以使用一些成熟的框架,如Alibaba的Seata、TCC-Transaction、ByteTCC等。
本地消息表:这种方式是在本地事务中将要执行的消息插入到本地消息表中,然后异步地由消息发送者将消息发送出去,由消息接收者消费消息。如果消息发送失败,可以重试或者回滚事务。
最大努力通知(Best Effort Delivery):这种方式是业务处理服务在业务处理完毕时,向下游服务“尽最大努力”发送应用内消息。如果消息接收方未能接收到消息,则消息发送方通过一定的机制重试发送消息,但不保证消息一定被接收方成功接收。
基于缓存(Redis)实现:使用Redis的某些命令(如SETNX、EXPIRE、DEL等)来实现锁的获取、设置过期时间和释放。
加锁:
使用SETNX命令尝试获取锁,如果key不存在(即锁未被其他客户端持有),则设置该key并返回1(表示加锁成功)。
如果key已存在(即锁已被其他客户端持有),则返回0(表示加锁失败)。
还可以使用SET命令结合NX和PX选项来实现加锁和设置过期时间。
设置过期时间:
为了防止客户端在持有锁期间崩溃而导致死锁,可以为锁设置一个合理的过期时间。这可以通过在加锁时同时使用EXPIRE命令或者在SET命令中设置PX选项来实现。
释放锁:
当客户端完成操作后,需要释放锁。这通常通过删除Redis中的key来实现。但为了避免误删其他客户端的锁,可以使用Lua脚本来确保只删除自己持有的锁。
27、什么是SchedulerX?有什么作用?(了解)
SchedulerX是阿里巴巴基于Akka架构开发的分布式调度平台。它提供了秒级、精准、高可靠、高可用的定时任务调度服务,并支持多种类型的任务调度,如简单单机任务、简单多机任务、脚本任务以及网格任务。
SchedulerX的主要作用包括:
分布式任务调度:SchedulerX可以在分布式系统中进行任务调度,支持多种调度模式和异构计算框架,提供了灵活、高效、可靠的多种调度方式。
任务编排和运维自动化:通过SchedulerX,用户可以轻松地构建分布式任务和工作流调度,实现任务编排和运维自动化。这有助于提高系统的效率和可靠性,降低运维成本。
支持多种任务类型:SchedulerX支持多种任务类型,包括Java、Shell、Python、Go脚本、HTTP以及自定义任务类型。这使得用户可以根据实际需求选择适合的任务类型进行调度。
丰富的运维能力:SchedulerX提供了丰富的运维能力,包括数据大盘(可视化查看任务执行状况)、日志查看、原地重跑、标记成功、任务停止等功能。这些功能有助于用户更好地监控和管理任务调度过程。
高可用性和容错性:SchedulerX具有高可用性和容错性,能够确保在分布式系统中稳定运行。即使某个节点出现故障,SchedulerX也能够自动进行故障转移和恢复,确保任务调度的连续性和可靠性。
28、什么是CAP理论?(了解)
CAP 原理(也称为 CAP 理论)是分布式系统中的核心概念,由计算机科学家 Eric Brewer 提出。它包含三个基本要素:
一致性(Consistency):所有节点在同一时间看到的数据是一致的,即系统的状态在任何时刻都是同步的。
可用性(Availability):每个请求都能在有限的时间内获得响应,无论响应成功还是失败。
分区容错性(Partition Tolerance):系统在遇到网络分区(即部分节点之间的通信失败)时,仍能够继续提供其服务。
CAP 原理的核心观点:在分布式系统中,无法同时完全满足一致性、可用性和分区容错性这三个特性,只能“鱼与熊掌不可兼得”,开发者需要根据具体的系统需求在其中进行权衡和取舍。
实际应用中的取舍:
CP 系统:放弃可用性,保证一致性和分区容错性。例如,分布式数据库系统在网络分区时选择拒绝请求以保证数据一致性。
AP 系统:放弃一致性,保证可用性和分区容错性。例如,NoSQL 数据库在网络分区时仍然接受请求,但可能导致数据不一致。
重要性:理解 CAP 原理有助于设计和构建可靠的分布式系统,根据业务需求选择合适的架构和技术方案。
