Perform a specific action after Spring Boot starts, then stop automatically

foreword

We originally used Spring Boot in web projects. The execution sequence is to start the embedded tomcat container → all services are on standby → wait for the request to come → process the request, and so on.

question

But here comes the problem. Now there is a special requirement that Spring Boot only needs to be executed once. The order is to start Spring Boot → execute some methods of the service → stop the container. The whole process does not require manual intervention. Spring Boot executes automatically after startup, and stops automatically after execution, instead of hanging and waiting as before.

analyze

First of all, by analyzing the requirements, we can find that there are mainly two special points:
1. Perform specific operations after starting Spring Boot;
2. Automatically stop after performing operations;

solve

For the first question, the answer is easy to find online. Just add a Listeners:

1
2
3
4
5
6
public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent> {

    public void onApplicationEvent(ContextRefreshedEvent event)     {
       // operation to be performed
    }
}

 

Add to SpringApplication:

1
2
3
4
5
public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(Application.class);
        springApplication.addListeners(new ApplicationStartup());
        springApplication.run(args);
    }

 

The focus is on the second question: stop Spring Boot automatically , searched all over the search engine and couldn't find a way, it should be because this requirement is rather strange, and most people don't use it like this. But I still have to solve the problem, so I thought, under what circumstances will Spring Boot stop? Abnormal! Yes, in the process of starting Spring Boot, as long as there is an exception, the container will stop, which is a common problem in the development process. Then when an exception is found, there must be some mechanism, to be precise, some code makes the container stop. After finding the clue, the method is naturally there. After I finished the operation in Spring Boot, I ApplicationStartupmanually threw an exception at the end, and then turned on the debug mode and traced it: I
found EmbeddedWebApplicationContextthat there is a very obvious stop operation in the class. :

1
2
3
4
5
6
7
8
9
public final void refresh() throws BeansException, IllegalStateException {
        try {
            super.refresh();
        } catch (RuntimeException var2) {
            // That's it!
            this.stopAndReleaseEmbeddedServletContainer();
            throw var2;
        }
    }

 

Going to the method, I found the EmbeddedWebApplicationContextculprit located in the class:

1
2
3
4
5
6
7
8
9
10
11
12
private void stopAndReleaseEmbeddedServletContainer() {
        EmbeddedServletContainer localContainer = this.embeddedServletContainer;
        if(localContainer != null) {
            try {
                localContainer.stop();
                this.embeddedServletContainer = null;
            } catch (Exception var3) {
                throw new IllegalStateException(var3);
            }
        }

    }

 

That's it localContainer.stop()! So far, we know how Spring Boot stops the container, which is EmbeddedServletContainerthe stopmethod to call. So we just need to get EmbeddedServletContainerit.

To get EmbeddedServletContainerit, you have to get it first EmbeddedWebApplicationContext. Observe EmbeddedWebApplicationContextand find that it is called Context. Is it implemented from ApplicationContext? Through the reference relationship, I found that it is indeed implemented ApplicationContext, which is easy to handle, because in daily development, we often need to obtain ApplicationContextand obtain beans, and a tool class has been encapsulated BeanTool:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Component
public class BeanTool extends ApplicationObjectSupport implements ApplicationContextAware {
    static ApplicationContext context;
    private static ApplicationContext applicationContext = null;

    public static void setApplicationContext(WebApplicationContext applicationContext) {
        BeanTool.applicationContext = applicationContext;
    }

    public BeanTool getInstance() {
        return new BeanTool();
    }

    protected void initApplicationContext(ApplicationContext context) {
        super.initApplicationContext(context);
        if (applicationContext == null) {
            applicationContext = context;
        }
    }

    public static ApplicationContext getAppContext() {
        return applicationContext;
    }

    public static Object getBean(String name) {
        return getAppContext().getBean(name);
    }

    public static Object getBean(Class clazz) {
        return getAppContext().getBean(clazz);
    }
}

 

This tool class needs to be registered in the Spring Boot entry class:

1
2
3
4
5
6
7
/**
 * bean tool class, you can get the beans created by spring in the common class
 */
@Bean
public BeanTool beanTool() {
    return new BeanTool();
}

 

现在就差最后一步了!在ApplicationStartup的最后,调用BeanTool获取EmbeddedWebApplicationContext,从而获取EmbeddedServletContainer,最后调用其stop方法:

1
2
EmbeddedWebApplicationContext context = (EmbeddedWebApplicationContext)BeanTool.getAppContext();
context.getEmbeddedServletContainer().stop();

 

执行,可以看到执行完操作后,容器停止了,大功告成!

最后

从这个的经历中,可以看到问题的产生,问题的分析和问题的解决之中的是如何思考的。面对未知的问题,只要采取合适的方法,一步一步试错,一定能找到解决的方法。

“你个人的项目,应该有四分之一会失败,否则就说明你的冒险精神不够。”(Expect and hope that a quarter of your projects fail. If not, you’re not taking enough risks. –Adam Smith)



------本文结束  感谢阅读------

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325238473&siteId=291194637