当前位置: 首页 > news >正文

Java八股文——Spring「SpringBoot 篇」

为什么使用 Spring Boot

面试官您好,对我来说,Spring Boot并不仅仅是Spring框架的一个升级,它是一次颠覆性的革命。它的核心目标,就是为了将开发者从繁重、易错的Spring配置和依赖管理中彻底解放出来,让我们能够以极快的速度搭建和开发生产级的、可独立运行的应用程序。

要理解它的价值,我们可以先回忆一下传统Spring开发的痛点

  • 繁琐的XML配置:我们需要编写大量的XML文件来定义Bean、配置AOP、事务、MVC等。
  • 复杂的依赖管理:我们需要手动地在pom.xml中添加大量的依赖,并且要小心翼翼地处理不同依赖之间的版本兼容性问题,这非常容易出错,被称为“依赖地狱”。

Spring Boot正是为了解决这些痛痛点而生的。它的核心优势,正如您所说,主要体现在以下几个方面,而这些优势背后都有其精巧的实现原理。

1. 简化开发 —— 核心是“起步依赖” (Starters)
  • 痛点解决:彻底解决了“依赖地狱”问题。
  • 实现原理:Spring Boot提供了一系列名为 spring-boot-starter-* 的“起步依赖”。比如,我们想开发一个Web应用,只需要在Maven或Gradle中引入spring-boot-starter-web这一个依赖就够了。
  • 它做了什么? 这个starter就像一个“全家桶”,它会通过Maven的依赖传递机制,自动地把开发一个Web应用所需要的所有常用库(如spring-webmvc, spring-web, tomcat-embed, jackson-databind等)都一起引入进来,并且管理好了它们之间的版本兼容性。我们完全不需要再关心应该引入哪些jar包,以及它们的版本应该是什么。
2. 自动化配置 (Auto-Configuration) —— 最具魔力的特性
  • 痛点解决:彻底解决了“繁琐的XML配置”问题。
  • 实现原理:这是Spring Boot的“魔法”核心,它遵循 “约定大于配置”(Convention over Configuration) 的理念。
    1. 条件化配置 (@ConditionalOn...):Spring Boot内部有大量的配置类(@Configuration),但这些配置类并不是无条件生效的。它们大多被@ConditionalOn...系列的注解所标记,比如@ConditionalOnClass(当classpath中存在某个类时生效)、@ConditionalOnBean(当容器中存在某个Bean时生效)、@ConditionalOnProperty(当配置文件中有某个属性时生效)等等。
    2. spring.factories机制:在每个starter的jar包中,都有一个META-INF/spring.factories文件(新版中是新的机制),里面列出了这个starter需要触发的自动配置类。
    3. 工作流程:当Spring Boot应用启动时,它会扫描所有starter中的spring.factories文件,加载这些自动配置类,然后根据@Conditional注解的条件,智能地判断哪些配置需要生效。比如,它发现classpath中有tomcat-embed.jar,就会自动为我们配置好内嵌的Tomcat服务器;发现有DataSource.class,就会自动配置好数据源。
  • 结果:我们无需编写任何一行XML,就能得到一个预先配置好的、最佳实践的Spring环境。
3. 快速启动与独立运行 —— 内嵌Web服务器
  • 痛点解决:解决了传统Web应用需要打包成WAR包,并部署到外部Tomcat服务器的繁琐流程。
  • 实现原理spring-boot-starter-web中默认就包含了内嵌的Tomcat服务器(也可以换成Jetty或Undertow)。
  • 结果:我们的Spring Boot应用可以直接打包成一个可执行的fat JAR。这个JAR包里不仅包含了我们自己的代码,还包含了所有依赖以及内嵌的Web服务器。我们可以通过一句简单的java -jar myapp.jar命令,在任何有Java环境的地方,一键启动我们的Web应用,极大地简化了开发、测试和部署流程。
总结与生态

Spring Boot通过起步依赖、自动化配置、内嵌服务器这三大法宝,从根本上重塑了Spring应用的开发体验。

它不仅自身极其强大,更重要的是,它成为了整个Spring Cloud微服务生态的基石。所有Spring Cloud的组件(如Nacos, Sentinel, Gateway等)都是以Spring Boot starter的形式提供的,这使得构建一个复杂的分布式系统,也变得像搭积木一样简单。可以说,Spring Boot是现代Java后端开发的事实标准和必备技能。

Spring Boot比Spring好在哪里

面试官您好,我认为“Spring Boot比Spring好在哪里”这个问题,更准确的说法应该是 “Spring Boot为我们解决了传统Spring框架的哪些核心痛点”

Spring Boot并不是用来替代Spring的,它是在强大的Spring框架之上,构建的一套旨在简化开发、提升效率的“脚手架”和“最佳实践集”。它让Spring从一个需要精细配置的“专家级”框架,变成了一个“开箱即用”的、对开发者极其友好的平台。

它的“好”,主要体现在解决了以下三大“痛点”:

1. 解决了“配置地狱”的痛点 —— 通过“自动化配置”
  • 传统Spring的痛点
    • 在过去,我们要搭建一个Spring MVC项目,需要编写大量的XML配置文件:web.xml来配置DispatcherServletspring-mvc.xml来配置MVC三组件和AOP,applicationContext.xml来配置数据源、事务等等。这个过程非常繁琐、易错,且充满了样板代码。
  • Spring Boot的解决方案:自动化配置 (Auto-Configuration)
    • Spring Boot遵循 “约定大于配置” 的原则。它内部集成了海量的“自动配置类”。
    • 它会根据我们项目classpath中的jar包,来智能地判断我们需要什么,并自动为我们配置好。
    • 比如:它发现classpath里有spring-webmvc.jar,就自动为我们配好DispatcherServlet和MVC的各种默认设置;发现有HikariCP.jar和JDBC驱动,就自动为我们配好DataSourceJdbcTemplate
    • 我们几乎可以做到零XML配置,就能得到一个功能完备的、按最佳实践配置好的Spring环境。
