• Spring Boot在启动过程中我们可以实现哪些干预工作?
  • 发布于 2个月前
  • 286 热度
    0 评论
Spring Boot启动过程中我们可以实现以下干预工作:
1.修改Spring Boot默认的配置属性:使用@ConfigurationProperties和@EnableConfigurationProperties注解,可以获取和修改Spring Boot的配置属性。
2.加载配置文件:Spring Boot会自动加载application.properties或application.yml等配置文件,我们可以在启动时加载其他配置文件。
3.自定义bean:我们可以通过@Component注解创建自定义的bean以及其他的@SpringBootAnnotation注解,来实现更灵活的配置和自动化初始化。
4.执行一些初始化逻辑:我们可以对应用程序的数据库、缓存、MQ等进行初始化,例如创建数据源、初始化缓存等,以确保应用程序正常运行,并且可以通过ApplicationRunner和CommandLineRunner等干预代码的方式执行这些初始化逻辑。
5.执行一些后置操作:在Spring Boot应用程序停止后执行一些清理工作,例如关闭数据源、释放缓存等。
这些干预步骤可以在Spring Boot应用程序启动和停止完成后进行,从而实现更灵活的配置和初始化。

(一)ApplicationContextInitializer扩展
通过实现ApplicationContextInitializer接口,我们可以在ApplicationContext创建之前对其进行一些定制化的修改。这个接口定义了一个initialize方法,接受一个ConfigurableApplicationContext对象作为参数,我们可以在这个方法中对这个对象进行修改和配置。

具体来说,我们可以通过ApplicationContextInitializer实现以下扩展任务:
1.修改Spring Boot默认的environment属性:使用configurableApplicationContext.getEnvironment()方法获取到environment对象,从而修改环境变量,例如添加自定义配置文件路径。
2.添加自定义的PropertySource:使用environment.getPropertySources().addLast(propertySource)方法,可以添加自定义的属性源,从而实现更灵活的配置。
3.注册自定义bean:使用configurableApplicationContext.getBeanFactory().registerSingleton(beanName, bean)方法,可以注册自定义的bean,从而实现更灵活的依赖注入。
4.可以添加自定义的ApplicationContextInitializer实现类,从而扩展应用程序的初始化逻辑。

总之,通过实现ApplicationContextInitializer接口,可以在Spring Boot应用程序启动之前对应用程序进行一些初始化定制化的操作,从而满足开发者对应用程序的特殊需求。

修改Spring Boot默认的environment属性
修改Spring Boot默认的environment属性,例如添加自定义配置文件路径,可以通过实现ApplicationContextInitializer接口来完成。下面是一个示例代码,演示如何添加自定义配置文件路径:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.ResourcePropertySource;
 
import java.io.IOException;
 
/**
 * @description 修改Spring Boot默认的environment属性。使用configurableApplicationContext.getEnvironment()方法获取到environment对象,从而修改环境变量,例如添加自定义配置文件路径。
 */
public class InterveneApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        //堆代码 duidaima.com
        ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
        // 添加自定义配置文件路径
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            System.out.println("InterveneApplicationContextInitializer initialize :" + configurableApplicationContext);
            environment.getPropertySources().addFirst(new ResourcePropertySource("classpath:zyftest.properties"));
            System.out.println("InterveneApplicationContextInitializer initialize add FirstResourcePropertySource  classpath:zyftest.properties");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
在上面的代码中,我们通过getEnvironment()方法获取到ConfigurableEnvironment对象,然后通过getPropertySources()方法获取到属性源,使用addFirst()方法将自定义的custom.properties文件的PropertySource添加到属性源的首位。这样,在应用程序启动时,就会首先加载custom.properties文件,从而实现了自定义的配置。

