java web 启动时执行_web容器启动后自动执行程序的几种方式比较

本文探讨了在Java Web项目启动时自动执行任务的需求,并提供了多种解决方案的比较,包括基于ServletContextListener、Filter、Servlet、Spring的ApplicationListener、BeanFactoryPostProcessor、BeanPostProcessor和InitializingBean的实现。文章详细阐述了各种方法的执行顺序、优缺点,帮助开发者根据实际需求选择合适的方法。
摘要由CSDN通过智能技术生成

1.       背景

1.1.       背景介绍

在web项目中我们有时会遇到这种需求,在web项目启动后需要开启线程去完成一些重要的工作,例如:往数据库中初始化一些数据,开启线程,初始化消息队列等,在这种需求下,如何在web容器启动后执行这些工作就成为了本文的重点。

1.2.       测试项目搭建

首先我们新建一个web项目来模拟这种需求,这里我们选择创建一个maven项目

6bc38aee10fdda05b84a353e3fac86ae.png

在项目的pom文件中添加以下properties项

3.1.0

4.3.8.RELEASE

1.1.7

1.7.5

添加以下依赖

javax

javaee-web-api

8.0

provided

org.springframework

spring-core

4.3.8.RELEASE

org.springframework

spring-webmvc

4.3.8.RELEASE

javax.servlet

javax.servlet-api

${servlet.version}

provided

org.slf4j

slf4j-api

${slf4j.version}

org.slf4j

jcl-over-slf4j

${slf4j.version}

ch.qos.logback

logback-classic

${logback.version}

ch.qos.logback

logback-core

${logback.version}

ch.qos.logback

logback-access

${logback.version}

junit

junit

3.8.1

test

等待maven构建完成之后我们就可以开始搭建一个用于测试的web项目

常规项目中基本上会搭配spring框架来构建,这里我们也不例外,这里我们使用0配置文件来构建一个web项目

有关spring0配置文件构建项目可以在网上找到很多资料,这里就至简单的搭建一个,不作详细解释

1.首先在项目src目录下建立包结构如下

2d0f971a252f40dc77442acaa414b348.png

2.在config目录下建立以下两个文件用于配置本项目

c3e7dabee68918e3df8d6c80db572ded.png

public class WebInitializer implements WebApplicationInitializer

{

Logger logger

= LoggerFactory.getLogger(WebInitializer.class);

public void onStartup(ServletContext servletContext) throws

ServletException {

AnnotationConfigWebApplicationContext ctx = new

AnnotationConfigWebApplicationContext();

ctx.register(MyConfig.class);

logger.debug("Boot sequence:开始");

ctx.setServletContext(servletContext);

//ctx.refresh();

ServletRegistration.Dynamic

servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));

servlet.addMapping("/");

servlet.setLoadOnStartup(1);

servlet.setAsyncSupported(true);

}

}

@Configuration

@EnableWebMvc

@ComponentScan("com.hei123")

public class MyConfig extends WebMvcConfigurerAdapter {

}

到这里为止,这个测试项目就已经搭建完成了,接下来介绍几种常见的解决方案

2.       几种解决方案

2.1.       基于javaweb的ServletContextListener

1、在listener包下新建类SimpleServletListener实现ServletContextListener接口

public class SimpleServletListener implements ServletContextListener {

Logger logger

= LoggerFactory.getLogger(SimpleConsumer.class);

public void contextInitialized(ServletContextEvent sce) {

logger.debug("Boot Sequence:监听ServletContext的监听器监听到ServletContext初始化");

/**

*在这里写需要执行的代码

*/}

public

void contextDestroyed(ServletContextEvent

sce) {

}

}

2、在WebInitializer类中的onStartup尾部添加如下代码

servletContext.addListener(SimpleServletListener.class);

2.2.       基于javaweb的Filter

在filter包下新建SimpleFilter类实现Filter接口

