SpringBoot面试题积累

目录

一、SpringBoot两道最重要的面试题

二、看黑马SpringBoot讲义

三、SpringBoot学习中文官网★★★★★

四、几个链接

五、SpringBoot部分知识摘录

1. SpringBoot有哪些特性?(与Spring对比的优势)

 2.1 SpringBoot自动配置原理

2.2 SpringBoot自动配置流程是怎样的?

2.3 自定义配置如何覆盖SpringBoot自动配置的设置?

2.4 Spring Boot中的条件装配(Conditional Annotation Processing)是什么?

2.5 Spring Boot中有哪些常用的自动配置类?

2.6 Spring Boot自动配置是基于哪些条件触发的?

3.1 SpringBoot启动流程是什么?(GitCode答案哦)

3.2 SpringBoot启动原理

4. SpringBoot如何加载主配置类?

5. 如何限制@ComponentScan的扫描范围?

6.1 SpringBoot的启动器(Starters)有哪些?

6.2 如何添加额外的配置到Spring Boot启动类?

7. 嵌入式 Tomcat 如何在 Spring Boot 中工作?

8.1 如何在 Spring Boot 中添加过滤器(Filter)?

8.2 为什么要使用 Filter?有什么具体应用场景?

8.3 为什么需要对请求参数进行校验,有哪些常见的校验规则?

8.4 你知道哪些Java库可以用来简化请求参数的校验吗?

9. Spring Boot 中的 Bean 生命周期是怎样的?

10.1 如何禁用Spring Boot的部分自动配置?

10.2 如何通过`@EnableAutoConfiguration`排除自动配置?

11.1 Spring Boot如何动态切换数据源?

11.2 如何在没有`@Profile`的情况下,基于代码逻辑动态切换数据源?★★★

11.3 你能解释一下`AbstractRoutingDataSource`的工作原理吗?

11.4 当找不到匹配的数据源时,`AbstractRoutingDataSource`会怎么做?

11.5 数据源切换的场景有哪些?

12. SpringBoot配置的优先级从高到低依次是什么?★★

五、图灵B站15道面试题​​​​​​​


一、SpringBoot两道最重要的面试题

1. SpringBoot启动原理(流程)是什么?(含有清晰的源码分析)

2. SpringBoot自动配置原理是什么?

这两个问题,看下面大标题四中的第一个链接“SpringBoot面试题-阿里云开发者社区”中寻找答案即可。也可以看黑马SpringBoot老师的讲义。再或者任何一个讲到SpringBoot的都会包含这两个内容。另阅读:SpringBoot——SpringBoot启动原理-CSDN博客

二、看黑马SpringBoot讲义

地址:暂时在电脑F盘【Springboot各机构课程&文档】文件夹中

三、SpringBoot学习中文官网★★★★★

官网:Core Features (spring.io)

中文:springboot/pages/spring-boot-features.md at master · DocsHome/springboot · GitHub

中文:Spring Framework (jcohy.com)

 

四、几个链接

下面这些好多好多内容都是重复的。

五、SpringBoot部分知识摘录

1. SpringBoot有哪些特性?(与Spring对比的优势)

 2.1 SpringBoot自动配置原理

2.2 SpringBoot自动配置流程是怎样的?

SpringBoot自动配置流程主要包括以下几个步骤:

  1. 启用自动配置: 应用启动时,@SpringBootApplication 注解(包括它的 @EnableAutoConfiguration 子注解)会标记当前类为启动类,并启用自动配置。

  2. 选择自动配置类AutoConfigurationImportSelector 类会根据 Maven 或 Gradle 的类路径中的 jar 包确定哪些自动配置类应该被加载。这个过程涉及到读取 META-INF/spring.factories 文件,该文件列出了所有可用的自动配置候选类。

  3. 判断条件: 使用 @Conditional 注解(如 @ConditionalOnClass@ConditionalOnBean@ConditionalOnMissingBean 等)检查某些条件是否满足。如果满足,相应的自动配置类就会生效。

  4. 实例化bean: 当条件满足后,Spring 容器会实例化这些自动配置类,并将它们的bean定义注册到Spring容器中。

  5. 属性绑定@ConfigurationProperties 注解用于将 application.properties 或 application.yml 配置文件中的键值对绑定到特定的Java对象上,这样可以根据配置动态调整bean的行为。

  6. 自动化配置bean之间的依赖关系: 根据自动配置类的定义,Spring容器会自动管理bean之间的依赖关系,例如,如果检测到某个类存在,那么可能会自动配置与之相关的其他类。

  7. 自定义配置覆盖默认配置: 用户可以通过自己定义的bean或者在配置文件中添加/修改配置,以覆盖自动配置的部分或全部内容。

2.3 自定义配置如何覆盖SpringBoot自动配置的设置?

在SpringBoot中,你可以通过以下几种方式来覆盖自动配置的设置:

1) 使用@ConfigurationProperties: 你可以创建一个类并使用@ConfigurationProperties注解,指定需要绑定的配置前缀,这样可以覆盖自动配置中的相应部分。例如,如果你想覆盖数据库连接池的配置,可以创建一个名为DatabaseConfig的类:

