1. Background 1.1. Background introduction
In Web projects we sometimes encounter this need, after the launch of the Web project needs to open the thread to do some important work, such as: to initialize some data in the database, open the thread, initialize the message queue, in this demand, how to execute after the Web container startup is the focus of this article.
1.2. Test Project Setup
First we create a new Web project to simulate this requirement, and here we choose to build a MAVEN project
Add the following properties items to the project's Pom file
<properties>
<!--web-->
<servlet.version>3.1.0</servlet.version>
<!--spring-->
<spring-framework.version>4.3.8.RELEASE</spring-framework.version>
<!--logging-->
<logback.version>1.1.7</logback.version>
<slf4j.version>1.7.5</slf4j.version>
</properties>
Add the following dependencies
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
Wait until the MAVEN build is complete and we can start building a Web project for testing
Regular projects are basically built with the spring framework, and here we are no exception, where we use 0 configuration files to build a Web project
For SPRING0 configuration file construction project can find a lot of information on the Internet, here to simple build one, without detailed explanation
1. First set up the package structure under the Project SRC directory as follows
2. Create the following two files in the Config directory to configure this project
public class Webinitializer implements webapplicationinitializer{
Logger logger= loggerfactory.GetLogger(Webinitializer.class);
public void Onstartup (ServletContext servletcontext) throwsservletexception {
Annotationconfigwebapplicationcontext CTX = Newannotationconfigwebapplicationcontext ();
Ctx.register (Myconfig.class);
Logger.debug ("Boot sequence: Start");
Ctx.setservletcontext (ServletContext);
Ctx.refresh ();
Servletregistration.dynamicservlet = Servletcontext.addservlet ("Dispatcher", New Dispatcherservlet (CTX));
Servlet.addmapping ("/");
Servlet.setloadonstartup (1);
Servlet.setasyncsupported (TRUE);
}
}
@Configuration
@EnableWebMvc
@ComponentScan ("com.hei123")
public class Myconfig extends Webmvcconfigureradapter {
}
By this point, the test project has been completed, followed by several common solutions
2. Several solutions
2.1. Javaweb-based Servletcontextlistener
1, the new class Simpleservletlistener implementation Servletcontextlistener interface under the Listener package
public class Simpleservletlistener implements Servletcontextlistener {
Logger logger= loggerfactory. GetLogger (Simpleconsumer.class);
public void contextinitialized (Servletcontextevent sce) {
Logger.debug ("Boot Sequence: Listener ServletContext listeners hear ServletContext initialization");
/**
* Write the code that needs to be executed here
*/
}
Publicvoid contextdestroyed (SERVLETCONTEXTEVENTSCE) {
}
}
2. Add the following code to the Onstartup tail of the Webinitializer class
Servletcontext.addlistener (Simpleservletlistener.class);
2.2. Javaweb-based filter
- To implement the filter interface under the filter package, create a new Simplefilter class
public class Simplefilter implements Filter {
Logger logger= loggerfactory. GetLogger (Simpleconsumer.class);
public void init (Filterconfig filterconfig) throwsservletexception {
Logger.debug ("Boot Sequence: Filter initialization configured in the Web Initialization Configuration");
Write the code that needs to be executed here
}
Publicvoid DoFilter (Servletrequestservletrequest, Servletresponseservletresponse, Filterchainfilterchain) throws IOException, Servletexception {
}
Publicvoid Destroy () {
}
}
- Add the following code to the tail of Onstartup in the Webinitializer class
Servletcontext.addfilter ("Simplefilter", Simplefilter.class);
2.3. Javaweb-based Servlet
- New Simpleservlet inherit HttpServlet under Servlet package
public class Simpleservlet extends HttpServlet {
Logger logger= loggerfactory. GetLogger (Webinitializer.class);
@Override
Publicvoid init () throws Servletexception {
Logger.debug ("Boot Sequence: servlet initialization configured in Web initialization Configuration");
Write the code that needs to be executed here
Super.init ();
}
}
- Add the following code to the tail of Onstartup in the Webinitializer class
Servletregistration.dynamic simpleservlet =servletcontext.addservlet ("Simpleservlet", New SimpleServlet ());
Simpleservlet.setloadonstartup (2);
This is set to 2 because you need to start SPRINGMVC's Dispatcherservlet first
2.4. Spring-based Applicationlistener
- The new Simpleapplicationlistener class implements the Applicationlistener<contextrefreshedevent> interface under the listener package to listen for events that the spring container started to complete
@Component
public class Simpleapplicationlistenerimplements applicationlistener<contextrefreshedevent>{
Logger logger= loggerfactory. GetLogger (Simpleconsumer.class);
public void Onapplicationevent (Contextrefreshedevent contextrefreshedevent) {
Logger.debug ("Boot Sequence: Method call to listen for the spring container context initialization"); Write the code that needs to be executed here
}
}
2.5. Spring-based PostProcessor2.5.1 Beanfactorypostprocessor
The new Simplebeanfactorypostprocessor class implements the Beanfactorypostprocessor interface under the Postprocessor package
@Component
public class Simplebeanfactorypostprocessorimplements beanfactorypostprocessor{
Logger logger= loggerfactory. GetLogger (Webinitializer.class);
public void Postprocessbeanfactory (configurablelistablebeanfactoryconfigurablelistablebeanfactory) throws beansexception {
Logger.debug ("Bootsequence:bootfactory processor execution");
Write the code that needs to be executed here
}
}
2.5.2 Beanpostprocessor
The new Simplebeanpostprocessor class implements the Beanpostprocessor interface under the Postprocessor package
@Component
public class Simplebeanpostprocessor implements Beanpostprocessor {
Note: The methods in this interface are executed once for each bean initialization
Loggerlogger = Loggerfactory. GetLogger (Webinitializer.class);
Public Objectpostprocessbeforeinitialization (Objecto, String s) throwsbeansexception {
Returno;
}
Publicobject postprocessafterinitialization (Object o, String s) throws Beansexception {
if (o instanceof simpleconsumer) {
Logger.debug ("Execution after initialization of Bootsequence:simpleconsumer");
Write the code that needs to be executed here
}else{
Logger.debug ("Other Bean: Executes after initialization");
}
Returno;
}
}
2.6. Spring-based Initializingbean
- The new Simpleconsumer class implements the Initializingbean interface under the Initializingbean package
@Component
public class Simpleconsumer implements Initializingbean {
Logger logger= loggerfactory. GetLogger (Simpleconsumer.class);
public void Afterpropertiesset () throws exception{
Logger.debug ("Boot sequence:simpleconsumer Bean Dependency Injection complete");
Write the code that needs to be executed here
}
}
3. Comparison between Solutions
3.1. Execution order
I have added all of the above solutions in my test project from the log to see the execution order of several scenarios
Boot order |
Scheme name |
Explain |
1 |
Servletcontextlistener based on the Javaweb |
Listening Webcontext initialization |
2 |
Filter based on the Javaweb |
Webcontext initializes the defined filter before loading the defined servlet, and the spring container here is initialized with the defined Dispatcherservlet. |
3 |
Spring-based Beanfactorypostprocessor |
|
4 |
Spring-based Initializingbean |
Executes after the Simpleconsumer property injection is complete |
5 |
Spring-based Beanpostprocessor |
Executes after Simplerconsumer initialization is complete |
6 |
Spring-based Applicationcontextlistener |
Executes after the spring container initializes all the beans |
7 |
Javaweb-based Servlet |
In the configuration we set the order of execution to 2, and this servlet will not initialize until the Dispatcherservlet initialization is complete, so it will fall to the last |
3.2. Horizontal contrast
|
Servlet Contextlistener |
Filter |
Beanfactory Postprocessor |
Initializing Bean |
Bean postprocessor |
Application Contextlistener |
servlet |
Auto execute |
√ |
√ |
√ |
√ |
Yes |
√ |
Strong>√ |
references a variable or method of another class |
x |
|
x |
* unsure |
* unsure |
√ |
√ |
Whether the spring container has attribute injection |
x |
x |
X |
All properties of the current bean have been injected and other properties referenced in its properties have been injected |
All properties of the current bean have been injected and other properties referenced in its properties have been injected |
√ |
√ |
Web Container fully started |
X |
X |
X |
X |
X |
X |
X |
* refers to a bean that is not initialized to be referenced if another bean has been initialized to complete
3.3. Details 3.3.1 The difference between Beanfactorypostprocessor and beanpostprocessor
Beanfactorypostprocessor and beanpostprocessor do not look like the name long, in fact, the contents of the gap is very large,
- Beanfactorypostprocessor is a post processor that is called after the spring container has acquired all the bean initialization lists and created the Beanfactory, and all the beans are not initialized at this time
- Beanpostprocessor is the Postprocessafterinitialization method that will be called at each initialization of a bean, at which point it may have initialized some beans
The difference between afterpropertiesset in 3.3.2 Initializing and the Init-method of XML configuration and Beanpostprocessor
If we were to configure a bean in spring through an XML configuration file, which could be configured with a Init-method method for initialization, what is the difference between the three?
Execution order
In fact, this project can add Init-method to verify the order of execution, here is no longer to add, directly explained, in fact, only from the name can be seen in the execution sequence should be
Afterpropertiessetàinitmethodàbeanpostprocessor
This bean is basically created when the property is set to complete, that is, Afterpropertiesset,
After the bean is created, you can perform some initialization operations on the bean, that is, the Init-method
After initialization is complete, call the Bean's back processor to do some other things
This can be viewed in the source code, here is not a long explanation
3.3.3 the start order of web containers
1. When the Web container starts, all Webcontextlistener will receive a notification that the Web container is started and can execute the listening method in it.
2. Next, the Web container will initialize all filter filters First
3. The Web container then initializes all servlets according to the order in which the servlet is initialized, in this case the Dispacherservlet boot order is 1 MAX,
The spring container is initialized in 4.DispacherServlet
5. Initialize the other servlet
6.web Container Start complete
3.3.4 How to choose
- If we need to execute the program when the Web container is just initialized, we need to implement the SERVLETCONTEXTLISTENRE scheme.
- If we need all the content in the spring container to be loaded, we will implement the Applicationcontextlistener scheme.
In short, we need to according to their actual situation to choose the corresponding scheme to achieve the best results
4. Summary
This article mainly introduces several kinds of solutions for automatic code execution after web container startup, and gives some general analysis of these solutions, some of which are explained in detail, and the detailed explanations need to be better understood by observing the source material, and the shortcomings, please correct me.
Any questions please contact [email protected]
Comparison of several ways to automatically execute programs after Web container startup