Spring + Springboot
目录
3. 为什么Spring解决循环依赖要用到三级缓存,二级缓存不够吗?
9.Spring 中的 ObjectFactory 是什么?
10.Spring 中的 ApplicationContext 是什么?
14.Spring AOP默认用的是什么动态代理,两者的区别?
26.@Component、@Controller、@Repository和@Service的区别?
(4)@PostConstruct 和 @PreDestroy
(9) @RequestHeader和@CookieValue
32.Spring WebFlux 是什么?它与 Spring MVC 有何不同?
37.Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?
39.SpringBoot(Spring)中为什么不推荐使用 @Autowired ?
1.什么是依赖循环?
循环依赖是指 Bean 对象循环引用,是两个或多个 Bean 之间相互持有对方的引用
举个例子,class A 中创建了个对象B,class B 中创建了个对象A
2.Spring 如何解决循环依赖?
关键就是提前暴露未完全创建完毕的 Bean。
在 Spring 中主要是使用三级缓存来解决了循环依赖:
-
一级缓存(Singleton Objects Map):用于存储完全初始化完成的单例Bean。
-
二级缓存(Eary singleton Objects Map):用于存储尚未完全初始化,但已实例化的Bean,用于提前暴露对象,避免循环依赖问题.
-
三级缓存(Singleton Factories Map):用于存储对象工厂,当需要时,可以通过工厂创建早Bean(特别是为了支持AOP代理对象的创建)。
解决步骤:
-
Spring 首先创建 Bean 实例,并将其加入三级缓存中(Factory)。
-
当一个 Bean 依赖另一个未初始化的 Bean 时,Spring 会从三级缓存中获取 Bean 的工厂,并生成该 Bean 的代理对象。
-
代理对象存入二级缓存,解决循环依赖。
-
一旦所有依赖 Bean 被完全初始化,Bean 将转移到一级缓存中。
3. 为什么Spring解决循环依赖要用到三级缓存,二级缓存不够吗?
Spring 之所以需要三级缓存而不是简单的二级缓存,主要原因在于AOP代理和Bean的早期引用问题,二级缓存虽然可以解决循环依赖的问题,但在涉及到动态代理(AOP)时,直接使用二级缓存不做任何处理会导致我们拿到的 Bean 是未代理的原始对象。如果二级缓存内存放的都是代理对象,则违反了 Bean 的生命周期。
以下是二级缓存的思路:
以下是三级缓存解决循环依赖的思路:
1.创建Bean A,从一级缓存、二级缓存、三级缓存中查找,没找到,创建BeanA,将A放入三级缓存的0biectfactory里,此时A依赖于B, 2.从一、二、三级缓存中查找B,没找到,就创建BeanB,放入三级缓存ObjectFactory里,B依赖于A 3.从一、二、三级缓存中查找A,之前A在三级缓存中存在,查找到A,将其放入到二级缓存中(创建代理A),删除三级缓存的A 4.A填充Bean B的属性,此时B是完整的了,放入一级缓存中,删除三级缓存的B 5.B填充了开始的A,A也变成完整的了,放入到一级缓存中(A查找二级缓存中的代理A,变成代理A完整实例,存入到一级缓有 中,删除二级缓存的代理A) 6.返回A(代理A)
所以:
二级缓存可以解决普通Bean的循环依赖问题,但如果涉及到代理对象的循环依赖,就需要三级缓存来存储一个工厂对象,以便在解决循环依赖时生成代理对象。这样既保证了Bean的生命周期正常,又能够处理循环依赖的情况。
4.什么是Spring 的IOC?
Spring IOC (Inversion of Control,控制反转)是 Spring 框架的核心概念之一。它是通过依赖注入(Dependency Injection)实现的IOC 让对象的创建与管理职责由容器负责,而不是由对象自身控制。 核心思想:控制反转意味着将对象的创建和依赖关系交由 Spring 容器管理,而不是由程序代码直接控制。这种机制使得程序更加灵活和解耦,提升了代码的可维护性和扩展性。 依赖注入:通过构造器注入、setter 注入或接口注入,将对象所需的依赖传递给它,而不是让对象自行创建依赖。
延伸→IOC有什么好处?
对象的创建都由 IOC 容器来控制之后,对象之间就不会有很明确的依赖关系,使得非常容易设计出松耦合的程序。
-
例如,对象 A需要依赖一个实现 B,但是对象都由 控制之后,我们不需要明确地在对象 A的代码里写死依赖的实现 B,只需要写明依赖一个接口,这样我们的代码就能顺序的编写下去。
-
然后,我们可以在配置文件里定义A依赖的具体的实现 B,根据配置文件,在创建 A的时候,IOC容器就知晓 A 依赖的 B,这时候注入这个依赖即可。
-
如果之后你有新的实现需要替换,那A的代码不需要任何改动,你只需要将配置文件A依赖 B改成 B1,这样重启之后,IOC 容器会为A注入 B1.
这样就使得类 A 和类 B解耦了,very nice! 并目也因为创建对象由 IOC 全权把控,那么我们就能很方便的让IOC基于扩展点来“"加工”对象,例如我们要代理一个对象,IOC 在对象创建完毕,直接判断下这个对象是否需要代理,如果要代理,则直接包装返回代理对象。 这等于我们只要告诉 IOC 我们要什么,IOC 就能基于我们提供的配置文件,创建符合我们需求的对象正是这个控制反转的思想,解放了我们的双手。
总结一句话来说就是:资源不由使用资源者管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。
用生活中的例子就是,老板给员工发工资,不会找员工**面对面发工资,员工也不会找老板面对面要工作,老板把钱给财务部,让财务部发工资。此时工资就是对象,财务部就是IOC容器。**
5.什么是Spring的DI?
Dl(Dependency lnjection,依赖注入)普遍认为是 Spring 框架中用于实现控制反转(IOC)的一种机制。DI 的核心思想是由容器负责对象的依赖注入,而不是由对象自行创建或查找依赖对象。通过 Dl,Spring 容器在创建一个对象时,会自动将这个对象的依赖注入进去,这样可以让对象与其依赖的对象解耦,提升系统的灵活性和可维护性。
6.什么是spring的bean?
Spring Bean 是 Spring 框架中被管理的对象。它可以是任何符合特定规范的 Java 类实例。Spring 通过配置文件或注解等方式来定义和管理这些 Bean,实现了对象的创建、初始化、依赖注入以及生命周期的管理等功能。这样可以使得应用程序的组件之间更加松散耦合,提高了代码的可维护性和可扩展性。
延伸→讲一讲bean的生命周期?
-
实例化:当 Spring 容器启动时,根据配置文件或注解,Spring 会首先实例化 Bean。
-
依赖注入:在实例化之后,Spring 容器会通过构造器、setter 方法或注解将其他 Bean 的依赖注入进来。
-
初始化:如果 Bean 实现了 InitializingBean 接口或者使用了 @PostConstruct 注解,Spring 会在依赖注入完成后调用相应的初始化方法
-
销毁:如果 Bean 实现了 DisposableBean 接口或使用了 @PreDestroy注解,Spring 会在容器关闭时调用销毁方法。
延伸→怎么定义一个bean?
-
XML 配置:早期的 Spring 应用通常通过 XML 文件定义 Bean,使用<bean>标签来指定类、构造器参数和依赖关系。
-
基于注解的配置:使用 @Component、@Service、@Repository、@Controller 等注解可以将类标记为 Spring Bean,Spring 会自动扫描这些类并将其注册为 Bean。
-
Java 配置类:通过 @Configuration 和 @Bean 注解,可以在 Java 类中手动定义 Bean。相比,XML 配置,这种方式更加简洁和类型安全
7.Spring 中的 BeanFactory 是什么?
Beanfactory 其实就是 IOC的底层容器。负责管理和配置应用中的 Bean。它是 Spring IOC (控制反转)容器的基础实现,提供了创建和管理 Bean 的基本功能。通过 BeanFactory,Spring 容器可以按照需求加载和实例化 Bean。
简单来说就是bean的工厂,负责生成bean,它只会在Bean首次请求之后才会开始实例化该bean,并不是容器一启动就开始创建所有的bean。
8.Spring中的FactoryBean是什么?
FactoryBean 是 Spring 提供的一个特殊接口,允许开发者通过自定义的逻辑创建复杂的 Bean 实例。与普通的 Bean 不同,通过FactoryBean 创建的 Bean 不一定是 FactoryBean 本身,而是它生产的对象。
延伸→它跟BeanFactory有什么区别?
Beanfactory是IOC的底层容器,用于管理和获取bean实例,
而FactoryBean
是一个特殊的bean,用于创建和管理其他bean的实例。FactoryBean
提供了更多的自定义能力,允许在bean的创建过程中进行额外的逻辑处理。
9.Spring 中的 ObjectFactory 是什么?
-
Objectfactory 是一个接口,它可以用来进行bean的延迟加载。
-
他提供getobject方法返回一个bean实例,避免在spring容器创建时就加载bean对象。
-
懒加载
-
解决循环依赖时都使用了它
10.Spring 中的 ApplicationContext 是什么?
ApplicationContext是Spring框架中的一个核心接口,代表了Spring IOC容器的高级形态,提供了比BeanFactory更丰富的功能和灵活性。它是Spring应用程序的核心,不仅负责创建和管理Bean,还提供了对Bean的全面管理以及对应用程序环境的支持。ApplicationContext继承自BeanFactory接口,并在此基础上进行了扩展,提供了更多的功能和特性,如事件传播、国际化支持、资源加载、生命周期回调等。
ApplicationContext的主要功能和特点包括:
-
Bean的管理:负责管理应用程序中的所有Bean对象,包括它们的创建、配置、生命周期管理等
-
资源的加载:可以加载和管理各种资源,如配置文件、XML文件、Properties文件等,提供了一个统一的接口方便开发者访问和使用这些资源。
-
国际化支持:提供了国际化的支持,可以根据不同的语言环境加载不同的资源文件,实现多语言支持。
-
事件的发布与监听:可以发布事件,并允许其他组件注册监听器来处理这些事件,实现了组件之间的解。
-
AOP支持:提供了对面向切面编程(AOP)的支持,允许开发者通过配置切面来实现横切关注点的模块化。
ApplicationContext有多个实现类,如ClassPathXmlApplicationcontext和FilesystemXmlApplicationContext等,适用于不同的应用场景。通过这些实现类,开发者可以根据具体需求选择合适的Applicationcontext来实现Spring应用程序的各种功能。
11.Spring Bean 一共有几种作用域?
-
singleton:默认是单例,含义不用解释了吧,一个IOC 容器内部仅此一个
-
prototype:原型,多实例
-
request:每个请求都会新建一个属于自己的 Bean 实例,这种作用域仅存在 Spring Web 应用中session:一个 http session 中有一个 bean 的实例,这种作用域仅存在 Spring Web 应用中application:整个 ServletContext 生命周期里,只有一个 bean,这种作用域仅存在 Spring Web 应用中
-
websocket:一个 WebSocket 生命周期内一个 bean 实例,这种作用域仅存在 Spring Web 应用中
12.Spring有几种注入方式?
构造器注入: Spring 倡导构造函数注入,因为构造器注入返回给客户端使用的时候一定是完整的。
public class MyService { private final MyDependency myDependency; @Autowired public MyService(MyDependency myDependency) { this.myDependency = myDependency; } }
为什么Spring提倡构造函数注入而不是使用@Autowired?
@Autowired与构造器注入区别,为什么spring推荐使用构造注入而不是Autowired?_autowired 构造器注入-CSDN博客 setter 注入:可选的注入方式,好处是在有变更的情况下,可以重新注入。
public class MyService { private MyDependency myDependency; @Autowired public void setMyDependency(MyDependency myDependency) { this.myDependency = myDependency; } }
字段注入:就是平日我们用 @Autowired标记字段
public class MyService { @Autowired private MyDependency myDependency; }
方法注入:就是平日我们用 @Autowired 标记方法
public class MyService { public void performAction(@Autowired MyDependency myDependency) { myDependency.doSomething(); } }
如果觉得构造器注入太繁琐了,还可以使用lombok的@RequireArgsConstructor注解
@RequiredArgsConstructor public class xxx { private final UserFormDao userFormDao; private final UserFormDataDao userFormDataDao; }
13.什么是AOP?
AOP是面向切面编程,是一种编程范式,用于将跨领域的关注点(如日志记录、安全检查、事务管理等)与业务逻辑分离开来。它允许开发者通过“切面”(Aspect)将这些通用功能模块化,并将其应用到应用程序中的多个地方,从而避免代码重复。 核心思想:AOP 的核心思想是将与业务逻辑无关的横切关注点抽取出来,通过声明的方式动态地应用到业务方法上,而不是将这些代码直接嵌入业务逻辑中。 主要组成部分:AOP包括几个关键概念:切面(Aspect)、连接点(JoinPoint)、通知(Advice)、切入点(Pointcut)和织入(Weaving)。
延伸→AOP有哪些应用场景?
日志记录、事务管理、安全检查、性能监控这些独立于业务逻辑开外的功能都可以用AOP。
14.Spring AOP默认用的是什么动态代理,两者的区别?
Spring:JDK动态代理。 springboot 2.x:CGLIB动态代理 区别:
-
jdk动态代理,基于接口实现,通过java.ang.reflect.proxy反射技术实现
-
cglib,基于类继承,通过字节码技术生成目标类的子类。不能代理final类和方法
jdk动态代理实现:
-
基于Java反射,只能代理实现了接口的类。首先通过InvocationHandler接口得到一个切面类,然后利用Proxy根据目标类的类加载器、接口和切面类得到一个代理类。
cglib动态代理实现:
-
基于字节码技术,可以代理所有对象。首先通过Methodlnterceptor接口得到一个拦截类,new一个enhancer,传入需要代理的类,创建callback方法并设置拦截类,再使用enhancer.create()方法返回一个代理类,同时这个代理类和方法不能被final修饰。
为什么springboot 用cglib? jdk动态代理基于接口,没有接口就会报错;而且cglib被打包到spring里面了,就直接用了。
15. 能说说 Spring 拦截链的实现吗?
在 Spring 中,拦载链通常指的是一系列拦截器(如 AOP 切面、过滤器、拦截器等)依次作用于请求或方法调用,实现横切关注点的处理比如日志记录、权限控制、事务管理等。 Spring 拦截链的核心实现包括以下几个方面:
Handlerinterceptor(MVC 拦截器):用于拦截HTTP 请求并进行预处理和后处理。通过实现 HandlerInterceptor 接口的preandle、 postHandle和afterCompletion方法,可以在请求到达控制器之前、控制器方法执行之后以及请求完成后进行处理
Filter (过滤器):基于 Servlet API的过滤器,可对请求进行初步筛选,应用于安全验证、编码过滤、跨域处理等场景。过滤器通过Filter 接口的 foFilter方法拦截请求。
AOP 拦截链(切面):Spring AOP 提供的方法级别的拦截,通过定义切面(Aspect)可以实现方法的前后处理。切面中的 @Before、@After 、@Around 等注解用于控制拦截的执行顺序。
16.说一下Bean的生命周期?
-
实例化:Spring 容器根据配置文件或注解实例化 Bean 对象。
-
属性注入:Spring 将依赖(通过构造器、setter 方法或字段注入)注入到 Bean 实例中。
-
初始化前的扩展机制:如果 Bean 实现了 BeanNameAware 等 aware 接口,则执行 aware 注入。
-
初始化前(BeanPostProcessor):在 Bean 初始化之前,可以通过 BeanPostProcessor 接囗对 Bean 进行一些额外的处理
-
初始化:调用 InitializingBean 接口的 afterPropertiesSet() 方法或通过 init-method 属性指定的初始化方法。
-
初始化后(BeanPostProcessor):在 Bean 初始化后,可以通过 BeanPostProcessor 进行进一步的处理。
-
使用 Bean:Bean 已经初始化完成,可以被容器中的其他 Bean 使用。
-
销毁:当容器关闭时,Spring 调用 DisposableBean 接口的 destroy() 方法或通过 destroy-method 属性指定的销毁方法。
17.说下对 Spring MVC 的理解?
SpringMVC是spring对mvc的实现,对传统的mvc做了扩展,将model层分为了业务模型Service和数据模型Repository,controller层拆分成了前端控制器DispatcherServlet和后端控制器Controller,以前的servlet+jsp的模式,开发要写很多的servlet,现在和Servlet解耦,简化了web开发。
Spring MVc 的工作流程可以分为以下几个关键步骤: 1.客户端请求:浏览器向服务器发送 HTTP 请求。 2.DispatcherServlet:所有的请求首先由 Spring MVC 的核心前端控制器 Dispatcherservlet 接收,它充当整个流程的调度中心。 3.处理器映射 (Handler Mapping): DispatcherServlet根据请求的 URL 使用处理器映射器找到对应的控制器(Controller)。 4.控制器(Controller):控制器接收请求并处理业务逻辑,通常通过注解 @Controller 和 @RequestMapping定义请求的映射方法, 5.模型和视图(ModelAndView):控制器处理完业务逻辑后,将教据封装到模型对象中,并指定返回的视图名称。 6.视图解析器 (ViewResolver): Dispatcherservlet 调用视图解析器,将逻辑视图名称解析为实际的视图,如 JSP、Thymeleaf 等模板引擎。 7.视图渲染:视图渲染引擎根据模型中的数据生成 HTML 页面并返回给客户端。
18.SpringMVC的父子容器你了解吗?
-
父容器是一个顶层的Spring容器,通常用于定义共享的bean和配置,并且所有的子容器都可以访问父容器中定义的bean,适合于定义全局的服务、数据源等。一般是由ContextLoaderListener去加载并初始化父容器。
-
子容器是从父容器派生出来的容器,可以定义自己的bean。子容器可以重写父容器中定义的bean,也可以添加新的bean。适合于模块化的应用程序,例如不同的模块或功能可以有自己的子容器 ,一般是由DispatcherServlet去加载并初始化子容器。
19.你知道Spring中使用了哪些设计模式吗?
-
工厂模式,从名字就能看出来是 BeanFactory,整个 Spring lOc 就是一个工厂模板方法,例如 JdbcTemplate、RestTemplate,名字是 xxxTemplate 的都是模板。
-
代理模式,AOP 整个都是代理模式。
-
单例模式,默认情况下 Bean 都是单例的。
-
责任链模式,比如 Spring MVC 中的拦截器,多个拦截器串联起来就形成了责任链,
-
观察者模式,在 Spring 中的监听器实现。
-
适配器模式,在 Spring MVC 中提到的 handlerAdapter 其实就是适配器,
20.Spring事务有几个隔离级别?
Spring 提供了五种事务隔离级别: 1.DEFAULT(默认):使用底层数据库的默认隔离级别。如果数据库没有特定的设置,通常默认为READCOMITTED。 2.READ UNCOMMITTED(读未提交):最低的隔离级别,允许事务读取尚未提交的数据,可能会导致脏读、不可重复读和幻读 3.READCOMMITTED(读已提交):仅允许读取已经提交的数据,避免了脏读,但可能会出现不可重复读和幻读问题。 4.REPEATABLE READ(可重复读):确保在同一个事务内的多次读取结果一致,避免脏读和不可重复读,但可能会有幻读问题。 5.SERIALIZABLE(可串行化):最高的隔离级别,通过强制事务按顺序执行,完全避免脏读、不可重复读和幻读,代价是性能显著下下降。
21.Spring有什么优点?
主要有以下几点(基本上答出 ioc、aop、生态和社区就差不多了): 1)轻量级和非侵入性,不需要引入大量的依赖和配置。 2)面向切面编程(AOP),Spring 提供了强大的面向切面编程的支持,允许用户定义横切关注点,并将其与核心业务逻辑分离,提高了灵活性。 3)依赖注入(DI)和控制反转(IOC)容器,Spring 的核心是 IOC 容器,它实现了依赖注入模式,通过配置文件或注解来管理对象之间的依赖关系,降低了耦合度,提高了代码的可维护性和可测试性。 4)拥有大量的生态,几乎集成市面所有 Java 技术栈的一切框架和类库。 5)Spring 拥有非常活跃的社区,官方文档丰富,网上大把教学资源,遇到问题比较容易找到对应的解决方案。
22.Spring loc 容器初始化过程?
启动、Bean 定义注册、实例化和依赖注入、初始化 1)启动阶段:
-
配置加载:加载配置文件或配置类,IOC容器首先需要加载应用程序的配置信息,这些配置信息可以是 XML 配置文件、Java 配置类或注解配置等方式。
-
创建容器:Spring 创建 IOC容器(BeanFactory、ApplicationContext),准备加载和管理 Bean。
2)Bean 定义注册阶段:
-
解析和注册:BeanDefinitionReader 读取解析配置中的 Bean 定义,并将其注册到容器中,形成 BeanDefinition 对象。
3)实例化和依赖注入:
-
实例化:根据 BeanDefinition 创建 Bean 的实例。
-
依赖注入:根据 BeanDefinition 中的依赖关系,可以通过构造函数注入、Setter 注入或字段注入,将依赖注入到 Bean 中。
4)初始化:
-
BeanPostProcessor 处理:这些处理器会在 Bean 初始化生命周期中加入定义的处理逻辑,postProcessBeforelnitialization 和postProcessAfterlnitialization 分别在 Bean 初始化前后被调用。
-
Aware 接口调用:如果 Bean 实现了 Aware 接口(如 BeanNameAware、BeanFactoryAware),Spring 会回调这些接口,传递容器相关信息。
-
初始化方法调用:调用 Bean 的初始化方法(如通过 @Postconstruct 注解标注的方法,或实现 InitializingBean 接口的 bean 会被调用afterPropertiesSet 方法)
23.Spring Bean 注册到容器有哪些方式?
Spring Bean 注册到容器的方式主要包括以下几种: 1)基于 XML的配置 使用 XML 文件配置 Bean,并定义 Bean 的依赖关系,
2)基于 @component 注解及其衍生注解 使用注解如 @component等进行配置。@Service@Controller@Repository注解
3)基于 @Configuration 和 @Bean 使用 @Configuration注解声明配置类,并使用@Bean注解定义 Bean。
4)基于 @Import 注解 @Import可以将普通类导入到 Spring 容器中,这些类会自动被注册为 Bean。
24.Spring 自动装配的方式有哪些?
自动装配指的是 Spring 可以根据一些特定的规则比如注解或者配置,自动在各个组件之间建立关联,完成依赖注入自动装配主要有以下四类: 1)no(默认):不自动装配,需要显式地定义依赖。 2)byName:通过 Bean 名称进行自动装配。 3)byType:通过 Bean 类型进行自动装配。 4)constructor:通过构造函数进行自动装配
25.@Bean和@Component有什么区别?
特性 | @Bean | @Component |
---|---|---|
使用位置 | 方法级别(在@Configuration类中) | 类级别 |
扫描机制 | 不支持自动扫描,需手动注册 | 支持自动扫描,通过@ComponentScan自动发现 |
主要用途 | 用于配置第三方库或复杂对象 | 用于自动发现并注册自定义类 |
常见场景 | 手动配置复杂对象、第三方库类 | 自定义服务、DAO层、控制器等类的自动注册 |
灵活性 | 更灵活,适合复杂初始化 | 自动化更强,适合类的简单注册 |
26.@Component、@Controller、@Repository和@Service的区别?
它们本质上没区别,并且其它三个都是 @component 的行生注解,之所以做了这些划分主要是为了更好地组织和管理应用的各个层次,提高代码的可读性和可维护性。 1)@Component:这是一个通用的注解,用于将任意类注册为 Spring 容器中的 Bean。它没有特定的语义,适用于任何需要 Spring 管理的类。 2)@Controller:这是一个专门用于 Spring MVC 中的控制器(Controller)层的注解。用于处理 HTTP 请求,并将结果返回给客户端。 3)@Service:用于标识业务逻辑层的类。它具有明确的语义,表明该类承担业务操作,主要用于编写服务类 4)@Repository:用于数据访问层(DAO)的类,与数据库交互。
区别总结:
-
@Component:通用注解,用于将任何类标记为 Spring Bean.
-
@Controller:特定于 Spring MVC,处理 Web 层请求。
-
@Service:特定于业务逻辑层,用于处理服务逻辑。
-
@Repository:特定于持久层,通常用于数据访问对象(DAO)。
27.Spring的事务在什么时候会失效?
1.方法是static修饰的静态方法。静态方法不属于任何实例,而是类级别的方法. 2.方法或者类被 final修饰。动态代理本质是继承你这个类,你都final了人家咋继承? 3.方法不是被public修饰的。你一个private的方法人家咋走代理。 4.这个类没被spring管理或者是自己new出来的。 5.类内方法自己调用。自己调用自己类的方法是不会走代理的。 6.rollbackFor使用默认的,默认的只会回滚RuntimeException和Error的异常,那些什么!0异常都不会回滚。 7.你的异常被自己try catch捕获了。你不抛出来人家咋知道报异常了 8.事务传播机制错了,比如你被调用的方法事务机制是REQUIRES NEW,会新起一个事务,两个事务没啥关联,第一个报错了第二个不回滚。 9.你用的数据库有问题,当你表用不支持事务的引擎时,比如说MyISAM,这时候当然事务不生效。
28.说一说Spring的启动过程?
1)加载配置文件,初始化容器: Spring 启动时首先会读取配置文件(如 XML 配置文件、JavaConfig 类等),包括配置数据库连接、事务管理、AOP 配置等
2)实例化容器: Spring 根据配置文件中的信息创建容器 ApplicationContext,在容器启动阶段实例化BeanFactory,并加载容器中的 BeanDefinitions.
3)解析 BeanDefinitions: Spring 容器会解析配置文件中的 BeanDefinitions,即声明的 Bean 元数据,包括 Bean 的作用域、依赖关系等信息.
4)实例化 Bean: Spring 根据 BeanDefinitions 实例化 Bean 对象,将其放入容器管理。
5)注入依赖: Spring 进行依赖注入,将 Bean 之间的依赖关系进行注入,包括构造函数注入、属性注入等。
6)处理 Bean 生命周期初始化方法: Spring 调用 Bean 初始化方法(如果有定义的话),对 Bean 进行初始化。 如果 Bean 实现了 InitializingBean 接囗,Spring 会调用其 afterPropertiesset 方法。
7)处理 BeanPostProcessors: 容器定义了很多 BeanPostProcessor,处理其中的自定义逻辑,例如 postProcessBeforelnitialization 会在 Bean 初始化前调用postProcessAfterlnitialization 则在之后调用。 Spring AOP 代理也在这个阶段生成。
8)发布事件: Spring 可能会在启动过程中发布一些事件,比如容器启动事件
9)完成启动: 当所有 Bean 初始化完毕、依赖注入完成、AOP 配置生效等都准备就绪时,Spring 容器启动完成.。
29.Spring 的单例 Bean 是否有并发安全问题?
存在线程安全问题,单例模式下Bean在应用程序中只有一个,如果多线程情况下,访问的都是一个Bean,如果Bean中存在实例变量和非线程安全资源,会引发线程安全问题。 解决方案: 1.避免在单例Bean中使用可变状态,确保单例Bean是无状态的,或者使用线程安全的数据结构 2.使用@Scope(“prototype”),保证每个线程访问的时候都是创建了一个新的Bean 3.加锁,如使用Synchornized关键字 4.使用ThreadLocal保存变量
30.你知道xxx注解的作用吗?
(1)@Primary
当容器中存在多个相同类型的Bean时,优先注入标记了Primary注解的Bean
(2)@Value
@Value用于将外部化的配置值注入到到Bean中,例如:
app.name = MyApp app.version = 1.0.0
import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class AppConfig { @Value("${app.name}") private String appName; @Value("${app.version}") private String appVersion; public String getAppName() { return appName; } public String getAppVersion() { return appVersion; } }
(3)@Profile
在 Spring 框架中,@Profile 注解用于定义一组 Bean 的配置文件所属的环境,比如 dev 通常表示开发环境,prod 表示生产环境.例如:
@Configuration @Profile("dev") // 这个配置类只在 "dev" profile激活时加载 public class DevConfig { @Bean public DataSource dataSource() { // 返回开发环境的DataSource return new EmbeddedDatabaseDataSource(); } } @Configuration @Profile("prod") // 这个配置类只在 "prod" profile激活时加载 public class ProdConfig { @Bean public DataSource dataSource() { // 返回生产环境的DataSource return new SomeProductionDataSource(); } }
(4)@PostConstruct 和 @PreDestroy
@Postconstruct 和 @PreDestroy 是Java 标准(JSR-250)中的注解,Spring 通过它们来管理 Bean 的生命周期。
-
@Postconstruct:用于标记方法,当依赖注入完成后,在 Bean 初始化完成后调用,这个方法会自动被调用。常用于进行初始化逻辑
-
@Prepestroy :用于标记方法,在 Bean 即将被销毁时调用。常用于进行资源的释放、清理工作。
(5)@RequestBody和@ResponseBody
-
@RequestBody:用于将从请求体中接受的json数据转换为java对象
-
@ResponseBody:用于将java对象转换为json数据并写入响应体中
(6)@Pathvariable
@Pathvariable用于从请求路径中捕获变量,例:/user/{id}
(7)@ExceptionHandler
@ExceptionHandler 是 Spring MVC 中用于处理控制器方法中抛出的异常的注解。它允许开发者定义一个专门用于处理特定异常类型的方法,避免将异常信息直接暴露给客户端,进而返回更加友好或定制的响应。
(8)@Responsestatus
@Responsestatus 注解用于定义一个方法或者异常类所对应的 HTTP 状态码和原因短语。当你使用这个注解时,Spring 会将该注解指定的状态码和原因设置到响应的 HTTP 状态行中。
例如:
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "网页未找到") public class UserNotFoundException extends RuntimeException { // 异常类实现 }
(9) @RequestHeader和@CookieValue
@RequestHeader注解用于获取请求头中的参数,并将其注入到controller方法参数中
@GetMapping("/header-info") public String getHeaderInfo(@RequestHeader("User-Agent") String userAgent) { // 使用 userAgent 进行业务处理 return "headerInfoView"; }
@CookieValue则是用于获取cookie的值
@GetMapping("/cookie-info") public String getCookieInfo(@CookieValue("sessionId") String sessionId) { // 使用 sessionId 进行业务处理 return "cookieInfoView"; }
(10)@Schedule
这个注解用于在 Spring 应用中定时执行方法。它可以将某个方法标记为一个定时任务,并根据设定的时间间隔、固定速率、或 Cron 表达式来定时触发该方法的执行。
(11)@Cacheable和@cacheEvict
1.@Cacheable:用于将方法的返回结果缓存起来。下次再调用相同参数的方法时,直接从缓存中获取结果,而不是重新执行该方法。 2.@cacheEvict :用于从缓存中移除一项或多项数据,通常在更新或删除操作时使用,确保缓存中的数据保持一致性。
(12)@Conditional
作用于有条件地装配 Bean。可以根据特定的条件来决定某个 Bean 是否应该被加载到 Spring 容器中。例如可以根据环境 (如开发、测试.生产)或特定上下文条件动态装配 Bean,实现动态加载。
(13)@lazy
@Lazy 的两种用法: 1.和注解 @Component 或 @Bean 一起使用,可以延迟 Bean 的创建时机,当用到这个 Bean 的时候(依赖注入、从 Beanfactory 直接获取),才进行创建。 2.和注解 @Autowire 一起使用,Spring 将注入一个代理类。
(14)@PropertySourse
@propertySourse主要用于从外部读取配置,加载到spring环境。
@Configuration @PropertySource("classpath:${config.location:application.properties}") public class AppConfig { }
(15) @EventListener
@EventListener主要用于监听和处理事件,其标注在方法上,可以使方法监听特定类型的事件,并在发布事件时执行。
31.Spring和Spring MVC的区别是什么?
Spring 是基础,Spring MVC 构建于 Spring 核心之上,利用其提供的容器管理、依赖注入、AOP 等功能来实现 Web 层的处理。
Spring MVC 通过前端控制器(DispatcherServlet)拦截所有的请求,并将请求分发给合适的控制器进行处理: 1.DispatcherServlet 拦截请求。
-
HandlerMapping 根据请求 URL 查找对应的控制器。
3.Controller 处理业务逻辑,并返回数据。
4.ViewResolver 决定渲染哪个视图模板。
5.将响应返回给客户端。
32.Spring WebFlux 是什么?它与 Spring MVC 有何不同?
Spring WebFlux:
-
异步非阻塞框架:Spring WebFlux 是 Spring5引入的响应式 Web 框架,旨在支持异步非阻塞编程模型。
-
基于 Reactor: Webflux基于 Reactor 库,支持响应式流(Reactive Streams)规范,使用 Mono 和 Flux 来表示单个和多个异步序列。
-
适用于高并发:WebFlux 适用于需要处理大量并发请求的场景,如实时数据流和高负载应用。
Spring MVC:
-
同步阻塞框架:Spring MVC 是一个基于 Servlet 的传统 Web 框架,使用同步阻塞模型处理请求
-
基于 Servlet APl:Spring MVC 使用标准的 Servlet API,通常每个请求对应一个线程。
-
广泛应用:Spring MVC 适用于大多数 Web 应用,特别是传统的 CRUD 操作和企业应用。
适用场景:
-
Spring MVc:适用于 I/O操作较少、请求数相对较少的应用。
-
Spring WebFlux:适用于I/O操作频繁、高并发、低延迟的应用。
33.介绍一下Spring MVC的核心组件?
1.DispatcherServlet:Spring MVC 的前端控制器,所有请求都会先由它接收并分发到相应的处理器。它是整个 Spring MVC 流程的入口,也是处理请求的调度中心。
2.HandlerMapping:根据请求 URL查找对应的处理器(Controller)。 HandlerMapping 用于将客户端的请求映射到具体的控制器方法。
3.Controller (控制器):负责处理用户请求的核心组件。Controller 接收请求后,调用业务逻辑处理,返回数据给前端
4.ModelAndView:Controller 返回的对象,包含模型数据和视图信息,表示返回给用户的页面或 API响应。
5.ViewResolver:用于解析视图名称并找到实际的视图(如JSP、Thymeleaf等),然后将处理结果渲染为 HTML 或其他格式,发送给客户端。
6.Handlerinterceptor(拦截器):在请求处理的各个阶段拦截 HTTP 请求和响应,可以在控制器执行前后添加自定义逻辑,如日志记录、权限检查等。
7.HandlerAdapter:负责执行找到的处理器(Controller),将请求交给处理器进行业务逻辑的处理。
核心工作流程:
1.DispatcherServlet 接收请求并调用 HandlerMapping 查找对应的控制器
2.HandlerAdapter 调用具体的 Controller 方法处理请求.。
3.Controller 返回 ModelAndView,包括视图名和模型数据。
4.ViewResolver 解析视图并渲染结果。
5.最终,DispatcherServlet 将处理结果返回给客户端
34.说一说Springboot的启动流程
1.从main方法启动,调用SpringApplication.run()方法
2.先会创建SpringApplication对象,创建的时候会推断应用类型(判断是servlet应用,还是reactive应用,或者不是web应用)设置启动监听器
3.创建完SpringApplication之后,调用该对象的run方法,通过ConfigurableEnvironment准备环境,这一步会读取配置文件,例application.preperties
4.创建应用上下文,这一步会注册所有配置类和自动配置类
5.刷新应用上下文,这一步会进行bean的创建和初始化,包括开发者自定义的bean以及自动注入的bean
6.对于web应用,刷新应用上下文的最后,会自动启动嵌入式web服务器
7.服务器启动完成会发送应用已启动的事件
8.接着调用实现了CommandLineRunner或者ApplicationRunner接囗的bean,执行一些初始化逻辑
9.发送ApplicationReadyEvent,应用启动完成
核心步骤如下:
1.SpringApplication.run(:这是启动的入口,它会创建 Spring 应用上下文,并执行自动配置。
2.创建应用上下文:为 Web 应用创建 AnnotationConfigServletWebServerApplicationContext 上下文.
3.启动内嵌 Web 服务器:在 refreshContext() 阶段启动内嵌的 Web 服务器(如 Tomcat)。
4.自动配置:通过 @EnableAutoConfiguration 自动配置各种组件,如 DispatcherServlet。
5.请求处理:内嵌的 DispatcherServlet 负责处理 HTTP 请求。
35.为什么使用springboot
-
简化开发:Spring Boot通过提供一系列的开箱即用的组件和自动配置,开发人员可以更专注于业务逻辑的实现,而不需要花费过多时间在繁琐的配置上。
-
快速启动:springboot可通过内嵌的Tomcat等容器快速启动应用程序,无需额外的部署步骤,方便快捷。
-
自动化配置:Spring Boot通过自动配置功能,根据项目中的依赖关系和约定俗成的规则来配置应用程序,减少了配置的复杂性,简化开发。
36.SpringBoot是如何实现自动装配的?
自动配置是通过@EnableAutoConfiguration注解实现的,这个注解会通过@import注解导入AtuoConfigurationlmportSelector类,这个类会扫描META-INF/Spring.factories文件,这个文件里面定义了所有的自动配置类,这些配置类上可能存在条件注解spring boot启动的时候根据这些注解,加载这些自动配置类。
1.自定义自动配置类步骤
定义一个类使用configuration注解修饰;定义META-INF/Spring-factories文件,向EnableAutoConfiguration中注入这个类
2.禁用特定的自动配置
application.properties配置文件中,使用spring.autoconfigure.exclude指定要禁用的自动配置类main启动类上,使用@SpringBootApplication的exclue属性进行排除
37.Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?
spring boot的jar除了包含源代码以及依赖之外,还包含运行所需要的环境,例如Tomcat服务器和运行代码所需要的依赖,能独立运行。 普通的jar只有源代码和依赖,不能直接独立运行。怎么打包?使用maven插件,运行mvn clean package命令
38.在 Spring Boot 中你是怎么使用拦截器的?
1.实现Handlerlnterceptor接口,并且实现preHandle,postHandle,afterCompletion方法
2.实现WebMvcConfigurer接口,并且实现addInterceptors方法,注入上面自定义的拦截器
常用于:权限校验,日志记录等
-
preHandle:在请求到达控制器之前,进行拦截处理。常用与权限校验
-
postHandle:在请求经过控制器处理,还没有进行染视图之前。可以对响应进行修改afterCompletion:在视图完成渲染之后。可以用于进行一些清理操作。
多个拦截器调用顺序:preHandle会根据拦截器定义的顺序,依次进行调用。postHandle和afterCompletion会按照定义的顺序逆序进行调用。
Filter和Interceptor区别:Filter优先级比Interceptor高
39.SpringBoot(Spring)中为什么不推荐使用 @Autowired ?
详情请看我的另一篇文章:@Autowired与构造器注入区别,为什么spring推荐使用构造注入而不是Autowired?_构造器注入和autowired区别-CSDN博客
40.怎么理解SpringBoot中的约定大于配置?
-
自动化配置:Spring Boot 提供了大量的自动化配置,通过分析项目的依赖和环境,自动配置应用程序的行为。例如,Spring Boot 可以根据项目中引入的数据库依赖自动配置数据源。
-
默认配置:Spring Boot 在没有明确配置的情况下,会使用合理的默认值来初始化应用程序。
-
约定优于配置:Spring Boot 遵循了约定优于配置的设计哲学,即通过约定好的方式来提供默认行为,减少开发者需要做出的决策。它是一种设计理念,通过减少配置和提供合理的默认值,让开发者快速构建部署一个程序,简化开发和降低维护成本。