@ConfigurationProperties(prefix = "spring.datasource")
public class DatabaseConfig {
    private String url;
    private String username;
    // getters and setters
}

然后在你的主配置类中使用@EnableConfigurationProperties(DatabaseConfig.class)来启用它。

2) 声明自己的@Bean: 如果你想替换自动配置的一个特定bean,可以在你的配置类中使用@Bean注解创建一个新的bean。Spring Boot会优先考虑你自己定义的bean。

@Configuration
public class CustomConfig {

    @Bean
    public MyService myService() {
        return new CustomMyServiceImpl();
    }
}

这样,CustomMyServiceImpl将会替代自动配置中的MyService bean。

3) 使用@EnableAutoConfiguration排除特定自动配置: 你还可以通过@EnableAutoConfigurationexclude属性来排除不希望使用的自动配置类。

@SpringBootApplication(exclude={SomeAutoConfiguration.class})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

这将防止SomeAutoConfiguration类被应用。

4) 使用application.properties/yml覆盖: 直接在application.propertiesapplication.yml文件中提供相同的关键字来覆盖自动配置的属性。比如:

# application.properties
spring.jpa.show-sql=false
# application.yml
spring:
  jpa:
    show-sql: false

2.4 Spring Boot中的条件装配(Conditional Annotation Processing)是什么?

条件装配(Conditional Annotation Processing)是Spring Boot的一种核心特性,它允许在特定条件下加载或忽略bean。这种机制基于@Conditional注解,使Spring能够根据运行时环境决定是否创建某个bean。

以下是使用@Conditional的基本步骤:

1) 创建一个条件类(Condition),实现org.springframework.context.annotation.Condition接口,并重写matches()方法来定义匹配条件。

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class MyCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 在这里检查条件,如环境变量、 bean的存在等
        return someConditionIsMet(context);
    }
    
    private boolean someConditionIsMet(ConditionContext context) {
        // 实现具体的条件判断逻辑
    }
}

2) 将@Conditional注解添加到带有条件的bean上,传入你的条件类。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    @Conditional(MyCondition.class)
    public MyBean myBean() {
        return new MyBean();
    }
}

这样,只有当MyCondition.matches()返回true时,myBean才会被创建并注入到Spring容器中。 

2.5 Spring Boot中有哪些常用的自动配置类?

在Spring Boot中,有许多自动配置类用于简化常见功能的设置。这里是一些常见的自动配置类:

  • WebMvcAutoConfiguration: 提供了基于Spring MVC的web应用配置,包括视图解析器、静态资源处理等。
  • DataSourceAutoConfiguration: 自动配置数据源,当检测到如HikariCPTomcat JDBC等连接池时会生效。
  • JpaBaseConfiguration: 为JPA提供基础配置,包括事务管理和实体扫描。
  • ThymeleafAutoConfiguration: 针对Thymeleaf模板引擎的自动配置。
  • MailSenderAutoConfiguration: 自动配置邮件发送服务,如果存在JavaMailSender实现。
  • ManagementServerProperties: 用于Actuator端点的配置,比如暴露健康检查和指标信息。
  • FlywayAutoConfiguration: 数据库迁移工具Flyway的自动配置。
  • LiquibaseAutoConfiguration: 数据库变更管理工具Liquibase的自动配置。
  • RedisAutoConfiguration: 对Redis缓存的支持。
  • SecurityAutoConfiguration: Spring Security的基础配置,提供了安全控制的默认设置。

2.6 Spring Boot自动配置是基于哪些条件触发的?

Spring Boot的自动配置主要基于以下几个条件触发:

  • 条件注解(@Conditional): Spring Boot使用@Conditional注解及其子注解(如@ConditionalOnClass@ConditionalOnBean@ConditionalOnProperty等)来判断是否满足特定条件,例如某个类是否存在、某个Bean已定义或者系统属性是否匹配。

  • 启用自动配置@EnableAutoConfiguration: 在Spring Boot主配置类上使用@EnableAutoConfiguration注解,这告诉Spring框架去寻找并应用所有的自动配置类。

  • 排除自动配置类(@Excludes, @AutoConfigurationExcludeFilter): 使用@SpringBootApplication(exclude = {...})可以排除不想要的自动配置,或者通过@AutoConfigurationPackage@AutoConfigurationExcludeFilter来过滤某些包下的自动配置。

  • 启动类扫描的包:Spring Boot将扫描启动类所在的包以及其子包,用于查找组件(@Component, @Service, @Repository, @Controller等)和自定义配置类。

  • META-INF/spring.factories 文件:Spring Boot通过读取spring-boot-autoconfigure-<version>.jar中的META-INF/spring.factories文件来找到所有的自动配置类。

3.1 SpringBoot启动流程是什么?(GitCode答案哦)

