星空网 > 软件开发 > Java

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

1.       背景

1.1.       背景介绍

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

1.2.       测试项目搭建

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

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

 

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

<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>

添加以下依赖

<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>

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

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

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

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

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

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

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

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:开始");
        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 {

   
}

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

 

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初始化");
        /**
         *
在这里写需要执行的代码
         */
   
}

    publicvoid contextDestroyed(ServletContextEventsce) {

    }
}

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

servletContext.addListener(SimpleServletListener.class);

 

 

2.2.       基于javaweb的Filter

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

public class SimpleFilter implements Filter {
    Logger logger= LoggerFactory.getLogger(SimpleConsumer.class);

    public void init(FilterConfig filterConfig) throwsServletException {
        logger.debug("Boot Sequence:在web初始化配置中配置的Filter初始化");

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

    publicvoid doFilter(ServletRequestservletRequest, ServletResponseservletResponse, FilterChainfilterChain) throws IOException, ServletException {

    }
    publicvoid destroy() {

    }
}

 

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

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

 

2.3.       基于javaweb的servlet

  1. 在servlet包下新建SimpleServlet继承HttpServlet

public class SimpleServlet extends HttpServlet {

    Logger logger= LoggerFactory.getLogger(WebInitializer.class);


    @Override
    publicvoid init() throws ServletException {
        logger.debug("Boot Sequence:在web初始化配置中配置的Servlet初始化");
        //在这里写需要执行的代码
        super.init();
    }
}

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

ServletRegistration.Dynamic simpleServlet =servletContext.addServlet("SimpleServlet", new SimpleServlet());
simpleServlet.setLoadOnStartup(2);

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

 

2.4.       基于Spring的ApplicationListener

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

@Component
public class SimpleApplicationListenerimplements ApplicationListener<ContextRefreshedEvent>{
    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 SimpleBeanFactoryPostProcessorimplements BeanFactoryPostProcessor{

    Logger logger= LoggerFactory.getLogger(WebInitializer.class);

    public void postProcessBeanFactory(ConfigurableListableBeanFactoryconfigurableListableBeanFactory) throws BeansException {
        logger.debug("BootSequence:bootFactory的后置处理器执行");

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

2.5.2 BeanPostProcessor

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

@Component
public class SimpleBeanPostProcessor implements BeanPostProcessor {
    //注意:此接口中的方**在初始化每一个Bean时都执行一次
    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("BootSequence:SimpleConsumer的初始化之后执行");

           //在这里编写需要执行的代码
        }else{
            logger.debug("Other Bean:的初始化之后执行");
        }
        returno;
    }
}

 

2.6.       基于Spring的InitializingBean

  1. 在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.       执行顺序

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

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

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

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

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

 

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

启动顺序

方案名称

解释

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别看名字长的差不多,其实里面的内容差距很大,

  1. BeanFactoryPostProcessor是在当Spring容器已经获取到所有的Bean初始化列表,并创建BeanFactory后才调用的后置处理器,此时所有的Bean都还未初始化
  2. BeanPostProcessor是会在每初始化一个Bean都会调用其中的postProcessAfterInitialization方法,此时可能已经初始化了一些Bean

3.3.2  Initializing中的AfterPropertiesSet与

如果我们通过

执行顺序

其实可以在本项目中再加上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 如何选择

  1. 如果我们需要在web容器刚初始化就执行程序的话需要采用实现ServletContextListenre的方案来执行
  2. 如果我们需要spring容器中的所有内容都加载完毕的话要采用实现ApplicationContextListener的方案来执行。

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

 

4.       总结

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

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

原标题:web容器启动后自动执行程序的几种方式比较

关键词:web

web
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

深圳到越南货运:https://www.goluckyvip.com/tag/104902.html
深圳货运到越南:https://www.goluckyvip.com/tag/104903.html
到越南物流多少钱:https://www.goluckyvip.com/tag/104904.html
物流到越南多少钱:https://www.goluckyvip.com/tag/104905.html
到越南发什么物流:https://www.goluckyvip.com/tag/104906.html
越南胡志明专线:https://www.goluckyvip.com/tag/104907.html
千岛湖绿城度假酒店的简介:https://www.vstour.cn/a/363185.html
深圳大湾区游轮夜景 深圳大湾区游轮夜景图:https://www.vstour.cn/a/363186.html
相关文章
我的浏览记录
最新相关资讯
海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流