This is my ninth day of the Genwen Challenge

Often in our project development, there are situations where some business code needs to be executed automatically as soon as the service is started.

  • Cache configuration information from the database or data dictionary to Redis
  • Start some configured scheduled tasks when the service starts.

There are many ways that Spring MVC or SpringBoot can execute some code at project startup.

1. UseContextRefreshedEventEvent (up and down file refresh event)

Official documentation: ContextRefreshedEvent doc description on the interface

Event raised when an {@code ApplicationContext} gets initialized or refreshed.


ContextRefreshedEvent is an implementation of Spring’s ApplicationContextEvent. The ContextRefreshedEvent event is triggered when the Spring container is initialized and refreshed.

In this case, I need to load the configuration information and dictionary information into the Redis cache after the SpringBoot program starts. I can write:

@Component All three require Spring to scan the class and hand it over to manage
public class InitRedisCache implements ApplicationListener<ContextRefreshedEvent> {
    static final Logger logger = LoggerFactory
            .getLogger(InitRedisCache.class);

    @Autowired
    private SysConfigService sysConfigService;

    @Autowired
    private SysDictService sysDictService;


    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        logger.info("------- Load configuration information start-------");
        sysConfigService.loadConfigIntoRedis();
        logger.info("------- Load configuration information end-------");

        logger.info("------- load dictionary information start-------");
        sysDictService.loadDictIntoRedis();
        logger.info("------- load dictionary information end-------"); }}Copy the code

Note: This approach was used in the (older) SpringMVC-Spring projects where it was executed twice. This is because two containers are created when Spring and SpringMVC are loaded, and both trigger the execution of this event. All you need to do is check if there is a parent in the onApplicationEvent method.

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
    if(event.getApplicationContext().getParent() == null) {// Root application context has no parent, it is the boss.
    	// The logical code that needs to be executed. This method will be executed when the Spring container is initialized.}}Copy the code

2.@PostConstructannotations

Since the Java EE5 specification, servlets have been added with two annotations that affect the life cycle of servlets, @postConstruct and @Predestroy, which are used to modify a non-static void() method. The @postconstruct is executed after the constructor of the class, and before the init() method. (The @predestroy annotation is executed after the class’s destory() method.)

** After the Spring container is loaded, I need to start a scheduled task to do the task processing (my scheduled task is to read the database configuration).

Here I use ** @postconstruct ** to specify the method I want to start.

@Component // Note that there must be one here
public class StartAllJobInit {

    protected Logger logger = LoggerFactory.getLogger(getClass().getName());
    @Autowired
    JobInfoService jobInfoService;

    @Autowired
    JobTaskUtil jobTaskUtil;

    @PostConstruct // The constructor is executed after
    public void init(a){
        System.out.println("Execute after container is started");
        startJob();
    }

    public void startJob(a) {
        List<JobInfoBO> list = jobInfoService.findList();
        for (JobInfoBO jobinfo :list) {
            try {
                if("0".equals(jobinfo.getStartWithrun())){
                    logger.info("Task {} is not set to start automatically.", jobinfo.getJobName());
                    jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STOP);
                }else{
                    logger.info("Task {} is set to start automatically.", jobinfo.getJobName()); jobTaskUtil.addOrUpdateJob(jobinfo); jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STARTING); }}catch (SchedulerException e) {
                logger.error("Error executing scheduled task, task name {}", jobinfo.getJobName()); }}}}Copy the code

3. The implementationCommandLineRunnerInterface to override the run() method

The CommandLineRunner interface is described as follows:

/**
 * Interface used to indicate that a bean should <em>run</em> when it is contained within
 * a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined
 * within the same application context and can be ordered using the {@link Ordered}
 * interface or {@link Order @Order} annotation.
 * <p>
 * If you need access to {@link ApplicationArguments} instead of the raw String array
 * consider using {@link ApplicationRunner}.
 *
 * @author Dave Syer
 * @see ApplicationRunner
 */
public interface CommandLineRunner {

  /**
	 * Callback used to run the bean.
	 * @param args incoming main method arguments
	 * @throws Exception on error
	 */
  void run(String... args) throws Exception;

}
Copy the code

As mentioned above: When the interface is used to join the Spring container, run(String… Args) method to pass arguments through the command line. SpringBoot iterates through all entity classes implementing CommandLineRunner and executes the Run method after a project is started. Multiple implementation classes can coexist and be executed in order according to the Order annotations. There is also an ApplicationRunner interface, but the received arguments are using ApplicationArguments. I won’t repeat it here.

To perform a scheduled task on startup, I write as follows:

@Component // Note that there must be one here
// @order (2) If more than one class needs to be started, the values in the Order of the Order annotation are executed
public class StartAllJobInit implements CommandLineRunner {

    protected Logger logger = LoggerFactory.getLogger(getClass().getName());
    @Autowired
    JobInfoService jobInfoService;

    @Autowired
    JobTaskUtil jobTaskUtil;

    @Override
    public void run(String... args) {
        List<JobInfoBO> list = jobInfoService.findList();
        for (JobInfoBO jobinfo :list) {
            try {
                if("0".equals(jobinfo.getStartWithrun())){
                    logger.info("Task {} is not set to start automatically.", jobinfo.getJobName());
                    jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STOP);
                }else{
                    logger.info("Task {} is set to start automatically.", jobinfo.getJobName()); jobTaskUtil.addOrUpdateJob(jobinfo); jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STARTING); }}catch (SchedulerException e) {
                logger.error("Error executing scheduled task, task name {}", jobinfo.getJobName()); }}}}Copy the code