SpringBoot启动流程主要包括以下几个步骤:

  1. 启动入口:程序执行到含有main方法的类,一般为SpringApplication.run()

  2. 判断应用类型:基于类路径检查决定是Web应用还是非Web应用,从而选择合适的ApplicationContext类型(例如 AnnotationConfigApplicationContext 或者 WebApplicationContext)。

  3. 初始化SpringApplication对象:设置应用名称、 Banner模式、环境变量等。

  4. 加载初始化器:从META-INF/spring.factories文件中加载ApplicationContextInitializer实现类。

  5. 准备环境:读取应用的属性源,包括命令行参数、系统属性、JAR内的application.propertiesapplication.yml等。

  6. 应用监听器:创建SpringApplicationRunListeners实例,用来监听SpringApplication运行的各个阶段。

  7. 执行run方法:创建ApplicationContext上下文,加载主配置类(一般由@SpringBootApplication注解标记的类)。

  8. Bean定义阶段:扫描包,发现并注册Bean Definition。

  9. 定制化Bean工厂:处理@Bean注解的方法,初始化Singleton Beans。

  10. 拓展点回调:调用ApplicationListeners和ApplicationRunnerCommandLineRunner的回调方法。

  11. 容器准备完成:所有非懒加载的Bean都将被初始化。

  12. 显示Banner:如果配置了Banner,则会打印出来。

  13. 应用上下文准备好:ApplicationContext完全启动完毕,运行完成。

3.2 SpringBoot启动原理

Spring Boot 的启动原理主要涉及到以下几个关键步骤:

  1. 主类执行

    • 通常,Spring Boot 应用有一个标记了 @SpringBootApplication 注解的主类。这个注解包含了 @SpringBootConfiguration@EnableAutoConfiguration, 和 @ComponentScan 三个注解的功能。
  2. 配置加载

    • Spring Boot 会从主类开始扫描组件,并寻找资源配置(如 application.properties 或 application.yml)文件来读取配置信息。
  3. 自动配置

    • @EnableAutoConfiguration 引导 Spring Boot 进行自动配置。它基于已知的类路径和应用属性来决定哪些 Spring 配置应该被“启用”。例如,如果 tomcat.jar 在类路径中,且没有明确禁用,那么就会自动配置一个嵌入式 Tomcat web 服务器。
  4. Bean 定义

    • 所有的 @Component(包括 @Service@Repository@Controller 等)以及其他 Spring 标准注解(如 @Bean)都会被处理,生成对应的 Bean 定义。
  5. ApplicationContext 初始化

    • Spring Boot 使用 SpringApplication.run() 方法来创建一个 SpringApplicationContext,这里加载了所有的配置和 Bean。
  6. Bean 实例化

    • Spring 容器管理这些 Bean 的生命周期,根据依赖关系进行实例化和注入。
  7. 监听器初始化

    • 若有配置了 Spring Boot 的监听器(如 WebServerStartupTracker),它们会在服务器启动时触发相应事件。
  8. Tomcat 启动

    • 对于 Web 应用,嵌入式的 Tomcat 或其他服务器将开始接受请求。
  9. 应用运行

    • 至此,Spring Boot 应用启动完成,可以接收和处理外部请求。

  

4. SpringBoot如何加载主配置类?

在Spring Boot中,主配置类通常是使用@SpringBootApplication注解标注的类。这个注解包含了三个主要的元注解:

  1. @Configuration:表示该类是一个配置类,用于声明Bean的定义或者通过@ComponentScan来扫描组件。
  2. @EnableAutoConfiguration:开启自动配置功能,Spring Boot将基于你的类路径和一些其他条件来自动配置相应的Bean。
  3. @ComponentScan:默认情况下,它会扫描当前包及其子包下的@Component、@Service、@Repository、@Controller等注解的类,将它们注册为Bean。

加载主配置类的过程如下:

  1. 当SpringApplication.run()方法被调用时,Spring Boot开始初始化过程。
  2. Spring Boot会查找带有@SpringBootApplication注解的类,并将其所在包作为根扫描范围。
  3. 使用@ComponentScan进行组件扫描,将找到的@Component、@Service、@Repository、@Controller等注解的类加入到Bean定义中。
  4. @EnableAutoConfiguration会触发Spring Boot的自动配置机制,Spring Boot根据类路径中的jar依赖和其他配置信息,自动配置相关Bean。
  5. 之后,SpringBoot会创建ApplicationContext,并将这些Bean加载到容器中。
  6. 最后,剩余的非懒加载的Bean将在ApplicationContext完全启动后被初始化。

5. 如何限制@ComponentScan的扫描范围?

要限制@ComponentScan的扫描范围,你可以指定要扫描的具体包名。以下是如何操作的:

@SpringBootApplication
@ComponentScan(basePackages = {"com.example.myapp"}) // 把这里替换为你想要扫描的包
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

在这个例子中,只有com.example.myapp包及其子包下的@Component、@Service、@Repository、@Controller等注解的类会被扫描并注册为Bean。

6.1 SpringBoot的启动器(Starters)有哪些?

可以在官网查看:Developing with Spring Boot

6.2 如何添加额外的配置到Spring Boot启动类?

要在Spring Boot启动类中添加额外的配置,你可以使用@ConfigurationProperties注解来绑定特定的配置文件属性。首先,创建一个配置类,然后在该类上添加@ConfigurationProperties注解。例如,如果你想处理应用的数据库连接配置,可以这样做:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "database")
public class DatabaseConfig {

    private String url;
    private String username;
    private String password;