需要注意的是,上述代码中的InterveneApplicationContextInitializer需要被注册才能生效。可以通过在src/main/resources/META-INF/spring.factories文件中指定注册项的方式来注册:
org.springframework.context.ApplicationContextInitializer=
org.zyf.javabasic.springextend.runext.InterveneApplicationContextInitializer
添加自定义的PropertySource
添加自定义的PropertySource,可以通过实现ApplicationContextInitializer接口来完成。下面是一个示例代码,演示如何添加自定义的PropertySource:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.ResourcePropertySource;
 
import java.io.IOException;
 
/**
 * @description 添加自定义的PropertySource。使用environment.getPropertySources().addLast(propertySource)方法,可以添加自定义的属性源,从而实现更灵活的配置。
 */
public class InterveneApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        // 添加自定义的PropertySource
        PropertySource<?> propertySource = new MyPropertySource("myPropertySource");
        environment.getPropertySources().addLast(propertySource);
        System.out.println("InterveneApplicationContextInitializer initialize add PropertySource  myPropertySource");
    }
 
    // 自定义PropertySource
    private static class MyPropertySource extends PropertySource<String> {
        private static final String MY_PROPERTY_SOURCE_KEY = "my.property.source.key";
 
        public MyPropertySource(String name) {
            super(name);
        }
 
        @Override
        public Object getProperty(String name) {
            if (MY_PROPERTY_SOURCE_KEY.equals(name)) {
                return "myPropertySourceValue";
            }
            return null;
        }
    }
}
在上面的代码中,我们新建了一个名为MyPropertySource的自定义PropertySource,然后在initialize方法中使用environment.getPropertySources().addLast(propertySource)方法将其添加到Spring环境中。

MyPropertySource中实现了一个用于获取属性的getProperty方法,在这个方法中,我们指定了一个名为my.property.source.key的属性及其对应的值,这样就可以通过@Value("${my.property.source.key}")的方式在应用程序中获取到它的值了。

需要注意的是,上述代码中的InterveneApplicationContextInitializer需要被注册才能生效。具体如上部分展示,这里不在展示。

注册自定义bean
注册自定义Bean,可以通过实现ApplicationContextInitializer接口来完成。下面是一个示例代码,演示如何注册自定义Bean:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.ResourcePropertySource;
 
import java.io.IOException;
 
/**
 * @description 注册自定义bean。使用configurableApplicationContext.getBeanFactory().registerSingleton(beanName, bean)方法,可以注册自定义的bean,从而实现更灵活的依赖注入。
 */
public class InterveneApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
 
        ConfigurableListableBeanFactory beanFactory = configurableApplicationContext.getBeanFactory();
        // 注册自定义Bean
        MyBean myBean = new MyBean();
        beanFactory.registerSingleton("myBean", myBean);
        System.out.println("InterveneApplicationContextInitializer initialize registerSingleton  myBean");
    }
    // 堆代码 duidaima.com
    // 自定义Bean
    private static class MyBean {
        private String name = "myBean";
 
        public String getName() {
            return name;
        }
    }
}
在上面的代码中,我们新建了一个名为MyBean的自定义Bean,然后在initialize方法中使用beanFactory.registerSingleton("myBean", myBean)方法将其注册到Spring应用程序上下文中,"myBean"是注册的bean名称,myBean是实际的类实例对象。需要注意的是,上述代码中的InterveneApplicationContextInitializer需要被注册才能生效。具体如上部分展示,这里不在展示。

(二)SpringApplicationRunListener扩展
SpringApplicationRunListener是Spring Boot的一个事件监听器,用于在应用程序启动和停止时执行一些操作。可能需要自定义SpringApplicationRunListener来执行某些特定操作。下面是一个示例,演示如何扩展SpringApplicationRunListener以添加自定义操作:

首先,需要实现SpringApplicationRunListener接口:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
 
/**
 * @description SpringApplicationRunListener是Spring Boot的一个事件监听器,用于在应用程序启动和停止时执行一些操作。
 * 可能需要自定义SpringApplicationRunListener来执行某些特定操作。
 * 下面是一个示例,演示如何扩展SpringApplicationRunListener以添加自定义操作
 */