2. 解决了“依赖地狱”的痛点 —— 通过“起步依赖”
  • 传统Spring的痛点
    • 我们要自己手动管理pom.xml中所有的依赖。比如,要用Spring+MyBatis,我们需要自己去添加spring-core, spring-beans, spring-context, spring-jdbc, mybatis, mybatis-spring等一大堆依赖。
    • 最头疼的是,我们还要自己去解决这些库之间的版本兼容性问题,稍有不慎就会导致各种ClassNotFoundExceptionNoSuchMethodError
  • Spring Boot的解决方案:起步依赖 (Starters)
    • Spring Boot提供了一系列spring-boot-starter-*的依赖包。
    • 我们只需要在pom.xml中引入一个,比如spring-boot-starter-webspring-boot-starter-data-jpa
    • 这个starter就像一个“依赖管理全家桶”,它会通过Maven的依赖传递,把所有相关的、且版本兼容的库都自动引入进来。
    • 我们再也无需关心应该引入哪些依赖,以及它们的版本号是什么,彻底告别了“依赖地狱”。
3. 解决了“部署繁琐”的痛点 —— 通过“内嵌服务器”
  • 传统Spring的痛点
    • Web项目必须被打成一个WAR包
    • 然后,我们需要在服务器上预先安装和配置一个外部的Web容器,比如Tomcat或Jetty。
    • 最后,再把WAR包部署到这个容器中去运行。整个过程步骤多,且环境依赖重。
  • Spring Boot的解决方案:内嵌Web服务器
    • spring-boot-starter-web中默认就包含了内嵌的Tomcat(或其他可选服务器)。
    • 这使得我们的Spring Boot应用可以被打包成一个可执行的、独立的fat JAR
    • 这个JAR包里包含了所有东西:我们的代码、所有依赖、以及内嵌的服务器。
    • 我们可以通过一句简单的 java -jar myapp.jar 命令,在任何有Java环境的地方,一键启动我们的应用,极大地简化了开发、测试和部署流程,也为Docker化和云原生部署铺平了道路。

总结一下,Spring Boot通过这三大法宝,让Spring框架的易用性、开发效率和部署便捷性都得到了质的飞跃。它让我们开发者可以真正地“约定优于配置”,将精力完全集中在业务逻辑的实现上。

怎么理解Spring Boot中的约定大于配置

面试官您好,“约定大于配置”(Convention over Configuration)是Spring Boot最核心、最具颠覆性的设计哲学。

我的理解是:Spring Boot为我们预设了一整套合理的、基于业界最佳实践的“约定”。只要我们遵循这些约定,就可以享受到框架带来的巨大便利,几乎无需任何手动配置就能快速地开发和运行一个应用程序。

这个理念,贯穿于Spring Boot设计的方方面面:

1. 依赖管理的约定 —— 通过“起步依赖” (Starters)
  • 约定:Spring Boot约定,“如果你想开发一个Web应用,你就应该需要Spring MVC、Tomcat、Jackson这些东西。”
  • 实现:它将这个“约定”打包成了一个名为spring-boot-starter-web起步依赖。我们只需要在pom.xml中引入这一个依赖。
  • 效果:所有相关的、版本兼容的jar包都会被自动引入。我们无需再手动配置繁琐的依赖列表,Spring Boot已经为我们做好了最佳的“约定”。
2. Bean配置的约定 —— 通过“自动化配置” (Auto-Configuration)

这是“约定大于配置”最魔力的体现。

  • 约定:Spring Boot约定,“如果你在classpath中加入了spring-boot-starter-web,那么你很可能就需要一个配置好的DispatcherServletTomcat服务器、JSON消息转换器等等。”
  • 实现
    • @EnableAutoConfiguration:我们主启动类上的@SpringBootApplication注解中,就包含了这个核心的自动配置开关。
    • @ConditionalOn...:Spring Boot内部有海量的自动配置类,但它们都带有条件注解。比如,WebMvcAutoConfiguration(负责配置Spring MVC)上,就有@ConditionalOnClass(Servlet.class)等条件。
    • 工作流程:应用启动时,Spring Boot会根据我们引入的starter(即classpath中的内容),来智能地判断哪些“约定”应该生效,并自动为我们配置好相应的Bean。
  • 效果:我们无需再手动编写大量的XML或Java Config来配置这些基础组件。
3. 项目结构的约定
  • 约定:正如您所说,Spring Boot推荐一种标准的项目结构。
    • 主启动类(带有@SpringBootApplication的类)放在一个根包下(比如com.example.myapp)。
    • 其他所有的业务代码(如Controller, Service, Repository)都放在这个根包的子包下(如com.example.myapp.controller)。
  • 为什么有这个约定? 因为@SpringBootApplication注解中包含的@ComponentScan,默认会扫描其所在包及其所有子包。遵循这个结构约定,我们就可以无需手动配置@ComponentScan的扫描路径。
4. 配置文件名的约定
  • 约定:Spring Boot约定,应用程序的配置文件应该命名为application.propertiesapplication.yml,并放在src/main/resources目录下。
  • 效果:只要我们把配置文件放在这个约定的位置,Spring Boot启动时就会自动加载它,我们无需任何额外配置来指定配置文件的位置。

