MST
星途 面试题库

面试题:Java 的 Spring Boot 启动过程中如何优化自定义 Starter 的加载性能

在开发自定义 Starter 时,Spring Boot 启动时会加载大量配置,描述你所知道的优化加载性能的方法,如资源预加载、按需加载等策略及其实现方式。
32.0万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

资源预加载

  1. 原理:在应用启动阶段提前加载一些必要的资源,避免在运行时才加载导致的延迟。
  2. 实现方式
    • 使用 @PostConstruct 注解:在自定义 Starter 的某个配置类中,定义一个方法并使用 @PostConstruct 注解。例如:
import org.springframework.stereotype.Component;

@Component
public class ResourcePreloader {

    @PostConstruct
    public void preloadResources() {
        // 这里进行资源预加载的逻辑,比如加载数据库连接池的配置等
    }
}
  • 使用 ApplicationListener<ContextRefreshedEvent>:创建一个监听器类实现 ApplicationListener<ContextRefreshedEvent> 接口,在 onApplicationEvent 方法中进行资源预加载。
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class MyContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 资源预加载逻辑
    }
}

按需加载

  1. 原理:只有在实际需要使用某个配置或资源时才进行加载,避免不必要的初始化开销。
  2. 实现方式
    • 懒加载配置类:在配置类上使用 @Lazy 注解。例如:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

@Configuration
@Lazy
public class LazyConfig {

    @Bean
    public SomeService someService() {
        return new SomeService();
    }
}
  • 使用 ObjectProvider:在需要使用某个 bean 的地方,通过 ObjectProvider 来获取,只有在真正调用 getIfAvailable 等方法获取 bean 时才会进行初始化。例如:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    private final ObjectProvider<SomeService> someServiceProvider;

    @Autowired
    public MyService(ObjectProvider<SomeService> someServiceProvider) {
        this.someServiceProvider = someServiceProvider;
    }

    public void doSomething() {
        someServiceProvider.getIfAvailable(someService -> {
            // 使用 someService 进行业务处理
            return null;
        });
    }
}
  • 条件化配置(@Conditional:通过 @Conditional 注解,根据一定的条件来决定是否加载某个配置类或 bean。例如,只有在特定环境变量存在时才加载某个配置:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ConditionalConfig {

    @Bean
    @Conditional(MyCondition.class)
    public SomeService someService() {
        return new SomeService();
    }
}
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) {
        // 判断环境变量等条件
        String someEnvVar = context.getEnvironment().getProperty("some.env.var");
        return "specificValue".equals(someEnvVar);
    }
}

减少不必要配置加载

  1. 原理:移除那些确实不需要的配置,减少 Spring Boot 启动时的加载负担。
  2. 实现方式
    • 分析项目需求:仔细梳理项目功能,确认哪些配置是真正必需的。例如,如果项目不使用消息队列,那么相关的消息队列配置(如 RabbitMQ 等)就可以移除。
    • 排除自动配置:使用 @SpringBootApplication(exclude = {SomeAutoConfiguration.class}) 来排除不需要的自动配置类。例如,如果不想加载 Spring Data JPA 的自动配置,可以这样写:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jpa.HibernateJpaAutoConfiguration;

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

优化配置文件读取

  1. 原理:提高配置文件的读取效率,避免复杂的解析过程。
  2. 实现方式
    • 使用属性占位符优化:在配置文件中合理使用属性占位符,减少重复配置。例如,在 application.properties 中:
app.base-url = http://example.com
app.api-url = ${app.base-url}/api
  • 配置文件结构优化:保持配置文件结构清晰,避免过多层级嵌套。例如,将相关配置放在同一层级下,而不是嵌套过深。比如对于数据库配置:
# 良好的结构
spring.datasource.url = jdbc:mysql://localhost:3306/mydb
spring.datasource.username = root
spring.datasource.password = password

# 避免这样过度嵌套(虽然 Spring Boot 支持)
spring.datasource.connection.url = jdbc:mysql://localhost:3306/mydb
spring.datasource.connection.username = root
spring.datasource.connection.password = password