public class IntervenRunListener implements SpringApplicationRunListener {
 
    private final SpringApplication application;
 
    private final String[] args;
 
    public IntervenRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
    }
 
    @Override
    public void starting() {
        System.out.println("IntervenRunListener starting");
    }
 
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("IntervenRunListener environmentPrepared");
    }
 
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("IntervenRunListener contextPrepared");
    }
 
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("IntervenRunListener contextLoaded");
    }
 
    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("IntervenRunListener started");
    }
 
    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("IntervenRunListener running");
    }
 
    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("IntervenRunListener failed");
    }
}
在上述代码中,我们新建了一个名为IntervenRunListener的自定义SpringApplicationRunListener,并在starting()、environmentPrepared()、contextPrepared()、contextLoaded()、started()、running()和failed()方法中添加了自定义操作。

接下来,我们需要告诉Spring Boot使用这个自定义的SpringApplicationRunListener,上述代码中的IntervenRunListener需要被注册才能生效。可以通过在src/main/resources/META-INF/spring.factories文件中指定注册项的方式来注册:
org.springframework.boot.SpringApplicationRunListener=
org.zyf.javabasic.springextend.runext.IntervenRunListener
(三)ApplicationRunner扩展
ApplicationRunner是Spring Boot提供的一种扩展点,它允许在Spring Boot应用程序启动时执行一些预配置操作。这些操作可以包括预热缓存,初始化数据库连接等。以下是一些用途:
数据库初始化: ApplicationRunner可以用于执行数据库初始化操作。例如,我们可以在应用程序启动时创建数据库表格,插入初始数据等操作。这对于确保数据库的正确性和可用性非常有用,以及为整个应用程序提供更好的可维护性。
缓存预热: 缓存在应用程序中非常重要,它可以大大提高应用程序的性能。但是,由于缓存通常是“懒加载”的,所以在应用程序第一次使用它们时,需要花费一些时间来加载它们。使用ApplicationRunner,我们可以在应用程序启动后立即加载缓存,而不是等到应用程序第一次使用它们时加载。这可以大大减少应用程序响应时间,并提高用户体验。

环境检查: 为了确保应用程序能够正常运行,我们需要检查它所在的环境是否满足应用程序的要求。例如,我们可能需要检查数据库是否可用,检查文件系统是否可写等。使用ApplicationRunner,我们可以在应用程序启动时立即执行这些检查,并在应用程序无法正常运行时采取适当的措施,如打印警告或抛出异常。


总之,ApplicationRunner可以用于执行任何需要在应用程序启动时进行的操作。它为应用程序提供了一种简单的扩展点,可以使我们轻松地实现预配置和初始化操作。

以下从缓存预热和环境检查给出简单的代码示例。

缓存预热
在应用程序启动后立即加载缓存,可以避免在应用程序第一次使用缓存时的延迟。以下是一个简单的示例,演示如何使用ApplicationRunner在应用程序启动时加载缓存:
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
 
/**
 * @description 演示如何使用ApplicationRunner在应用程序启动时加载缓存
 */
@Component
public class CacheInitiator implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("CacheInitiator cache deal!");
    }
}
在应用程序启动时,ApplicationRunner接口的run方法将被自动调用,从而将一些初始数据加载到缓存中。当应用程序需要访问缓存时,它们可以立即从缓存中获取数据,而不必等待它们被“懒加载”的时间。这将大大提高应用程序的性能和用户体验。

环境检查
以下是一个简单的示例,演示如何使用ApplicationRunner在应用程序启动时执行环境检查:
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
 
/**
 * @description 演示如何使用ApplicationRunner在应用程序启动时执行环境检查
 */