当“约定”不满足需求时 —— 配置大于约定

“约定大于配置”的后半句,其实是 “配置大于约定” 。Spring Boot在提供了巨大便利的同时,也保留了极高的灵活性。

  • 如何覆盖? 当预设的“约定”不满足我们的需求时,我们可以通过在application.propertiesapplication.yml明确地提供我们自己的配置,来覆盖掉自动配置的默认值。
  • 例子:Spring Boot自动配置的Tomcat默认端口是8080。如果我们想换成8081,只需要在配置文件里加上一行server.port=8081即可。我们自己的配置,其优先级高于自动配置。

总结一下,“约定大于配置”是Spring Boot实现“开箱即用”和“快速开发”的核心思想。它通过一系列精心设计的“约定”,将开发者从繁重的配置工作中解放出来,让我们能更专注于业务逻辑本身。同时,它也提供了简单的方式来覆盖这些约定,保证了框架的灵活性。

Spring Boot的项目结构是怎么样的?

面试官您好,Spring Boot推崇 “约定大于配置” 的设计理念,这也体现在它推荐的项目结构上。遵循这套标准的项目结构,不仅能让项目看起来非常清晰、专业,更能让我们充分利用Spring Boot的自动化配置特性,减少很多不必要的配置。

一个典型的、基于Maven的Spring Boot项目,其结构通常如下:

my-project/
├── .mvn/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           └── myapp/
│   │   │               ├── MyApplication.java          <-- (1) 主启动类
│   │   │               ├── controller/                 <-- (2) 控制层 (Web层)
│   │   │               │   └── UserController.java
│   │   │               ├── service/                    <-- (3) 业务逻辑层
│   │   │               │   └── UserService.java
│   │   │               ├── repository/ (或 dao/)       <-- (4) 数据访问层
│   │   │               │   └── UserRepository.java
│   │   │               ├── domain/ (或 model/, entity/) <-- (5) 实体/领域模型
│   │   │               │   └── User.java
│   │   │               ├── config/                     <-- (6) 配置类
│   │   │               │   └── AppConfig.java
│   │   │               └── common/ (或 util/)          <-- (7) 公共工具类
│   │   │                   └── DateUtils.java
│   │   ├── resources/
│   │   │   ├── static/                         <-- (8) 静态资源
│   │   │   │   ├── css/
│   │   │   │   └── js/
│   │   │   ├── templates/                      <-- (9) 视图模板
│   │   │   │   └── index.html
│   │   │   ├── application.properties (或 .yml)  <-- (10) 核心配置文件
│   │   │   └── mybatis/                        <-- (可选) MyBatis Mapper XML
│   │   │       └── UserMapper.xml
│   └── test/
│       └── java/
│           └── com/
│               └── example/
│                   └── myapp/
│                       └── MyApplicationTests.java  <-- (11) 单元测试/集成测试
├── target/
└── pom.xml                                       <-- (12) Maven配置文件
对这个结构的关键点,我的理解如下:
  1. 主启动类 (MyApplication.java)
    • 这是整个应用的入口,包含main方法,并使用@SpringBootApplication注解标记。
    • 一个最重要的约定:它必须放在一个根包下(如com.example.myapp)。因为@SpringBootApplication中包含了@ComponentScan,它默认会扫描其所在包及其所有子包。遵循这个约定,我们就不需要手动指定要扫描的包路径了。
  2. 分层结构 (controller, service, repository)
    • 这是经典的三层架构。将代码按职责划分到不同的包中,使得项目结构非常清晰。
    • controller:负责处理HTTP请求,与前端交互。
    • service:负责封装核心的业务逻辑。
    • repositorydao:负责与数据库进行交互,进行数据持久化操作。
  3. 实体/领域模型 (domainentity)
    • 存放POJO(Plain Old Java Object),通常是与数据库表结构对应的实体类。
  4. 配置类 (config)
    • 存放所有使用@Configuration注解的Java配置类。比如数据源配置、MyBatis配置、线程池配置等。
  5. src/main/resources目录
    • 这是存放所有非Java代码资源的地方。
    • (8) static/:Spring Boot约定,这个目录下的所有文件(如CSS, JavaScript, 图片)都会被当作静态资源直接对外提供访问。
    • (9) templates/:这是存放动态视图模板的约定目录,比如Thymeleaf或FreeMarker的模板文件。
    • (10) application.propertiesapplication.yml: 这是Spring Boot的核心配置文件,所有应用的配置项都写在这里。Spring Boot启动时会自动加载它。
  6. 测试目录 (src/test)
    • 遵循与主代码完全相同的包结构,用于存放单元测试和集成测试代码。
  7. pom.xml
    • Maven项目的核心配置文件。我们在这里引入Spring Boot的起步依赖(Starters),并管理项目的基本信息和插件。

遵循这套标准的项目结构,不仅能让团队协作更顺畅,更能最大限度地利用Spring Boot“约定大于配置”的优势,让我们的开发工作事半功倍。

Spring Boot自动装配原理是什么?

面试官您好,Spring Boot的自动装配(Auto-Configuration)是它最核心、最具“魔力”的特性。它的根本目标,就是根据我们项目classpath中存在的jar包,来智能地、自动地为我们配置好各种所需的Bean,从而让我们摆脱繁琐的手动配置

这个“魔法”的实现,主要依赖于以下几个核心组件和机制的协同工作:

1. 起点:@SpringBootApplication注解

我们通常在主启动类上标注@SpringBootApplication。这其实是一个组合注解,其中最重要的一个元注解就是:

  • @EnableAutoConfiguration: 这就是开启自动装配功能的总开关。没有它,一切都不会发生。