    // Getters and Setters
    ......
}

在这个例子中,prefix = "database"意味着会从application.ymlapplication.properties文件中的database.*属性读取值。

然后,在主启动类中,添加@EnableConfigurationProperties注解,启用配置属性:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties({DatabaseConfig.class})
public class CustomizedSpringBootApplication {

    public static void main(String[] args) {
        SpringApplication.run(CustomizedSpringBootApplication.class, args);
    }

}

这样,Spring Boot就会将配置文件中的database.*属性值注入到DatabaseConfig实例中。

7. 嵌入式 Tomcat 如何在 Spring Boot 中工作?

嵌入式 Tomcat 在 Spring Boot 中的工作流程如下:

  1. 自动配置:当 Spring Boot 发现类路径中有 spring-boot-starter-web 包,它会自动配置一个内嵌的 Tomcat 服务器。

  2. 创建 ServletContext:Spring Boot 自动为 Tomcat 创建 ServletContext,这是所有 web 应用的基础。

  3. 注册 DispatcherServlet:Spring Boot 将 DispatcherServlet 注册到 Tomcat 中,它是 Spring MVC 框架的核心组件,负责处理 HTTP 请求。

  4. 加载 MVC 配置DispatcherServlet 加载默认的 MVC 配置或自定义的 @Configuration 类中的配置。

  5. 启动 Tomcat:Spring Boot 应用启动时,内嵌的 Tomcat 也会随之启动,并监听配置的端口(默认是 8080)。

  6. 接收与处理请求:Tomcat 接收到 HTTP 请求后,将其转发给 DispatcherServletDispatcherServlet 根据映射规则找到合适的处理器(如 Controller)并调用。

  7. 返回响应:处理器执行完业务逻辑后,返回一个 ModelAndView 或直接返回 ResponseEntityDispatcherServlet 将其转换成 HTTP 响应并发送回客户端。

8.1 如何在 Spring Boot 中添加过滤器(Filter)?

在 Spring Boot 中添加过滤器(Filter)可以按照以下步骤进行:

// 1. 创建 Filter 实例
@Component
public class MyFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 在这里添加你的过滤逻辑,例如修改请求或响应
        System.out.println("MyFilter: Before Request Processing");
        
        // 继续传递请求到下一个过滤器或者目标资源
        chain.doFilter(request, response);

        System.out.println("MyFilter: After Request Processing");
    }

    // 其他可选的方法,如 init 和 destroy
}
// 2. 注册 Filter 到 Spring 容器中
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private MyFilter myFilter;

    // 3. 使用 addFilters 方法将 Filter 添加到 Spring Boot 的应用上下文中
    @Override
    public void addFilters(FilterRegistrationBean... filterRegistrations) {
        FilterRegistrationBean registration = new FilterRegistrationBean<>();
        registration.setFilter(myFilter);
        registration.setName("myFilter"); // 可以指定过滤器名称
        registration.addUrlPatterns("/*"); // 设置需要过滤的 URL 模式
        registration.setOrder(ORDER); // 可设置过滤器的执行顺序,ORDER 越小优先级越高
        filterRegistrations.add(registration);
    }
}

8.2 为什么要使用 Filter?有什么具体应用场景?

Filter(过滤器)在 Java web 应用开发中起到很重要的作用,它允许你在请求被处理之前和之后进行拦截操作。以下是使用 Filter 的一些主要原因和应用场景:

  1. 数据转换:对请求参数进行校验、格式化或加密解密,确保传入的数据符合预期。

  2. 安全控制:实现权限验证,比如登录检查,防止未授权的访问。也可以用于实现跨站请求伪造(CSRF)防护。

  3. 缓存管理:在某些场景下,你可以使用 Filter 来缓存响应内容,提高性能。

  4. 日志记录:记录请求和响应信息,便于跟踪和调试应用程序。

  5. 性能优化:压缩响应内容,减少网络传输的数据量。

  6. 字符编码转换:处理请求和响应中的编码问题,避免乱码。

  7. 集成第三方库:与第三方服务集成时,可能需要通过 Filter 进行中间件通信,比如 Google Analytics 或其他追踪系统。

8.3 为什么需要对请求参数进行校验,有哪些常见的校验规则?

请求参数校验是为了确保输入的数据符合应用程序的期望,防止因为非法或者不正确的数据引发错误,保护系统安全,以及提升用户体验。以下是一些常见的校验规则:

  1. 非空性(Not Null):确保必填字段已填写,不接受null值。

  2. 长度限制(Length):限制字符串类型的字段长度,如最小长度和最大长度。

  3. 数字范围(Range):对于整数或浮点数,设置最小值和最大值。

  4. 日期格式(Date Format):要求日期字段遵循特定的格式,如"yyyy-MM-dd"。

  5. 邮箱格式(Email):验证邮箱地址是否合法,遵循标准的电子邮件格式。

  6. URL格式(URL):确认链接是否为有效的URL格式。

  7. 唯一性(Uniqueness):例如,在数据库中检查用户名或ID是否已经存在。

  8. 正则表达式匹配(Regex):根据业务需求,使用正则表达式来检查特定的模式,如电话号码格式。

  9. 自定义校验(Custom Validation):针对特定业务逻辑创建自定义校验规则。

