应用启动自动运行代码的方案
在我们开发项目时经常需要做一段在应用启动时自动执行的代码来执行一些前置任务,比如 检测配置、检测网络、启动调度任务等等。应对不同业务场景需要不同的解决方案,我会在这篇文章给大家介绍三种我熟悉的方案;它们的异同、各自的特性,包括具体实现。希望我的文章能给大家带来帮助,如有不足之处请大家不吝指教,共同进步。
先来了解下Web程序的生命周期。
Web程序生命周期
启动过程可插入代码的三个方案
- 初始化Servlet时:load-on-startup的Servlet
- 实例化SpringMVC上下文时:Spring MVC的InitializingBean
- 构造Bean时:@PostConstruct注解
Servlet是三个方案中第一个启动的,使用Servlet的一些方法可以方便获取应用相关信息,在这时系统还有很多组件可能还没启动也可以在这做一些改动之类的动作,这时它的优势。使用Servlet初始化的缺点是不能使用Spring代理的Bean:看图得知,因为这时Spring还没有对Bean做实例化、所以Bean肯定是无法使用的。如果要在初始化过程中使用Spring代理的对象(Spring托管的MyBatis)这个方案是不可用的。
Spring MVC的InitializingBean是三个方案中第二个启动的,这种方案实际上是针对于SpringMVC上下文的Bean实例化的一个监听器,它是处于Bean实例化之后的时间点上,也就是说此时Bean已经时可用状态了。一般情况下我都会使用这个方案来做启动业务。
第三个方案是@PostConstruct注解,它的执行时间点是比较个性的,一般Spring的Bean都是懒加载的,意味着只有在实际使用与加了这个注解相关的Bean时它才会被触发,相对于上两个方案都是伴随着应用启动而触发,这个就比较个性化了、灵活。缺点是应用启动时它 不一定会被触发,这是比较尴尬的。
下面我会给出这三个方案的实现,然后观察启动日志来观察它们的具体执行。
Web项目自启动实现
别忘了自行加入Bean、包扫描等设置。
Servlet自启动
在servlet的配置当中,load-on-startup 配置为 1 的含义是:标记容器是否在启动的时候就加载这个servlet。当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。正数的值越小,启动该servlet的优先级越高。 如果我们在web.xml中设置了多个servlet的时候,可以使用load-on-startup来指定servlet的加载顺序,服务器会根据load-on-startup的大小依次对servlet进行初始化。不过即使我们将load-on-startup设置重复也不会出现异常,服务器会自己决定初始化顺序。配置load-on-startup后,servlet在startup后立即加载,但只是调用servlet的init()方法,用以初始化该servlet相关的资源。初始化成功后,该servlet可响应web请求;如未配置load-on-startup,容器一般在第一次响应web请求时,会先检测该servlet是否初始化,如未初始化,则调用servlet的init()先初始化,初始化成功后,再响应请求。
加入Maven依赖库
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.example</groupId>
<artifactId>newWebApp</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
</project>
配置web.xml
<servlet>
<servlet-name>spring-web-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-MVC.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>spring-web-mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 伴随启动Servlet配置 start -->
<servlet>
<servlet-name>BootInitServletA</servlet-name>
<servlet-class>cn.Servlets.BootInitServletA</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>BootInitServletA</servlet-name>
<url-pattern>/BootInitServletA</url-pattern>
</servlet-mapping>
<!-- 伴随启动Servlet配置 end -->
Servlet实现类
public class BootInitServletA extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
public void init() throws ServletException {
super.init();
System.out.println("Servlet boot starting ..."+ (new Date()).toString());
}
}
SpringMVC的Bean实例化监听器实现类
Bean实例化监听器类
@Service
public class WebBootListener implements InitializingBean, ServletContextAware {
public void setServletContext(ServletContext servletContext) {
System.out.println("boot setServletContext ..."+new Date().toString());
}
public void afterPropertiesSet() throws Exception {
System.out.println("boot afterPropertiesSet ... " + new Date().toString());
}
}
在Spring MVC的配置文件里加入Bean
<bean id="werwer" class="cn.Service.WebBootListener"></bean>
Bean构造器监听 : @PostConstruct
实现类
@Service
public class BootApplication {
private static boolean isBoot = false;
static int count = 1;
@PostConstruct
public void constructProc(){
if(!isBoot) {
System.out.println("boot application by @PostConstruct run count : " + (count++) + new Date().toString());
isBoot=true;
}
System.out.println(" @PostConstruct run count :" + (count++) + new Date().toString());
}
}
测试用Controller
@Controller
public class IndexController {
@RequestMapping("/")
@ResponseBody
public String proc1(){
return "hello mvc";
}
@ResponseBody
@RequestMapping("/hello")
public String proc2(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
ServiceInteface serviceInteface =(ExampleService) applicationContext.getBean("aaa");
System.out.println(serviceInteface.testProc());
serviceInteface.testProc();
return "good eveing";
}
}