2. “候选配置”的加载:spring.factories机制

@EnableAutoConfiguration本身并不包含具体的配置逻辑,它通过另一个注解@Import(AutoConfigurationImportSelector.class),来加载所有需要被自动配置的“候选者”。

  • AutoConfigurationImportSelector:这个类的作用,就是去扫描我们项目所有依赖的jar包中,一个约定好的文件—— META-INF/spring.factories
  • spring.factories文件
    • 这是一个标准的Properties文件。在Spring Boot的各个starter(如spring-boot-autoconfigure.jar)中,都包含了这个文件。
    • 文件里,有一个关键的key:org.springframework.boot.autoconfigure.EnableAutoConfiguration
    • 这个key下面,用逗号分隔,列出了所有可能需要被自动配置的@Configuration类的全限定名
    • 例如
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
  • 工作流程:Spring Boot启动时,AutoConfigurationImportSelector会读取所有jar包里的spring.factories文件,将这个key下的所有配置类名都加载到内存中,形成一个巨大的“自动配置候选列表”。

(注意:在Spring Boot 2.7及以后,spring.factories机制被一个新的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件所取代,原理类似,但性能更好。面试时可以提一下,更能体现知识的与时俱进。)

3. “智能筛选”:@Conditional系列注解

现在,我们有了一个包含上百个自动配置类的“候选列表”,但显然我们不需要所有的。Spring Boot如何智能地决定哪些该生效,哪些不该生效呢?

  • 答案就是@Conditional条件注解
  • 几乎每一个自动配置类(比如DataSourceAutoConfiguration)或者其内部的@Bean方法,都被一个或多个@ConditionalOn...注解所修饰。
  • 这些条件注解就像一个个“智能开关”
    • @ConditionalOnClass: “如果classpath中存在某个类,我这个配置才生效。”
      • 例子DataSourceAutoConfiguration上就有@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })。这意味着,只有当你引入了数据库驱动(classpath中有了DataSource类),Spring Boot才会尝试为你自动配置数据源。
    • @ConditionalOnMissingBean: “如果容器中不存在某个类型的Bean,我才创建这个默认的Bean。”
      • 例子:在DataSourceAutoConfiguration内部,创建DataSource@Bean方法上,有@ConditionalOnMissingBean(DataSource.class)。这给了我们覆盖的权利:如果你自己定义了一个DataSource的Bean,那么Spring Boot的自动配置就不会生效,会优先使用你自己的。
    • 其他常用条件@ConditionalOnProperty(当配置文件中有某个属性时生效)、@ConditionalOnWebApplication(当这是一个Web应用时生效)等等。

总结整个流程

  1. 启动@SpringBootApplication中的@EnableAutoConfiguration开启了自动装配。
  2. 加载AutoConfigurationImportSelector通过扫描spring.factories,加载了所有starter中定义的“候选配置类”。
  3. 筛选:Spring在处理这个“候选列表”时,会逐一检查每个配置类上的@Conditional条件。
  4. 生效:只有满足所有条件的自动配置类,才会被容器加载,其内部定义的@Bean才会最终被创建和注册到IoC容器中。

通过这套 “约定(spring.factories) + 条件判断(@Conditional)” 的精巧机制,Spring Boot就实现了在几乎零配置的情况下,为我们的应用程序提供“按需服务”、“开箱即用”的强大能力。

说几个启动器(starter)?

面试官您好,Spring Boot的starter(起步依赖)是其 “约定大于配置”理念的核心体现。它的本质就是一个“依赖管理全家桶” ,通过引入一个starter,我们就能自动地把某个技术场景下所需的所有常用依赖(并且是版本兼容的)都引入进来,极大地简化了我们的Maven或Gradle配置。

在我的项目中,我用过非常多的starter,可以按照它们解决的技术领域,举几个最典型的例子:

1. Web开发领域
  • spring-boot-starter-web: 这是最常用、最基础starter
    • 它提供了:构建一个标准Web应用所需的一切,包括Spring MVC框架、RESTful支持(通过Jackson进行JSON转换),以及一个内嵌的Tomcat服务器
    • 效果:只要引入它,我们就能立刻开始编写@RestController,并且可以直接将应用打包成可执行的JAR包来运行,无需部署到外部Web容器。
2. 数据访问领域

这个领域有非常丰富的starter来适配不同的数据存储技术。

  • spring-boot-starter-data-jpa:
    • 它提供了:用于实现基于JPA (Java Persistence API)规范的数据持久化。它默认会引入Hibernate作为JPA的实现,并包含spring-data-jpa、数据库连接池(如HikariCP)等。
    • 效果:让我们能够通过定义Repository接口,以一种非常优雅、几乎不用写SQL的方式来操作数据库。
  • mybatis-spring-boot-starter:
    • 它提供了:这是由MyBatis社区官方提供的,用于无缝集成MyBatis框架
    • 效果:它会自动配置好SqlSessionFactorySqlSessionTemplate,并能自动扫描我们的Mapper接口并将其注册为Spring Bean,我们只需要在配置文件中提供数据源信息即可。
  • spring-boot-starter-data-redis:
    • 它提供了:与Redis数据库集成的所有支持。它包含了Spring Data Redis模块和底层的Java客户端(如Lettuce或Jedis)。
    • 效果:我们可以非常方便地通过RedisTemplate或者注解驱动的缓存(@Cacheable)来操作Redis,实现高速缓存、分布式锁等功能。
