Spring八股文整理

58 阅读14分钟

参考spring视频up:孙哥
孙哥主页

Spring基础

什么是 Spring 框架?

Spring 是一款开源的轻量级 Java 开发框架,旨在提高开发人员的开发效率以及系统的可维护性。 **我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发,比如说 Spring 支持 IoC(Inversion of Control:控制反转) 和 AOP(Aspect-Oriented **Programming:面向切面编程)、可以很方便地对数据库进行访问、可以很方便地集成第三方组件(电子邮件,任务,调度,缓存等等)、对单元测试支持比较好、支持 RESTful Java 应用程序的开发。

Spring 包含的模块有哪些?

:::info 主要包括 Core Container,这是Spring 的核心模块,主要提供控制反转和依赖注入的功能,Spring的其他功能基本都需要依赖于这个模块 ,其中

  • Spring-core :Spring框架的核心工具类,主要提供IOC和DI功能的支持
  • Spring-Beans:提供对Bean的创建,配置和管理
  • Spring-context:提供对于国际化,事件传播,资源加载等功能的支持
  • Spring-expression:提供对表达式语言(Spring Expression Language) SpEL 的支持,只依赖于 core 模块,不依赖于其他模块,可以单独使用。

AOP:面向切面编程,以切面为基本的功能的开发,通过切面直接的彼此协调,相互调用,完成程序的构建:

  • spring-aspects:该模块为与 AspectJ 的集成提供支持。
  • spring-aop:提供了面向切面的编程实现。
  • spring-instrument:提供了为 JVM 添加代理(agent)的功能。 具体来讲,它为 Tomcat 提供了一个织入代理,能够为 Tomcat 传递类文 件,就像这些文件是被类加载器加载的一样。没有理解也没关系,这个模块的使用场景非常有限

Spring-web

  • spring-web:对 Web 功能的实现提供一些最基础的支持。
  • spring-webmvc:提供对 Spring MVC 的实现。
  • spring-websocket:提供了对 WebSocket 的支持,WebSocket 可以让客户端和服务端进行双向通信。
  • spring-webflux:提供对 WebFlux 的支持。WebFlux 是 Spring Framework 5.0 中引入的新的响应式框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步。

Spring Test

Spring 团队提倡测试驱动开发(TDD)。有了控制反转 (IoC)的帮助,单元测试和集成测试变得更简单。 :::

Spring,Spring MVC,Spring Boot 之间什么关系?

Spring包括了很多功能模块,最重要的就是Spring-Core,提供了控制反转和依赖注入的功能,Spring的其他模块都是依赖于这个模块,

Spring MCV 是Spring中一个重要的模块,赋予Spring快速搭建Web程序的能力MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。

Spring Boot 只是简化了配置,如果你需要构建 MVC 架构的 Web 程序,你还是需要使用 Spring MVC 作为 MVC 框架,只是说 Spring Boot 帮你简化了 Spring MVC 的很多配置,真正做到开箱即用!

Spring IOC

谈谈对IoC的理解

IOC 叫做控制反转,是一种设计思想 控制:创建对象的权力 反转:将创建对象的权力交给Spring容器 ioc,将对象之间的相互依赖交给IoC容器来管理,并由IoC容器完成对对象的注入,这样很大程度简化了应用开发,把负复杂的依赖关系解放出来,同时降低了耦合。IoC容器就像一个工厂,当我们需要创建一个对象,或依赖其他对象的时候,只需要配置好配置文件或加上相应的注解即可

什么是Spring Bean

Bean指的是交给Spring容器帮我们管理的对象,我们需要通过XML配置文件或者注解的方式告诉Spring容器帮我们管理哪些对象

<!-- Constructor-arg with 'value' attribute -->
<bean id="..." class="...">
   <constructor-arg value="..."/>
</bean>

将一个类声明为Bean的注解有哪些

  • @Component:通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。
  • @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
  • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
  • @Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。

@Component和@Bean的区别

  • @Component 注解作用于类,而@Bean注解作用于方法。
  • @Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。
  • @Bean 注解比 @Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。
@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }

}
//下面这个例子是通过 @Component 无法实现的。

@Bean
public OneService getService(status) {
    case (status)  {
        when 1:
                return new serviceImpl1();
        when 2:
                return new serviceImpl2();
        when 3:
                return new serviceImpl3();
    }
}


注入Bean的注解

Spring 内置的 @Autowired 以及 JDK 内置的 @Resource 和 @Inject 都可以用于注入 Bean