8.4 你知道哪些Java库可以用来简化请求参数的校验吗?

Java中有一些库可以帮助简化请求参数的校验,其中最常用的是javax.validation(JSR 303/JSR 349)和Hibernate Validator。以下是使用这些库的一些基本步骤:

  1. 添加依赖:在你的项目中引入Hibernate Validator库,如果你使用Maven,可以在pom.xml文件中添加如下依赖:
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>版本号</version>
</dependency>
  1. 使用注解:在需要校验的字段上添加相应的验证注解,如@NotNull@Size@Min@Max, 等等。

  2. 创建验证器:如果需要自定义验证,可以通过实现ConstraintValidator接口创建自己的验证器。

  3. 运行时校验:使用ValidatorFactory获取Validator实例,然后调用validate()方法来进行校验。

  4. 处理结果:validate()方法会返回一个ConstraintViolation集合,你可以遍历这个集合以查看和处理校验失败的情况。

以下是一个简单的使用Hibernate Validator进行参数校验的Java示例:

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

public class User {

    @NotNull
    private String name;

    @Min(value = 18, message = "年龄必须大于等于18岁")
    private int age;

    // 构造函数、getter和setter省略...
}

然后,在需要校验的地方:

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

public class Main {

    public static void main(String[] args) {
        User user = new User();
        user.setName("Alice");
        user.setAge(17);  // 不满足年龄条件

        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();

        Set<ConstraintViolation<User>> violations = validator.validate(user);
        if (!violations.isEmpty()) {
            for (ConstraintViolation<User> violation : violations) {
                System.out.println(violation.getMessage());
            }
        } else {
            System.out.println("验证成功");
        }
    }
}

在这个例子中,我们创建了一个User类,并对name字段使用了@NotNull注解要求不能为空,对age字段使用了@Min(18)注解要求最小值为18。在main方法中,我们创建了一个User对象并校验其属性,如果校验失败,则打印出对应的错误消息。

9. Spring Boot 中的 Bean 生命周期是怎样的?

在Spring Boot中,Bean的生命周期主要包括以下几个阶段:

  1. 创建Bean:Spring通过定义的Bean工厂(如DefaultListableBeanFactory)来创建Bean实例,通常使用无参构造器。

  2. 属性注入:Spring会填充Bean的属性,这包括通过@Autowired注解自动装配、@Value注解注入值以及@Qualifier注解选择正确的依赖等。

  3. 初始化方法调用:如果定义了初始化方法(如 InitializingBean 的 afterPropertiesSet() 或者自定义的 initMethod),Spring会在所有属性注入完成后调用这个方法。

  4. BeanPostProcessor回调:如果有注册了BeanPostProcessor,那么它的postProcessBeforeInitialization()方法会在Bean初始化之前被调用,postProcessAfterInitialization()则在初始化之后。

  5. Bean的作用域:Bean可以在单例(Singleton)、原型(Prototype)或其他自定义作用域内。对于单例Bean,Spring只创建一次;对于原型Bean,每次请求都会创建一个新的实例。

  6. 容器关闭时的通知:如果是单例Bean并且实现了DisposableBean接口或配置了destroy-method,那么当Spring容器关闭时,这些销毁方法会被调用。

  7. 销毁Bean:在容器关闭时,执行Bean的销毁操作。

10.1 如何禁用Spring Boot的部分自动配置?

要禁用Spring Boot的部分自动配置,你可以采取以下步骤:

  1. 排除自动配置类:在你的主配置类(通常标注有@SpringBootApplication的类)上添加@SpringBootConfiguration@EnableConfigurationProperties注解。然后,使用@ExcludeAutoConfiguration注解来指定要排除的自动配置类。例如,如果你想禁用Web MVC配置,可以这样写:

    @SpringBootConfiguration
    @EnableConfigurationProperties
    @ ExcludeAutoConfiguration(WebMvcAutoConfiguration.class)
    public class MyApplication {
        // ...
    }
    
  2. 使用spring.autoconfigure.exclude属性:你也可以在application.propertiesapplication.yml文件中,通过spring.autoconfigure.exclude属性来指定要排除的自动配置类全名。例如,在application.properties中:

    spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration
    
  3. 调整Spring Boot Starter的依赖:如果你不想完全禁止但想降低某些特性的影响,也可以考虑不引入相应的Starter。例如,如果不想要Web相关的自动配置,就避免引入spring-boot-starter-web

注意,禁用自动配置可能会导致其他依赖于它的服务失效或行为异常。因此,建议只在确实需要的情况下才进行排除,并理解这将对应用产生的影响。

10.2 如何通过`@EnableAutoConfiguration`排除自动配置?

通过@EnableAutoConfiguration注解的exclude属性,你可以排除不想使用的Spring Boot自动配置。以下是具体步骤:

  1. 在你的主配置类(通常带有@SpringBootApplication)上添加@EnableAutoConfiguration注解。
  2. 使用exclude属性,指定要排除的自动配置类。这些类通常是org.springframework.boot.autoconfigure.*AutoConfiguration形式的全限定名。

