Bootstrap

spring万字面试题汇总

Spring + Springboot

目录

1.什么是依赖循环?

2.Spring 如何解决循环依赖?

3. 为什么Spring解决循环依赖要用到三级缓存,二级缓存不够吗?

4.什么是Spring 的IOC?

5.什么是Spring的DI?

6.什么是spring的bean?

7.Spring 中的 BeanFactory 是什么?

8.Spring中的FactoryBean是什么?

9.Spring 中的 ObjectFactory 是什么?

10.Spring 中的 ApplicationContext 是什么?

11.Spring Bean 一共有几种作用域?

12.Spring有几种注入方式?

13.什么是AOP?

14.Spring AOP默认用的是什么动态代理,两者的区别?

15. 能说说 Spring 拦截链的实现吗?

16.说一下Bean的生命周期?

17.说下对 Spring MVC 的理解?

18.SpringMVC的父子容器你了解吗?

19.你知道Spring中使用了哪些设计模式吗?

20.Spring事务有几个隔离级别?

21.Spring有什么优点?

22.Spring loc 容器初始化过程?

23.Spring Bean 注册到容器有哪些方式?

24.Spring 自动装配的方式有哪些?

25.@Bean和@Component有什么区别?

26.@Component、@Controller、@Repository和@Service的区别?

27.Spring的事务在什么时候会失效?

28.说一说Spring的启动过程?

29.Spring 的单例 Bean 是否有并发安全问题?

30.你知道xxx注解的作用吗?

(1)@Primary

(2)@Value

(3)@Profile

(4)@PostConstruct 和 @PreDestroy

(5)@RequestBody和@ResponseBody

(6)@Pathvariable

(7)@ExceptionHandler

(8)@Responsestatus

(9) @RequestHeader和@CookieValue

(10)@Schedule

(11)@Cacheable和@cacheEvict

(12)@Conditional

(13)@lazy

(14)@PropertySourse

(15) @EventListener

31.Spring和Spring MVC的区别是什么?

32.Spring WebFlux 是什么?它与 Spring MVC 有何不同?

33.介绍一下Spring MVC的核心组件?

34.说一说Springboot的启动流程

35.为什么使用springboot

36.SpringBoot是如何实现自动装配的?

37.Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?

38.在 Spring Boot 中你是怎么使用拦截器的?

39.SpringBoot(Spring)中为什么不推荐使用 @Autowired ?

40.怎么理解SpringBoot中的约定大于配置?


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 将转移到一级缓存中。

img

3. 为什么Spring解决循环依赖要用到三级缓存,二级缓存不够吗?

Spring 之所以需要三级缓存而不是简单的二级缓存,主要原因在于AOP代理和Bean的早期引用问题,二级缓存虽然可以解决循环依赖的问题,但在涉及到动态代理(AOP)时,直接使用二级缓存不做任何处理会导致我们拿到的 Bean 是未代理的原始对象。如果二级缓存内存放的都是代理对象,则违反了 Bean 的生命周期。

以下是二级缓存的思路:

img

以下是三级缓存解决循环依赖的思路:

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 容器来控制之后,对象之间就不会有很明确的依赖关系,使得非常容易设计出松耦合的程序。

  1. 例如,对象 A需要依赖一个实现 B,但是对象都由 控制之后,我们不需要明确地在对象 A的代码里写死依赖的实现 B,只需要写明依赖一个接口,这样我们的代码就能顺序的编写下去。

  2. 然后,我们可以在配置文件里定义A依赖的具体的实现 B,根据配置文件,在创建 A的时候,IOC容器就知晓 A 依赖的 B,这时候注入这个依赖即可。

  3. 如果之后你有新的实现需要替换,那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的生命周期?

  1. 实例化:当 Spring 容器启动时,根据配置文件或注解,Spring 会首先实例化 Bean。

  2. 依赖注入:在实例化之后,Spring 容器会通过构造器、setter 方法或注解将其他 Bean 的依赖注入进来。

  3. 初始化:如果 Bean 实现了 InitializingBean 接口或者使用了 @PostConstruct 注解,Spring 会在依赖注入完成后调用相应的初始化方法

  4. 销毁:如果 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的生命周期?

  1. 实例化:Spring 容器根据配置文件或注解实例化 Bean 对象。

  2. 属性注入:Spring 将依赖(通过构造器、setter 方法或字段注入)注入到 Bean 实例中。

  3. 初始化前的扩展机制:如果 Bean 实现了 BeanNameAware 等 aware 接口,则执行 aware 注入。

  4. 初始化前(BeanPostProcessor):在 Bean 初始化之前,可以通过 BeanPostProcessor 接囗对 Bean 进行一些额外的处理

  5. 初始化:调用 InitializingBean 接口的 afterPropertiesSet() 方法或通过 init-method 属性指定的初始化方法。

  6. 初始化后(BeanPostProcessor):在 Bean 初始化后,可以通过 BeanPostProcessor 进行进一步的处理。

  7. 使用 Bean:Bean 已经初始化完成,可以被容器中的其他 Bean 使用。

  8. 销毁:当容器关闭时,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 拦截请求。

  1. 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

  1. 简化开发:Spring Boot通过提供一系列的开箱即用的组件和自动配置,开发人员可以更专注于业务逻辑的实现,而不需要花费过多时间在繁琐的配置上。

  2. 快速启动:springboot可通过内嵌的Tomcat等容器快速启动应用程序,无需额外的部署步骤,方便快捷。

  3. 自动化配置: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接口,并且实现preHandlepostHandleafterCompletion方法

2.实现WebMvcConfigurer接口,并且实现addInterceptors方法,注入上面自定义的拦截器

常用于:权限校验,日志记录等

  • preHandle:在请求到达控制器之前,进行拦截处理。常用与权限校验

  • postHandle:在请求经过控制器处理,还没有进行染视图之前。可以对响应进行修改afterCompletion:在视图完成渲染之后。可以用于进行一些清理操作。

多个拦截器调用顺序:preHandle会根据拦截器定义的顺序,依次进行调用。postHandle和afterCompletion会按照定义的顺序逆序进行调用。

Filter和Interceptor区别:Filter优先级比Interceptor高

39.SpringBoot(Spring)中为什么不推荐使用 @Autowired ?

详情请看我的另一篇文章:@Autowired与构造器注入区别,为什么spring推荐使用构造注入而不是Autowired?_构造器注入和autowired区别-CSDN博客

40.怎么理解SpringBoot中的约定大于配置?

  1. 自动化配置:Spring Boot 提供了大量的自动化配置,通过分析项目的依赖和环境,自动配置应用程序的行为。例如,Spring Boot 可以根据项目中引入的数据库依赖自动配置数据源。

  2. 默认配置:Spring Boot 在没有明确配置的情况下,会使用合理的默认值来初始化应用程序。

  3. 约定优于配置:Spring Boot 遵循了约定优于配置的设计哲学,即通过约定好的方式来提供默认行为,减少开发者需要做出的决策。它是一种设计理念,通过减少配置和提供合理的默认值,让开发者快速构建部署一个程序,简化开发和降低维护成本。

;