public class SimpleFilter implements Filter {

Logger logger

= LoggerFactory.getLogger(SimpleConsumer.class);

public void init(FilterConfig filterConfig) throws

ServletException {

logger.debug("Boot Sequence:在web初始化配置中配置的Filter初始化");

//在这里写需要执行的代码

}

public

void doFilter(ServletRequest

servletRequest, ServletResponse

servletResponse, FilterChain

filterChain) throws IOException, ServletException {

}

public

void destroy() {

}

}

在WebInitializer类中的onStartup尾部添加如下代码

servletContext.addFilter("SimpleFilter", SimpleFilter.class);

2.3.       基于javaweb的servlet

在servlet包下新建SimpleServlet继承HttpServlet

public class SimpleServlet extends HttpServlet {

Logger logger

= LoggerFactory.getLogger(WebInitializer.class);

@Override

public

void init() throws ServletException {

logger.debug("Boot Sequence:在web初始化配置中配置的Servlet初始化");

//在这里写需要执行的代码

super.init();

}

}

在WebInitializer类中的onStartup尾部添加如下代码

ServletRegistration.Dynamic simpleServlet =

servletContext.addServlet("SimpleServlet", new SimpleServlet());

simpleServlet.setLoadOnStartup(2);

//这里设置为2是因为需要先启动springMVC的dispatcherServlet

2.4.       基于Spring的ApplicationListener

在listener包下新建SimpleApplicationListener类实现ApplicationListener接口来监听spring容器启动完成的事件

@Component

public class SimpleApplicationListener

implements ApplicationListener

{

Logger logger

= LoggerFactory.getLogger(SimpleConsumer.class);

public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

logger.debug("Boot Sequence: 监听spring 容器Context初始化的的方法调用");        //在这里写需要执行的代码

}

}

2.5.       基于Spring的PostProcessor

2.5.1 BeanFactoryPostProcessor

在postprocessor包下新建SimpleBeanFactoryPostProcessor类实现BeanFactoryPostProcessor接口

@Component

public class SimpleBeanFactoryPostProcessor

implements BeanFactoryPostProcessor