3. 系统安全领域
  • spring-boot-starter-security:
    • 它提供了Spring Security框架的支持。
    • 效果:一旦引入,我们的应用会立即获得基础的安全防护,比如默认的登录页面、HTTP Basic认证等。然后我们可以通过编写SecurityConfigurerAdapter配置类,来非常灵活地定制我们的认证和授权规则。
4. 测试领域
  • spring-boot-starter-test:
    • 它提供了:进行单元测试和集成测试所需的全套工具。包括JUnit 5, Spring Test & Spring Boot Test, AssertJ(一个流式的断言库), Mockito(一个Mocking框架)等。
    • 效果:让我们能够非常方便地编写测试用例,无论是对单个Bean的单元测试,还是启动一个完整的Spring容器进行集成测试。
总结

Spring Boot的starter生态非常庞大,几乎覆盖了我们日常开发中可能用到的所有主流技术和框架。它的核心价值在于,将“技术集成”这个复杂、易错的过程,标准化、自动化了。我们开发者只需要在众多“全家桶套餐”中选择自己需要的,然后就可以直接开始烹饪我们的“主菜”(业务逻辑),而无需关心“厨房”的搭建和“食材”的采购问题。

写过Spring Boot Starter吗?

面试官您好,是的,我在之前的项目中有过编写自定义Spring Boot Starter的经验。当时我们是为了解决一个跨多个微服务团队的通用功能复用问题。

这个通用功能是一个定制化的操作日志记录模块。我们希望所有微服务都能以一种统一的、零配置的方式,轻松地集成这个日志功能。将它封装成一个Starter,是实现这个目标的最佳方式。

下面我来介绍一下我当时设计和实现这个Starter的完整思路和步骤。

第一步:明确Starter的核心目标与功能

我们的目标是,任何一个微服务,只需要在它的pom.xml中引入我们这个log-spring-boot-starter依赖,然后:

  1. 自动地在Spring容器中注册一个核心的LogService Bean。
  2. 自动开启一个AOP切面,通过一个自定义的@LogRecord注解,来拦截需要记录操作日志的方法。
  3. 提供一些可配置的属性,比如日志的输出格式、是否启用等,允许使用者在application.yml中进行定制。
第二步:创建项目结构

一个标准的Starter通常包含两个模块:

  1. log-spring-boot-autoconfigure (自动配置模块)
    • 这是Starter的核心,包含了所有的自动配置逻辑(@Configuration类)、属性配置类(@ConfigurationProperties)以及spring.factories文件。
    • 这个模块不包含任何具体的业务实现,它只负责“配置”。
  2. log-spring-boot-starter(起步依赖模块)
    • 这是一个 “空壳” 的Maven项目,它里面几乎没有代码。
    • 它的唯一作用,就是通过pom.xml<dependencies>,将 log-spring-boot-autoconfigure模块以及这个日志功能所需要的其他第三方依赖 (比如AOP相关的spring-boot-starter-aop)聚合在一起。
    • 使用者只需要引入这一个starter依赖,就能通过Maven的依赖传递,获得所有需要的东西。
第三步:实现自动配置模块 (autoconfigure)

这是最关键的一步。

  1. 编写属性配置类 (LogProperties.java)
@ConfigurationProperties(prefix = "myapp.log")
public class LogProperties {private boolean enabled = true; // 提供一个总开关private String format = "default"; // 提供日志格式配置// ... getters and setters
}

这个类用于映射application.ymlmyapp.log.*开头的配置。

  1. 编写核心业务Bean (LogService.java等)
    • 这里面是真正的日志记录逻辑的实现。
  2. 编写自动配置类 (LogAutoConfiguration.java)
@Configuration
// 只有当用户配置了 myapp.log.enabled=true (或者没配,默认为true)时,此配置才生效
@ConditionalOnProperty(prefix = "myapp.log", name = "enabled", havingValue = "true", matchIfMissing = true) 
// 让我们的LogProperties生效
@EnableConfigurationProperties(LogProperties.class) 
// 确保在AOP自动配置之后再配置我们的
@AutoConfigureAfter(AopAutoConfiguration.class)
public class LogAutoConfiguration {@Bean// 只有当容器中不存在LogService这个Bean时,才创建我们这个默认的@ConditionalOnMissingBeanpublic LogService logService(LogProperties properties) {// 根据配置属性来创建Servicereturn new LogServiceImpl(properties.getFormat());}@Bean@ConditionalOnMissingBeanpublic LogAspect logAspect(LogService logService) {// 创建AOP切面Beanreturn new LogAspect(logService);}
}
  • 设计的关键:大量使用@ConditionalOn...注解,保证我们的自动配置足够“智能”,不与用户的自定义配置冲突。
  1. 创建META-INF/spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.log.autoconfigure.LogAutoConfiguration

(我会补充一句:在Spring Boot 2.7之后,更推荐使用META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,原理类似但性能更好。)

  • 在这个文件中,注册我们的自动配置类,告诉Spring Boot启动时需要加载它。
第四步:打包、发布和使用
  • 将这两个模块打包并发布到公司的私有Maven仓库。
  • 其他微服务团队,只需要在他们的pom.xml中添加一段简单的依赖:
<dependency><groupId>com.example</groupId><artifactId>log-spring-boot-starter</artifactId><version>1.0.0</version>
</dependency>
  • 然后,他们就可以直接在自己的业务方法上使用@LogRecord注解,并且在application.yml中通过myapp.log.enabled来控制功能的开关,真正实现了“开箱即用”和“约定大于配置”。

通过这个过程,我深刻地理解了Spring Boot Starter不仅仅是一种技术,更是一种封装和分享最佳实践的强大思想。

Spring Boot里面有哪些重要的注解?还有一个配置相关的注解是哪个?