这是一个示例:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; // 这是你想排除的自动配置类
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) // 排除数据源自动配置
@ComponentScan(basePackages = {"com.example.myapp"}) // 扫描你的包
public class MyAppApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyAppApplication.class, args);
    }

}

在这个例子中,我们排除了DataSourceAutoConfiguration,这意味着Spring Boot不会自动配置数据源,你需要自己手动配置。

11.1 Spring Boot如何动态切换数据源?

在Spring Boot中动态切换数据源通常可以通过以下步骤实现:

1) 创建数据源配置:首先,为每个数据源创建对应的配置类,使用@ConfigurationProperties来自定义配置的属性,并且声明不同的DataSource bean。

@Configuration
@ConfigurationProperties(prefix = "db.primary")
public class PrimaryDataSourceConfig {
    private String url;
    private String username;
    private String password;
    // getters and setters
}

@Configuration
@ConfigurationProperties(prefix = "db.secondary")
public class SecondaryDataSourceConfig {
    private String url;
    private String username;
    private String password;
    // getters and setters
}

2) 创建多数据源bean:定义一个配置类,使用@Profile注解来标记不同环境下的数据源,并通过@Primary指定默认数据源。

@Configuration
public class DataSourceConfig {

    @Bean
    @Profile("dev")
    @Primary
    public DataSource primaryDataSource(PrimaryDataSourceConfig config) {
        return DataSourceBuilder.create().url(config.getUrl()).username(config.getUsername()).password(config.getPassword()).build();
    }

    @Bean
    @Profile("!dev")
    public DataSource secondaryDataSource(SecondaryDataSourceConfig config) {
        return DataSourceBuilder.create().url(config.getUrl()).username(config.getUsername()).password(config.getPassword()).build();
    }
}

3) 切换数据源:默认情况下,Spring Boot会根据spring.profiles.active属性来选择激活的配置。你可以通过修改这个属性值(比如设置为prod),在运行时切换到不同的数据源。也可以在代码中动态切换,但需要更复杂的逻辑。

4) 配置事务管理器:确保@EnableTransactionManagement@Transactional注解可用,并根据当前活动的数据源配置正确的PlatformTransactionManager

@Configuration
@EnableTransactionManagement
public class TransactionManagerConfig {

    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;

    @Autowired
    @Qualifier("secondaryDataSource")
    private DataSource secondaryDataSource;

    @Bean(name = "primaryTransactionManager")
    PlatformTransactionManager primaryTransactionManager() {
        return new JpaTransactionManager(primaryDataSource);
    }

    @Bean(name = "secondaryTransactionManager")
    PlatformTransactionManager secondaryTransactionManager() {
        return new JpaTransactionManager(secondaryDataSource);
    }
}

5) 在业务代码中选择数据源:根据需求,可以选择使用哪个数据源。可以通过@Resource注入不同的事务管理器来决定数据源。

@Service
public class MyService {

    @Resource(name = "primaryTransactionManager")
    private PlatformTransactionManager transactionManager;

    // 使用primary数据源进行操作
}
@Service
public class AnotherService {

    @Resource(name = "secondaryTransactionManager")
    private PlatformTransactionManager transactionManager;

    // 使用secondary数据源进行操作
}

11.2 如何在没有`@Profile`的情况下,基于代码逻辑动态切换数据源?★★★

在没有@Profile的情况下,可以使用Environment来获取配置信息并在运行时动态切换数据源。以下是一个简单的示例:

1) 创建数据源配置类:与之前相同,创建两个数据源的配置。

@Configuration
@ConfigurationProperties(prefix = "db.primary")
public class PrimaryDataSourceConfig {
    // properties...
}

@Configuration
@ConfigurationProperties(prefix = "db.secondary")
public class SecondaryDataSourceConfig {
    // properties...
}

2) 创建数据源切换工具类:创建一个类来管理数据源的切换。

@Component
public class DynamicDataSourceHolder {

    @Autowired
    private Environment env;

    private static final ThreadLocal<String> DATA_SOURCE_KEY_HOLDER = new ThreadLocal<>();

    public void switchToPrimary() {
        DATA_SOURCE_KEY_HOLDER.set("primary");
    }

    public void switchToSecondary() {
        DATA_SOURCE_KEY_HOLDER.set("secondary");
    }

    public String getCurrentDataSourceKey() {
        return DATA_SOURCE_KEY_HOLDER.get();
    }

    public void clearDataSourceKey() {
        DATA_SOURCE_KEY_HOLDER.remove();
    }
}

3) 创建数据源切换策略:实现AbstractRoutingDataSource,它允许我们在运行时动态决定要使用的数据源。

@Configuration
public class DynamicDataSourceConfig {

    @Autowired
    private PrimaryDataSourceConfig primaryDataSourceConfig;

    @Autowired
    private SecondaryDataSourceConfig secondaryDataSourceConfig;