{

Logger logger

= LoggerFactory.getLogger(WebInitializer.class);

public void postProcessBeanFactory(ConfigurableListableBeanFactory

configurableListableBeanFactory) throws BeansException {

logger.debug("Boot

Sequence:bootFactory的后置处理器执行");

//在这里编写需要执行的代码

}

}

2.5.2 BeanPostProcessor

在postprocessor包下新建SimpleBeanPostProcessor类实现BeanPostProcessor接口

@Component

public class SimpleBeanPostProcessor implements BeanPostProcessor {

//注意:此接口中的方法会在初始化每一个Bean时都执行一次

Logger

logger = LoggerFactory.getLogger(WebInitializer.class);

public Object

postProcessBeforeInitialization(Object

o, String s) throws

BeansException {

return

o;

}

public

Object postProcessAfterInitialization(Object o, String s) throws BeansException {

if(o instanceof SimpleConsumer){

logger.debug("Boot

Sequence:SimpleConsumer的初始化之后执行");

//在这里编写需要执行的代码

}else{

logger.debug("Other Bean:的初始化之后执行");

}

return

o;

}

}

2.6.       基于Spring的InitializingBean

在initializingbean包下新建SimpleConsumer类实现InitializingBean接口

@Component

public class SimpleConsumer implements InitializingBean {

Logger logger

= LoggerFactory.getLogger(SimpleConsumer.class);

public void afterPropertiesSet() throws Exception

{

logger.debug("Boot sequence:SimpleConsumer Bean 依赖注入完成");

//在这里编写需要执行的代码

}

}

3.       解决方案之间的对比

3.1.       执行顺序

780f516792d28b62eaa292ec387eff78.png

07bec79ad4b6d5a346dc9a2a5abeb9b1.png

756d02139b4eb911b456b7b59386363d.png

2b1b68064d4e5ca62f63501f4b8357e5.png

dd84e182e99de739359c864bf1994036.png

我在测试项目中添加了以上所有的几种解决方案从日志中可以看到几种方案的执行顺序

启动顺序

方案名称

解释

1

基于javaweb的ServletContextListener

监听webContext初始化

2

基于javaweb的filter

WebContext初始化后会先加载定义的过滤器,然后才会加载定义的Servlet,而这里的spring容器也是借助定义的DispatcherServlet来初始化的。

3

基于spring的BeanFactoryPostProcessor

4

基于spring的InitializingBean

在SimpleConsumer的属性注入完成后执行

5

基于spring的BeanPostProcessor

在SimplerConsumer初始化完成后执行

6

基于spring的ApplicationContextListener

在spring容器初始化完所有的Bean后执行

7

基于javaweb的servlet

在配置中我们将其执行顺序设置为2,此servlet将会在DispatcherServlet初始化完成后才会去初始化,因此会落在最后

3.2.       横向对比

Servlet

ContextListener

filter

BeanFactory

PostProcessor

Initializing

Bean

Bean

PostProcessor

Application

ContextListener

servlet

自动执行

引用其他类的变量或方法

×

×

×

*不确定

*不确定

Spring容器是否已进行属性注入

×

×

×

当前Bean所有属性已注入,且其属性中引用的其他属性也已注入

当前Bean所有属性已注入,且其属性中引用的其他属性也已注入

Web容器完全启动

×

×

×

×

×

×

×

*指若其他Bean已经初始化完成可引用,未初始化完成的Bean不可引用

3.3.       细节详解

3.3.1 BeanFactoryPostProcessor与BeanPostProcessor的区别

BeanFactoryPostProcessor与BeanPostProcessor别看名字长的差不多,其实里面的内容差距很大,

BeanFactoryPostProcessor是在当Spring容器已经获取到所有的Bean初始化列表,并创建BeanFactory后才调用的后置处理器,此时所有的Bean都还未初始化

BeanPostProcessor是会在每初始化一个Bean都会调用其中的postProcessAfterInitialization方法,此时可能已经初始化了一些Bean

3.3.2  Initializing中的AfterPropertiesSet与xml配置的init-method以及BeanPostProcessor之间的区别

如果我们通过xml配置文件来配置spring中的Bean的话,其中可以通过init-method配置一个用于初始化方法,那这三者之间有什么区别呢?

执行顺序

其实可以在本项目中再加上init-method来验证执行顺序,这里就不再去加了,直接解释好了,其实仅从名称上就可以看出执行顺序应为

AfterPropertiesSetàinitMethodàbeanPostProcessor

只有当属性设置完成之后,此Bean才算基本上创建完成,即afterPropertiesSet,

在Bean创建完成之后,可以对此Bean进行一些初始化操作,即init-method

在初始化完成之后,调用Bean的后置处理器来完成一些其他的操作

这一点可以在源码中查看,这里就不做多的解释了

3.3.3 web容器的启动顺序

1.在web容器启动时会所有的webContextListener会收到web容器启动的通知,并可以执行其中的监听方法

2.接下来web容器会先去初始化所有的filter过滤器

3.然后web容器会根据servlet的初始化顺序去初始化所有的Servlet,在本例中DispacherServlet启动顺序为1最大,

4.DispacherServlet中会去初始化spring容器

5.初始化其他的Servlet

6.web容器启动完成

3.3.4 如何选择

如果我们需要在web容器刚初始化就执行程序的话需要采用实现ServletContextListenre的方案来执行

如果我们需要spring容器中的所有内容都加载完毕的话要采用实现ApplicationContextListener的方案来执行。

总之,我们需要根据自己的实际情况来选择对应的方案来达到最好的效果

4.       总结

本文主要介绍了几种在web容器启动后自动执行代码的解决方案,并对这些解决方案进行了一些大概的分析,对其中的一些细节内容进行了一些解释,详细的解释需要通过观察源码才能对这些内容有更好的理解,不足之处,还请指正。

任何问题请联系hei12138@outlook.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值