面试官您好,Spring Boot通过一套强大的注解体系,极大地简化了开发。在我的实践中,我习惯于把这些重要的注解分为几个核心类别:

1. 启动与自动配置注解
  • @SpringBootApplication: 这是Spring Boot应用的“心脏”,通常标注在主启动类上。它本身是一个组合注解,至少包含了三个核心注解:
    • @EnableAutoConfiguration: 开启自动配置的总开关。
    • @ComponentScan: 启用组件扫描,默认扫描当前包及其子包。
    • @SpringBootConfiguration: 继承自@Configuration,表明这个类也是一个Spring配置类。
2. Bean定义与依赖注入注解 (IoC/DI)

这些是Spring框架的基础,Spring Boot让它们用起来更方便。

  • @Component 及其衍生注解 @Service,@Repository,@Controller,@RestController: 用于将类声明为Spring容器管理的Bean。
  • @Autowired: 用于自动注入依赖。
3. Web开发注解 (Spring MVC)
  • @RequestMapping 及其衍生注解 @GetMapping,@PostMapping 等:用于将HTTP请求映射到Controller方法。
  • @PathVariable,@RequestParam,@RequestBody: 用于从请求中提取数据。
4. 配置相关注解 (Configuration)

配置相关的注解,在Spring Boot中至关重要,因为它们是连接代码外部化配置(application.yml 的桥梁。

  • @Configuration:
    • 这是定义Java配置类的核心注解。它告诉Spring,这个类是一个配置源,里面可以通过@Bean方法来定义Bean。这是替代传统XML配置的现代方式。
  • @Value("${...}"):
    • 这是一个简单、直接的属性注入方式。它可以将配置文件中的单个属性值注入到Bean的字段中。
    • 示例@Value("${server.port}") 可以获取服务器端口。
    • 缺点:当需要注入的配置项很多时,代码会显得非常零散和重复。
  • @ConfigurationProperties(prefix = "..."):
    • 这是我强烈推荐的、用于处理复杂配置的最佳实践
    • 作用:它提供了类型安全的、结构化的属性绑定。我们可以创建一个POJO类,用@ConfigurationProperties标记它,并指定一个配置前缀。
    • 示例
      application.yml中:
myapp:thread-pool:core-size: 10max-size: 200

Java配置类:

@Component // 或在@Configuration类中用@EnableConfigurationProperties
@ConfigurationProperties(prefix = "myapp.thread-pool")
public class ThreadPoolProperties {private int coreSize;private int maxSize;// getters and setters...
}
  • 优势
    1. 类型安全:Spring Boot会自动进行类型转换。
    2. 结构化:将相关的配置项聚合到一个对象中,代码更清晰、更易于维护。
    3. 强大的IDE支持:IDE可以对配置文件中的这些属性提供自动补全和语法高亮。

总结一下,虽然@Value很方便,但在处理一组相关的配置时,使用 @Configuration配合@Bean@ConfigurationProperties 的组合,是构建可维护、类型安全的配置的最佳方式。它完美地体现了Spring Boot在简化配置方面的设计哲学。

Spring Boot怎么开启事务?

面试官您好,在Spring Boot中开启和使用事务非常简单和直接,这得益于Spring Boot强大的自动化配置约定大于配置的理念。

我们只需要做两件核心的事情:添加依赖使用注解

第一步:添加必要的“起步依赖” (Starter)

要使用Spring的声明式事务管理,我们首先需要确保项目中包含了数据访问AOP相关的依赖。

  1. 添加JDBC Starter
    • 通常,我们会在pom.xml中引入spring-boot-starter-jdbc或者spring-boot-starter-data-jpa
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
  • 为什么是这个? 因为spring-boot-starter-jdbc不仅包含了JDBC操作所需的一切,它还会传递性地引入spring-tx模块,这正是Spring事务管理的核心。同时,它也包含了spring-boot-starter-aop,为事务的AOP实现提供了基础。
  1. 添加数据库驱动
    • 当然,我们还需要添加具体的数据库驱动依赖,比如MySQL的:
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>
第二步:在主启动类上开启事务管理
  • 使用@EnableTransactionManagement注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;@SpringBootApplication
@EnableTransactionManagement // 开启事务管理
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}
  • 我们需要在主启动类上,明确地加上@EnableTransactionManagement这个注解,来告诉Spring Boot:“请为我开启声明式事务管理的功能。”
  • Spring Boot 2.x及以后的简化
    • 值得一提的是,在现代的Spring Boot版本中,只要我们引入了像spring-boot-starter-jdbcspring-boot-starter-data-jpa这样的starter,Spring Boot的自动化配置 (DataSourceTransactionManagerAutoConfiguration)自动地为我们开启事务管理
    • 所以,在很多情况下,即使我们不写@EnableTransactionManagement,事务也能正常工作。但从代码的可读性和明确性角度出发,我个人还是习惯于显式地加上这个注解。
第三步:在需要事务的方法上使用@Transactional注解
  • 这是最关键的一步。我们只需要在需要进行事务管理的方法(或者整个类)上,加上@Transactional注解即可。
  • 最佳实践:通常,我们会将@Transactional注解加在Service层的public方法上。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Autowiredprivate AccountDao accountDao;@Override@Transactional // 声明此方法需要事务管理public void registerUser(User user, Account account) {// 1. 插入用户记录userDao.insert(user);// 模拟一个异常if (account.getBalance() < 0) {throw new RuntimeException("账户余额不能为负!");}// 2. 插入账户记录accountDao.insert(account);}
}
  • 工作原理
    • 当外部调用registerUser方法时,Spring AOP会拦截到这个调用。
    • 它会发现这个方法被@Transactional标记了,于是会在方法执行前,开启一个数据库事务
    • 然后,它会执行方法体内的业务逻辑。
    • 如果方法正常执行完毕,AOP会在方法返回后,提交事务
    • 如果方法执行过程中,抛出了 RuntimeExceptionError(默认情况下),AOP会捕获到这个异常,并回滚事务 。在上面的例子中,userDao.insert(user)的操作就会被撤销。