    @Bean
    public DataSource dynamicDataSource() {
        AbstractRoutingDataSource dataSource = new AbstractRoutingDataSource() {
            @Override
            protected Object determineCurrentLookupKey() {
                return DynamicDataSourceHolder.getCurrentDataSourceKey();
            }
        };

        dataSource.setTargetDataSources(Map.of(
                "primary", primaryDataSourceConfig,
                "secondary", secondaryDataSourceConfig
        ));

        // 设置默认数据源
        dataSource.setDefaultTargetDataSource(primaryDataSourceConfig);

        return dataSource;
    }
}

4) 在业务代码中切换数据源:在需要的地方调用DynamicDataSourceHolder的方法来切换数据源。

@Service
public class ServiceWithSwitchableDataSource {

    @Autowired
    private DynamicDataSourceHolder dataSourceHolder;

    @Autowired
    @Lazy
    private DataSource dataSource; // autowire the dynamic datasource

    public void doSomething() {
        dataSourceHolder.switchToPrimary();
        // your code using 'dataSource' here, which will use the primary data source...

        dataSourceHolder.switchToSecondary();
        // your code using 'dataSource' here, which will use the secondary data source...
    }
}

11.3 你能解释一下`AbstractRoutingDataSource`的工作原理吗?

AbstractRoutingDataSource是Spring框架提供的一种抽象数据源,它可以动态地路由到其他数据源。它的主要工作原理如下:

  1. 初始化时配置目标数据源:在AbstractRoutingDataSource的子类中,你需要设置targetDataSources属性,这通常是一个映射,其中键代表不同的数据源标识(如字符串),值为实际的数据源对象。

  2. 确定当前数据源:通过覆盖determineCurrentLookupKey()方法,你可以返回当前应该使用的数据源的标识。这个方法将在每次请求数据库连接时被调用。

  3. 动态选择数据源:在上述determineCurrentLookupKey()方法中,你可以根据特定的上下文环境(例如,线程局部变量、请求参数等)来决定哪个数据源将用于提供连接。

  4. 获取数据源连接:一旦determineCurrentLookupKey()返回了标识,AbstractRoutingDataSource会使用这个标识从targetDataSources映射中获取对应的数据源,并返回其连接。

  5. 默认数据源:如果找不到匹配的数据源标识,AbstractRoutingDataSource会使用defaultTargetDataSource作为默认的数据源。

这里是一个简单的示例:

@Configuration
public class DynamicDataSourceConfig extends AbstractRoutingDataSource {

    @Autowired
    private DataSource primaryDataSource;
    @Autowired
    private DataSource secondaryDataSource;

    @Override
    protected Object determineCurrentLookupKey() {
        // 可能会从ThreadLocal或者其它方式获取当前活跃的数据源标识
        return Thread.currentThread().getId();  // 假设每个线程对应不同的数据源
    }

    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        super.targetDataSources = targetDataSources;
    }

    @Override
    public void afterPropertiesSet() {
        Map<Object, Object> dataSources = new HashMap<>();
        dataSources.put(1, primaryDataSource);
        dataSources.put(2, secondaryDataSource);
        setTargetDataSources(dataSources);
        defaultTargetDataSource = primaryDataSource;
    }
}

11.4 当找不到匹配的数据源时,`AbstractRoutingDataSource`会怎么做?

AbstractRoutingDataSource找不到与当前上下文关联的匹配数据源标识时,它将会使用defaultTargetDataSource作为默认的数据源。defaultTargetDataSource是在afterPropertiesSet()方法中设置的,如果没有明确设置,AbstractRoutingDataSource会在初始化时抛出异常,因为它需要至少有一个默认的数据源。如果设置了allowMultipleDbValues属性为true,则不会抛出异常,而是允许有多个数据源值。

11.5 数据源切换的场景有哪些?

在Spring Boot应用中,数据源切换通常用于处理多租户或多环境的情况,例如:

  1. 多租户系统:在一个应用程序中服务于多个客户(租户),每个租户可能有自己的数据库以保持数据隔离。

  2. 开发/测试/生产环境切换:在不同的环境中使用不同的数据库配置,例如开发环境的数据源和生产环境的数据源可能不同。

  3. 负载均衡:在分布式系统中,为了实现数据库的负载均衡,可能需要在多个数据库实例之间切换读写操作。

  4. 数据迁移:在进行数据库升级或数据迁移的过程中,可能需要短暂地切到备份数据库以保证服务的连续性。

  5. 故障恢复:当主数据库出现故障时,可以自动切换到备用数据库,确保系统的高可用性。

12. SpringBoot配置的优先级从高到低依次是什么?★★

Spring Boot配置的优先级从高到低依次是:

  1. 命令行参数:使用启动命令时附带的参数,比如--server.port=8080,这种配置具有最高优先级。

  2. 外部配置文件:可通过spring.config.location环境变量指定,包括文件或目录路径。如果指定了多个配置文件,它们的优先级与提供的顺序有关,后出现的文件会覆盖前面的同名配置。

  3. java -jar的配置:使用java -jar启动时,通过--spring.config.location参数指定的额外配置文件。

  4. 特定环境的配置文件:基于当前激活的spring.profiles.active环境,例如application-${profile}.propertiesapplication-${profile}.yml

  5. 内置类路径中的配置:按照以下顺序检查:

    • src/main/resources/config/application.properties 或 src/main/resources/config/application.yml
    • src/main/resources/META-INF/resources/application.properties  或 src/main/resources/META-INF/resources/application.yml
    • src/main/resources/application.properties 或 src/main/resources/application.yml
    • BOOT-INF/classes/config/application.properties 或 BOOT-INF/classes/config/application.yml
    • BOOT-INF/classes/application.properties 或 BOOT-INF/classes/application.yml