@Component
public class EnvironmentChecker implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 检查数据库是否可用
        System.out.println("EnvironmentChecker DatabaseConnection checkConnection! ");
 
        // 检查文件系统是否可写
        System.out.println("EnvironmentChecker FileStorage checkWriteAccess! ");
    }
}
在应用程序启动时,ApplicationRunner接口的run方法将被自动调用,从而执行环境检查操作。当应用程序无法正常运行时,它们将抛出一个运行时异常,包含适当的错误消息,以帮助我们进行故障排除和修复操作。

(四)CommandLineRunner扩展
CommandLineRunner接口用于在应用程序启动时执行一些命令行操作。这在调试应用程序、自动化部署、初始化系统配置等方面非常有用。在较复杂的业务场景下,我们可以使用CommandLineRunner接口来扩展应用程序。

首先,我们可以将一些常用的业务逻辑封装在命令行工具中,然后在应用程序启动时通过执行这些命令来进行操作。例如,我们可以创建一个名为UserImportCommand的命令行工具,用于导入用户数据到应用程序中。在应用程序启动时,可以执行UserImportCommand来导入用户数据。

其次,我们可以使用CommandLineRunner接口来定制化应用程序的启动过程。例如,我们可以创建一个名为StartupTasks的类,并实现CommandLineRunner接口。在run方法中,我们可以执行任何我们需要在应用程序启动时完成的任务,如加载配置文件、初始化缓存等。

最后,我们可以结合使用ApplicationRunner和CommandLineRunner来完成更复杂的任务。例如,我们可以创建一个名为InitializationRunner的类,实现ApplicationRunner和CommandLineRunner接口,并在其中执行所有初始化任务。这样,在应用程序启动时,不仅可以自动执行初始化任务,还可以通过命令行手动执行这些任务。

综上所述,CommandLineRunner接口在业务扩展方面有着广阔的应用前景,在实际业务场景中,可以根据自己的需求进行灵活应用和扩展。

应用举例一:UserImportCommand命令行工具
以下是一个简单的示例,演示如何使用CommandLineRunner接口创建一个命令行工具,用于导入用户数据到应用程序中。
import com.google.common.collect.Lists;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.zyf.javabasic.common.User;
 
import java.util.List;
 
/**
 * @description 演示如何使用CommandLineRunner接口创建一个名为UserImportCommand的命令行工具,用于导入用户数据到应用程序中。
 */
@Component
public class UserImportCommand implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        List<User> users = readUserFromFile("fileName");
        System.out.println("UserImportCommand readUserFromFile importUsers!");
    }
 
    // 从数据文件中读取用户信息
    private List<User> readUserFromFile(String fileName) {
        // 省略代码,从文件中读取用户信息,返回一个User对象列表
        return Lists.newArrayList();
    }
}
在应用程序启动时,CommandLineRunner接口的run方法将被自动调用,并将命令行参数作为字符串数组传递给run方法。
上面展示的时候并没有使用入参,当需要导入用户数据时,可以执行如下命令:
java -jar myapp.jar user-import users.txt
其中,myapp.jar是应用程序运行的jar包,user-import是命令行工具的名称,users.txt是要导入的用户数据文件名。

应用举例二:定制化应用程序的启动过程
可以使用CommandLineRunner接口来定制化应用程序的启动过程。下面是一个示例,演示如何使用CommandLineRunner来执行一些自定义启动任务:
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
 
/**
 * @description 演示如何使用CommandLineRunner来执行一些自定义启动任务
 */
@Component
public class StartupTasks implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        // 加载应用程序配置
        System.out.println("StartupTasks configService loadConfig");
 
        // 初始化缓存
        System.out.println("StartupTasks cacheService initialize");
    }
}
在应用程序启动时,CommandLineRunner接口的run方法将被自动调用,并且我们的自定义启动任务将会被执行。通过使用CommandLineRunner接口,我们可以执行任何自定义的启动任务,以满足特定的应用程序需求。

(五)所有扩展验证
验证以上配置是否生效很简单,只需要启动程序查看打印效果即可,以下给出一些基本的展示效果:

用户评论