总结一下,在Spring Boot中开启事务,流程非常简单:

  1. 引入正确的starter(如spring-boot-starter-jdbc)。
  2. (可选但推荐)在主启动类上加上@EnableTransactionManagement
  3. 在需要事务的Service层public方法上,标注@Transactional注解

剩下的所有复杂的事务开启、提交、回滚逻辑,都由Spring Boot通过自动化配置和AOP为我们透明地完成了。

Spring Boot怎么做到导入就可以直接使用的?

面试官您好,Spring Boot之所以能做到“导入一个依赖就可以直接使用”,这背后是其两大核心机制——“起步依赖(Starters)”“自动化配置(Auto-Configuration)”——在协同工作。

下面我来详细解释一下这两个机制是如何配合的:

第一步:“起步依赖”——打包好所有需要的“零件”
  • 它解决了什么问题? 解决了传统开发的 “依赖地狱” 问题。在过去,我们要用一个技术(比如MyBatis),需要自己手动去pom.xml里添加mybatis.jar, mybatis-spring.jar, spring-jdbc.jar等一大堆依赖,并且还要小心翼翼地处理它们之间的版本兼容性。
  • Starter如何工作?
    • Spring Boot提供了一系列名为spring-boot-starter-*的“起步依赖”。比如,我们想用MyBatis,只需要引入一个mybatis-spring-boot-starter
    • 这个starter本身几乎没有代码,它最重要的作用,就是通过Maven的依赖传递机制,将使用MyBatis所需要的所有相关jar包(包括MyBatis自身、Spring的集成支持、AOP、JDBC等)都自动地、以一个兼容的版本引入到我们的项目中。
第二步:“自动化配置”——智能地“组装和配置”
  • 它解决了什么问题? 解决了传统开发的 “配置地狱” 问题。过去,零件到齐了,我们还需要编写大量的XML或Java代码,去手动地将这些零件组装起来(比如配置SqlSessionFactoryBean, MapperScannerConfigurer等)。
  • Auto-Configuration如何工作?
    1. @EnableAutoConfiguration开关:我们主启动类上的@SpringBootApplication注解,已经为我们打开了这个“自动安装”的总开关。
    2. spring.factories清单:在我们引入的starter的jar包中(比如mybatis-spring-boot-autoconfigure.jar),有一个 META-INF/spring.factories文件。这个文件就像一张“安装说明书” ,里面写着:“如果你想自动配置MyBatis,请加载我这个MybatisAutoConfiguration配置类。”
    3. @Conditional智能判断:Spring Boot启动时,会加载这张“安装说明书”里列出的所有自动配置类。但它并不会无脑地全部生效。每个自动配置类(如MybatisAutoConfiguration)都带有一系列 @ConditionalOn... 的条件注解。
      • 比如,@ConditionalOnClass(SqlSessionFactory.class)会检查:“你的项目里(classpath)是不是真的有MyBatis的核心类?”
      • 比如,@ConditionalOnMissingBean(SqlSessionFactory.class)会检查:“用户是不是已经自己配置了一个SqlSessionFactory?如果配了,那我就不重复配置了,优先用用户的。”
    4. 自动创建Bean:只有当所有条件都满足时,这个自动配置类才会生效。它内部会使用@Bean注解,自动地为我们创建和配置好所有需要的Bean(比如SqlSessionFactory),并注册到Spring容器中。

总结:为什么导入就能用?

整个流程可以总结为:

  1. 你引入了一个starter(买了一个大礼包)
  2. 这个starter自动地把所有需要的依赖jar包(零件) 带进了你的项目。
  3. Spring Boot启动时,看到了这些新来的jar包(零件),于是触发了对应的自动化配置(自动安装程序)
  4. 这个“自动安装程序”检查了一下你家里的环境(@Conditional判断),发现条件都满足。
  5. 于是,它自动地把这些零件组装好(创建并配置好Bean),并放到了Spring容器中。

最终,我们作为开发者,什么都不用做,只需要通过@Autowired就可以直接注入并使用这个已经配置好的SqlSessionFactoryMapper接口了。这就是Spring Boot“约定大于配置”理念的强大之处。

Spring Boot 过滤器和拦截器说一下?

面试官您好,过滤器(Filter)和拦截器(Interceptor)都是Web开发中用于实现横切关注点(Cross-Cutting Concerns) 的重要工具,比如日志记录、权限校验、数据编码等。

但它们是两个完全不同层面的东西,我通常会从它们的归属、功能范围、执行时机和使用方式这几个维度来区分它们。

一个核心的比喻:大楼安检与楼层门禁

我们可以把一次Web请求的处理过程,想象成进入一栋大楼:

  • 过滤器 (Filter):就像是整栋大楼的入口安检系统
  • 拦截器 (Interceptor):就像是进入具体某个公司或楼层的内部刷卡门禁