每一个新的位置都会覆盖前一个位置的同名配置。如果存在多个相同环境的配置文件,它们会被一起处理,但文件内的顺序可能会影响最终结果。

验证过程说明:(以端口号为例)

1. 最高优先级是命令行参数,可以通过java -jar --server.port=8080 xxx.jar配置,也可以在IDEA中通过编辑Environment variables【server.port=8080】​​​​​​​进行配置;

2. 外部配置文件,可以通过下面3种方式配置:

注意:上面这三种制定外部配置文件时,application.yaml文件中不能只有端口,还得有像Redis、MySQL那些启动项目需要的配置哦,否则会启动报错,也即:需要和resources目录下配置文件中的内容相同才行!因为外部指定的配置文件会覆盖resources下面的配置文件。

3. 第三、四种优先级的,略。

4. 下面4个内置文件的优先级如下:

​​​​​​​

五、图灵B站15道面试题​​​​​​​

腾讯二面:SpringBoot 有几种读取配置文件的方式?你详细说一下每种方式是怎么使用的 ?_哔哩哔哩_bilibili

1. SpringBoot 有几种读取配置文件的方式?你详细说一下每种方式是怎么使用的 ?

2. 为什么会出现跨域问题 ?你详细说一下SpringBoot解决跨域的方案 !

3. SpringBoot项目启动慢,如何优化springboot的启动速度 ?

4. SpringBoot可以同时处理多少请求 ?

5. 为什么SpringBoot使用cglib作为默认动态代理 ?AOP使用jdk动态代理会有什么问题 ?

6. 什么是Spring Boot自动装配?你说一下SpringBoot自动配置原理?★★★

7. 你知道SpringBoot项目是如何启动的吗?说说SpringBoot的启动原理?★★★

8. 你有没有看过源码?详细说说SpringBoot内置Tomcat启动过程与原理 ?

9. 为什么SpringBoot的jar可以直接运行?有哪些原因 ?

10. SpringBoot的默认日志实现框架是什么?怎么切换成别的?

11. 说说你在开发的时候怎么在SpringBoot的基础上做扩展?

  • Spring:有接口使用JDK动态代理,没有接口使用cglib动态代理;
  • SpringBoot:强制使用cglib动态代理。

12. springboot读取配置文件原理是什么 ? 加载顺序优先级是怎么样的 ?

13. 如何使用 SpringBoot 自定义 starter?你说下大概实现过程 ?

14. 简历上你写的看过源码,你说说SpringBoot外部Tomcat启动原理?

打包方式改为war,排除tomcat依赖,创建一个类继承SpringBootServletInitializer并重写configure方法,将启动类传入其中。

15. SpringBoot有哪些核心注解 ?

  • @SpringBootApplication注解:这个注解标识了一个SpringBoot 工程,它实际上是另外三个注解的组合,其中两个注解是:
  • @SpringBootConfiguration:这个注解实际就是一个@Configuration,表示启动类也是一个配置类
  • @EnableAutoConfiguration:向Spring容器中导入了一个Selector,用来加载ClassPath下SpringFactories中所定义的自动配置类,将这些自动加载为配置Bean
  • @Conditional也很关键,如果没有它我们无法在自定义应用中进行定制开发
    • @ConditionalOnBean
    • @ConditionalOnMissingBean
    • @ConditionalOnClass
    • @ConditionalOnExpression
    • @ConditionalOnMissingExpression等等

另外,像@Bean、@Import、@Configuration这些严格说来,其实是Spring的注解。

Spring Boot的核心注解主要用于简化配置和快速启动Spring应用。以下是几个关键的注解:

  1. @SpringBootApplication:这是最常用的注解,它结合了@Configuration(表示这是一个配置类),@EnableAutoConfiguration(开启自动配置)和@ComponentScan(扫描@Component及其子注解如@Service, @Repository等)三个注解的功能。

  2. @Configuration:这个注解用于标记一个类为@Configuration bean,它允许你在其中定义bean的实例化逻辑。

  3. @EnableAutoConfiguration:当这个注解添加到类上时,Spring Boot会根据项目中的依赖自动配置bean。例如,如果项目中有JDBC库,那么Spring Boot会自动配置数据源。

  4. @Component@Service@Repository@Controller:这些是Spring的组件注解,分别用于标记普通bean、业务层bean、数据访问层bean和控制器层bean。

  5. @RestController:在@Controller之上,添加了@RequestMapping注解的默认行为,用于创建RESTful服务。

  6. @Autowired:用于注入bean,Spring会自动将匹配的bean注入到标注的字段或方法中。

  7. @Value:用于注入配置属性值,可以是从application.properties或application.yml文件中读取的。

  8. @Bean:该注解用于声明一个方法将返回的对象注册为Spring容器中的bean。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值