@Autowired 和 @Resource 的区别是什么?

Autowired 属于 Spring 内置的注解,默认的注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)。 Resource属于 JDK 提供的注解,默认注入方式为 byName。如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType。

当一个接口存在多个实现类的情况下,@Autowired 和@Resource都需要通过名称才能正确匹配到对应的 Bean。Autowired 可以通过 @Qualifier 注解来显式指定名称,@Resource可以通过 name 属性来显式指定名称。

Bean 的作用域有哪些?

  • singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
  • prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例。
  • request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
  • session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
  • application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
  • websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。

Bean是线程安全的吗?

prototype 作用域下,每次获取都会创建一个新的 bean 实例,不存在资源竞争问题,所以不存在线程安全问题。singleton 作用域下,IoC 容器中只有唯一的 bean 实例,可能会存在资源竞争问题(取决于 Bean 是否有状态)。如果这个 bean 是有状态的话,那就存在线程安全问题(有状态 Bean 是指包含可变的成员变量的对象)。 不过,大部分 Bean 实际都是无状态(没有定义可变的成员变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。 对于有状态单例 Bean 的线程安全问题,常见的有两种解决办法:

  1. 在 Bean 中尽量避免定义可变的成员变量。
  2. 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。

Bean的生命周期

在Spring框架中,Bean的生命周期包括以下阶段:

  1. 实例化(Instantiation):在容器启动时或根据需要创建Bean的实例。这通常涉及到使用构造函数或工厂方法来实例化Bean对象。
  2. 属性设置(Property Setting):在Bean实例创建后,Spring容器会将属性值注入到Bean中。这包括使用setter方法或字段注入。
  3. 初始化(Initialization):在Bean的属性设置完成后,Spring容器会调用初始化方法,这通常包括两种方式:
  • 调用Bean中的init方法,可以使用init-method属性来配置。
  • 实现InitializingBean接口并覆盖afterPropertiesSet方法。
  1. 使用(In Use):Bean实例可以在容器中被其他Bean或应用程序组件引用和使用。
  2. 销毁(Destruction):当容器关闭时或者根据需要,Spring容器可以调用Bean的销毁方法来释放资源,这通常包括两种方式:
  • 调用Bean中的destroy方法,可以使用destroy-method属性来配置。
  • 实现DisposableBean接口并覆盖destroy方法。

在XML配置中,可以使用<bean>元素的init-methoddestroy-method属性来指定初始化和销毁方法。

以下是一个示例Bean的生命周期:

<bean id="myBean" class="com.example.MyBean" init-method="initMethod" destroy-method="destroyMethod">
    <property name="name" value="John"/>
</bean>

在这个示例中,myBean Bean会被实例化,属性name会被设置,然后initMethod方法会被调用以初始化Bean。当容器关闭时,destroyMethod方法会被调用以销毁Bean。

需要注意的是,并非所有Bean都需要初始化和销毁方法,这取决于Bean的具体需求。有时,Bean可以只是简单地创建和设置属性,而无需显式定义初始化和销毁方法。

Spring AOP

谈谈自己对于 AOP 的了解

AOP(面向切面编程)能够将那些与实际业务无关,但又需要增加的额外功能,比如日志,性能,事务 ,将这些额外功能给封装起来,便于减少重复代码,减低代码之间的耦合度 SpringAOP是基于动态代理实现的,实现MethodInterceptor 这类接口,重写invoke方法,那么AOP ,默认就会通过JDK的 代理Proxy,去帮我们创建代理对象,它是通过实现原始类的相同接口来完成的,如果我们要对没有实现接口的对象进行 JDK proxy,就得修改配置文件 AOP的参数,修改Spring为我们创建代理对象的方法,改成Cjlib,他是通过继承原始类的方式,帮我们创建代理对象。

Spring AOP 和 AspectJ AOP 有什么区别

Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。 Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单, 如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比 Spring AOP 快很多。

AspectJ 定义的通知类型有哪些?

  • AspectJ支持多种通知类型,它们与Spring AOP通知类型类似,但更灵活和强大。以下是一些主要的AspectJ通知类型以及它们的实现方法:
  1. 前置通知(Before Advice)
  • 实现方法:使用@Before注解或before()关键字。前置通知在目标方法执行之前执行。
@Before("execution(* com.example.MyService.*(..))")
public void beforeAdvice() {
    // 在目标方法执行前执行的逻辑
}
  1. 后置通知(After Advice)
  • 实现方法:使用@After注解或after()关键字。后置通知在目标方法执行之后执行,不管方法是否正常返回或抛出异常。
@After("execution(* com.example.MyService.*(..))")
public void afterAdvice() {
    // 在目标方法执行后执行的逻辑
}
  1. 返回通知(After Returning Advice)
  • 实现方法:使用@AfterReturning注解或after returning()关键字。返回通知在目标方法成功执行并返回结果后执行。
@AfterReturning(pointcut = "execution(* com.example.MyService.*(..))", returning = "result")
public void afterReturningAdvice(Object result) {
    // 在目标方法返回结果后执行的逻辑
}
  1. 异常通知(After Throwing Advice)
  • 实现方法:使用@AfterThrowing注解或after throwing()关键字。异常通知在目标方法抛出异常后执行。
@AfterThrowing(pointcut = "execution(* com.example.MyService.*(..))", throwing = "ex")
public void afterThrowingAdvice(Exception ex) {
    // 在目标方法抛出异常后执行的逻辑
}
  1. 环绕通知(Around Advice)
  • 实现方法:使用@Around注解或around()关键字。环绕通知是最强大的通知类型,它可以完全控制目标方法的执行,包括是否执行、何时执行以及如何执行。
@Around("execution(* com.example.MyService.*(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
    // 在目标方法之前执行的逻辑
    Object result = joinPoint.proceed(); // 手动调用目标方法
    // 在目标方法之后执行的逻辑
    return result;
}

这些通知类型允许您在不同的连接点上定义不同类型的通知,以实现特定的切面需求。AspectJ通知可以与切入点表达式结合使用,以指定在哪些连接点上应用它们。AspectJ提供了丰富的切入点表达式语言,可以匹配各种连接点,从而实现更灵活的切面。

多个切面的执行顺序如何控制?

通常使用@Order 注解直接定义切面顺序

// 值越小优先级越高
@Order(3)
@Component
@Aspect
public class LoggingAspect implements Ordered {

2、实现Ordered 接口重写 getOrder 方法。

@Component
@Aspect
public class LoggingAspect implements Ordered {

    // ....

    @Override
    public int getOrder() {
        // 返回值越小优先级越高
        return 1;
    }
}

Srping MVC

说说你对Spring MVC的了解

首先MVC是 模型(Model),视图(View),控制器(Controller)的简写,其核心思想就是通过将业务逻辑,数据,显示分离来组织代码

Model1 时代 这个模式下 JSP 即是控制层(Controller)又是表现层(View)。显而易见,这种模式存在很多问题。比如控制逻辑和表现逻辑混杂在一起,导致代码重用率极低;再比如前端和后端相互依赖,难以进行测试维护并且开发效率极低。 Model2时代 将控制器与显示分离,也就是早期的MVC开发模式 Java Bean(Model)+ JSP(View)+Servlet(Controller) ”这种开发模式 Spring MVC时代 随着 Spring 轻量级开发框架的流行,Spring 生态圈出现了 Spring MVC 框架, Spring MVC 是当前最优秀的 MVC 框架。相比于 Struts2 , Spring MVC 使用更加简单和方便,开发效率更高,并且 Spring MVC 运行速度更快。 MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring MVC 可以帮助我们进行更简洁的 Web 层的开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。

Spring MVC的核心组件

:::info

  • DispatcherServlet核心的中央处理器,负责接收请求、分发,并给予客户端响应。
  • HandlerMapping处理器映射器,根据 URL 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。
  • HandlerAdapter处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler;
  • Handler请求处理器,处理实际请求的处理器。
  • ViewResolver视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端 :::

Spring MVC工作原理

:::info

  • 客户端(浏览器)发送请求, DispatcherServlet拦截请求。
  • DispatcherServlet 根据请求信息调用 HandlerMapping 。HandlerMapping 根据 URL 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。
  • DispatcherServlet 调用 HandlerAdapter适配器执行 Handler 。
  • Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServlet,ModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View。
  • ViewResolver 会根据逻辑 View 查找实际的 View。
  • DispaterServlet 把返回的 Model 传给 View(视图渲染)。
  • 把 View 返回给请求者(浏览器) :::

统一异常处理怎么做?

推荐使用注解的方式统一异常处理,具体会使用到 @ControllerAdvice + @ExceptionHandler 这两个注解 。

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    @ExceptionHandler(BaseException.class)
    public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {
      //......
    }

    @ExceptionHandler(value = ResourceNotFoundException.class)
    public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
      //......
    }
}