1. 归属与依赖关系 (出身不同)
  • 过滤器 (Filter)
    • 归属:它是Java Servlet规范的一部分(javax.servlet.Filter),不依赖于任何特定的框架。只要是支持Servlet的Web容器(如Tomcat),都可以使用它。
    • 定位:是一个更底层的、与框架无关的组件。
  • 拦截器 (Interceptor)
    • 归属:它是Spring MVC框架提供的一个组件(org.springframework.web.servlet.HandlerInterceptor)。
    • 定位:它强依赖于Spring的IoC容器和MVC框架,是一个更高层的、与Spring紧密集成的组件。
2. 功能范围与能访问到的信息 (权限不同)
  • 过滤器 (Filter)
    • 能做什么:因为工作在最外层,它可以对任何进入容器的请求进行过滤,甚至可以修改请求(Request)和响应(Response)的内容。
    • 能访问什么:它只能访问到最原始的HttpServletRequestHttpServletResponse对象。它不知道这个请求最终会被哪个Controller的哪个方法处理,也无法直接访问到Spring容器中的任何Bean。
  • 拦截器 (Interceptor)
    • 能做什么:它只拦截被Spring MVC的DispatcherServlet所分派的请求。
    • 能访问什么:它的权限要大得多。因为它本身就是Spring容器管理的一个Bean,所以它可以轻松地通过@Autowired注入任何其他的Service或Component。在postHandle方法中,它还能访问到即将返回给视图的 ModelAndView对象 ,可以修改模型数据。
3. 执行时机与生命周期 (工作流程不同)

这是一个非常关键的区别,也印证了“大楼安检与楼层门禁”的比喻。

  • 执行流程:一个请求过来,其执行顺序是:
    请求 -> Filter链 -> DispatcherServlet -> Interceptor链前置处理(preHandle) -> Controller -> Interceptor链后置处理(postHandle) -> 视图渲染 -> Interceptor链完成处理(afterCompletion) -> Filter链 -> 响应
  • 过滤器 (Filter)
    • 它的执行时机非常靠前,在请求进入DispatcherServlet之前。
    • 它的doFilter方法将整个请求处理过程包裹起来。
  • 拦截器 (Interceptor)
    • 它的执行被细分为了三个阶段,嵌入在Controller的执行前后:
      1. preHandle: 在Controller方法执行之前调用。如果它返回false,则整个请求处理流程会被中断。非常适合做权限校验。
      2. postHandle: 在Controller方法执行之后,视图渲染之前调用。我们可以在这里修改ModelAndView
      3. afterCompletion: 在整个请求处理完成(包括视图渲染)之后调用。非常适合做一些资源清理工作。

总结与选型建议

对比维度过滤器 (Filter)拦截器 (Interceptor)
归属Servlet规范Spring MVC框架
依赖无,Web容器即可依赖Spring IoC容器
拦截范围所有请求仅Spring MVC处理的请求
可访问信息原始的Request/ResponseRequest/Response, Spring Beans, ModelAndView
执行时机请求进入Servlet之前Controller方法执行前后
功能粒度较粗,包裹整个请求更细,分为前、后、完成三个阶段

我的选型策略

  • 当需要实现一些与框架无关的、通用的、底层的请求处理时,我会选择过滤器 (Filter)
    • 典型场景:字符编码统一处理、跨域(CORS)配置、XSS攻击防护等。
  • 当需要实现与业务逻辑紧密相关、并且需要使用Spring容器中的服务(如用户认证、权限校验)时,我一定会选择拦截器 (Interceptor)
    • 典型场景
      • 登录校验:在preHandle中检查Session或Token,如果未登录,则直接中断请求。
      • 权限验证:在preHandle中获取当前用户信息,并检查其是否有权限访问即将被调用的Controller方法。
      • 在向视图传递数据前,添加一些公共的模型属性:在postHandle中实现。

简单来说,过滤器管“入口”,做通用粗粒度处理;拦截器管“业务”,做精细化、与Spring集成的处理

参考小林coding和JavaGuide

相关文章:

  • 全连接层和卷积层
  • 学习threejs,使用TSL计算粒子鼠标特效
  • 【AI时代速通QT】第一节:C++ Qt 简介与环境安装
  • uniapp 腾讯云 COS 访问控制实战(细粒度权限管理)
  • PHP7+MySQL5.6 雪里开简易预约制访客管理系统V1.0
  • IGBT(绝缘栅双极型晶体管)简介
  • Vue3 + TypeScript + Element Plus 表格行按钮不触发 row-click 事件、不触发勾选行,只执行按钮的 click 事件
  • 基于开源AI智能名片链动2+1模式S2B2C商城小程序的新零售融合路径研究
  • elementui使用Layout布局-对齐方式
  • 零基础玩转物联网-串口转以太网模块如何快速实现与HTTP服务器通信
  • element-ui 的el-table,多选翻页后,之前选择的数据丢失问题处理
  • Flutter - 原生交互 - 相机Camera - 02
  • 深度学习小项目合集之音频语音识别-视频介绍下自取
  • Flutter 常用组件详解:Text、Button、Image、ListView 和 GridView
  • 【编译工具】(版本控制)Git + GitHub Actions:自动化工作流如何让我的开发效率提升200%?
  • 深度强化学习 | 详细推导随机/确定性策略梯度定理
  • `dispatch_source_t` 计时器 vs `NSTimer`:核心差异一览
  • React 18 渲染机制优化:解决浏览器卡顿的三种方案
  • 应用无法获取用户真实ip问题排查
  • 前端面试宝典---事件循环面试题
  • 网站建设公司营业执照/海外营销公司
  • 怎么建设一个优秀的网站/优化网络培训
  • 上海专业网站建站品牌/公众号怎么做文章推广
  • 国内 wordpress 大战/南宁seo内部优化
  • 新闻发布稿/网站seo内容优化
  • 微网站的定义/吉林seo技术交流