springboot项目做初始化

299 篇文章 5 订阅

前言
在我们用springboot搭建项目的时候,有时候会碰到在项目启动时初始化一些操作的需求,针对这种需求springboot(spring)为我们提供了以下几种方案供我们选择: 
- ApplicationRunner与CommandLineRunner接口 
- Spring Bean初始化的InitializingBean,init-method和PostConstruct 
- Spring的事件机制

ApplicationRunner与CommandLineRunner
如果需要在SpringApplication启动时执行一些特殊的代码,你可以实现ApplicationRunner或CommandLineRunner接口,这两个接口工作方式相同,都只提供单一的run方法,而且该方法仅在SpringApplication.run(…)完成之前调用,更准确的说是在构造SpringApplication实例完成之后调用run()的时候,具体分析见后文,所以这里将他们分为一类。

ApplicationRunner
构造一个类实现ApplicationRunner接口

@Component
public class ApplicationRunnerTest implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner");
    }
}
1
2
3
4
5
6
7
8
项目启动测试结果如图: 


CommandLineRunner
对于这两个接口而言,我们可以通过Order注解或者使用Ordered接口来指定调用顺序,@Order()中的值越小,优先级越高

@Component
@Order(1)
public class CommandLineRunnerTest implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner...");
    }
}
1
2
3
4
5
6
7
8
9
10
当然我们也可以同时使用ApplicationRunner和CommandLineRunner,默认情况下前者比后者先执行,但是这没有必要,使用一个就好了

两者的联系与区别
前面就提到过,两个接口都有run()方法,只不过它们的参数不一样,CommandLineRunner的参数是最原始的参数,没有进行任何处理,ApplicationRunner的参数是ApplicationArguments,是对原始参数的进一步封装

源码跟踪
接下来我们简要跟踪一下源码看ApplicationRunner(CommandLineRunner)是如何被调用的。

Springboot在启动的时候,都会构造一个SpringApplication实例,至于这个实例怎么构造的,这里不去探究了,有感兴趣的可以去看下源码。这里主要看ApplicationRunner是如何被调用的,而它的调用就是在SpringApplication这个实例调用run方法中。

@SpringBootApplication
public class Application {


    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
1
2
3
4
5
6
7
8
进入run方法

    public static ConfigurableApplicationContext run(Class<?> primarySource,
            String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }
1
2
3
4
5
执行SpringApplication的run方法

    public static ConfigurableApplicationContext run(Class<?>[] primarySources,
            String[] args) {
        return new SpringApplication(primarySources).run(args);
    }
1
2
3
4
5
一路点击run()来到这里 

发现对ApplicationRunner的调用实际上在callRunners方法中 

对于CommandLineRunner或者ApplicationRunner来说,需要注意的两点: 
- 所有CommandLineRunner/ApplicationRunner的执行时点是在SpringBoot应用的ApplicationContext完全初始化开始工作之后,callRunners()可以看出是run方法内部最后一个调用的方法(可以认为是main方法执行完成之前最后一步) 
- 只要存在于当前SpringBoot应用的ApplicationContext中的任何CommandLineRunner/ApplicationRunner,都会被加载执行(不管你是手动注册还是自动扫描去Ioc容器)

Spring Bean初始化的InitializingBean,init-method和PostConstruct
InitializingBean接口
InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet()方法。

在spring初始化bean的时候,如果bean实现了InitializingBean接口,在对象的所有属性被初始化后之后才会调用afterPropertiesSet()方法

@Component
public class InitialingzingBeanTest implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean..");
    }
}
1
2
3
4
5
6
7
8


当然,我们可以看出spring初始化bean肯定会在 
ApplicationRunner和CommandLineRunner接口调用之前。

当然有一点我们要注意的是,尽管使用initialingBean接口可以实现初始化动作,但是官方并不建议我们使用InitializingBean接口,因为它将你的代码耦合在Spring代码中,官方的建议是在bean的配置文件指定init-method方法,或者在@Bean中设置init-method属性 


init-method和@PostConstruct
前面就说过官方文档上不建议使用InitializingBean接口,但是我们可以在<bean>元素的init-method属性指定bean初始化之后的操作方法,或者在指定方法上加上@PostConstruct注解来制定该方法在初始化之后调用

@SpringBootApplication
public class Application {


    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @PostConstruct
    public void init() {
        System.out.println("init...");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
更多关于Spring Bean的生命周期的内容,请参阅Spring相关书籍或博客Spring Bean的生命周期

Spring的事件机制
Spring的事件机制实际上是设计模式中观察者模式的典型应用,在Head First 设计模式中是这样定义观察者模式的:

观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监听一个主题对象。这样一来,当被观察者状态改变时,需要通知相应的观察者,使这些观察者能够自动更新

基础概念
Spring的事件驱动模型由三部分组成 
- 事件: ApplicationEvent,继承自JDK的EventObject,所有事件都要继承它,也就是被观察者 
- 事件发布者: ApplicationEventPublisher及ApplicationEventMulticaster接口,使用这个接口,就可以发布事件了 
- 事件监听者: ApplicationListener,继承JDK的EventListener,所有监听者都继承它,也就是我们所说的观察者,当然我们也可以使用注解 @EventListener,效果是一样的

事件
在Spring框架中,默认对ApplicationEvent事件提供了如下支持: 
- ContextStartedEvent:ApplicationContext启动后触发的事件 
- ContextStoppedEvent:ApplicationContext停止后触发的事件 
- ContextRefreshedEvent:ApplicationContext初始化或刷新完成后触发的事件;(容器初始化完成后调用,所以我们可以利用这个事件做一些初始化操作) 
- ContextClosedEvent:ApplicationContext关闭后触发的事件;(如web容器关闭时自动会触发spring容器的关闭,如果是普通java应用,需要调用ctx.registerShutdownHook();注册虚拟机关闭时的钩子才行) 


构造一个类继承ApplicationEvent

public class TestEvent extends ApplicationEvent {

    private static final long serialVersionUID = -376299954511699499L;
    private String message;
    /**
     * Create a new ApplicationEvent.
     *
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public TestEvent(Object source) {
        super(source);
    }

    public void getMessage() {
        System.out.println(message);
    }

    public void setMessage(String message) {
        this.message = message;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
创建事件监听者
有两种方法可以创建监听者,一种是直接实现ApplicationListener的接口,一种是使用注解 @EventListener,注解是添加在监听方法上的,下面的例子是直接实现的接口

@Component
public class ApplicationListenerTest implements ApplicationListener<TestEvent> {
    @Override
    public void onApplicationEvent(TestEvent testEvent)
    {
        testEvent.getMessage();
    }
}
1
2
3
4
5
6
7
8
事件发布
对于事件发布,代表者是ApplicationEventPublisher和ApplicationEventMulticaster,系统提供的实现如下 

ApplicationContext接口继承了ApplicationEventPublisher,并在AbstractApplicationContext实现了具体代码,实际执行是委托给ApplicationEventMulticaster(可以认为是多播)

下面是一个事件发布者的测试实例:

@RunWith(SpringRunner.class)
@SpringBootTest
public class EventTest {
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void publishTest() {
        TestEvent testEvent = new TestEvent("");
        testEvent.setMessage("hello world");
        applicationContext.publishEvent(testEvent);
    }
}
//output:
hello world
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
利用ContextRefreshedEvent事件进行初始化操作
前面做了这么多铺垫,下面进入今天的主题,利用Spring的事件机制进行初始化一些操作,实际上就是前面提到了,利用ContextRefreshedEvent事件进行初始化,该事件是ApplicationContext初始化完成后调用的事件,所以我们可以利用这个事件,对应实现一个监听器,在其onApplicationEvent()方法里初始化操作

@Component
public class ApplicationListenerTest implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("我被调用了..");
    }
}
1
2
3
4
5
6
7
8


注意: 在传统的基于XML配置的Spring项目中会存在二次调用的问题,即调用两次该方法,原因是在传统的Spring MVC项目中,系统存在两个容器,一个root容器,一个project-servlet.xml对应的子容器,在初始化这两个容器的时候都会调用该方法一次,所以有二次调用的问题,而对于基于Springboot的项目不存在这个问题

小结
以上简要总结了在springboot启动时进行初始化操作的几个方案,这几种方式都可以满足我们的需求,针对具体场景使用对应的方案。但是,CommandLineRunner或者ApplicationRunner不是Spring框架原有的东西,它俩属于SpringBoot应用特定的回调扩展接口,所以很容易进行扩展,在一些微服务应用中使用也较广泛。
--------------------- 
作者:pjmike 
来源:CSDN 
原文:https://blog.csdn.net/pjmike233/article/details/81908540 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot项目中,可以使用初始化代码来实现项目启动时的一些初始化操作。下面以示例代码进行解释: 1. 创建一个类,命名为ApplicationRunnerImpl实现ApplicationRunner接口,该接口继承了CommandLineRunner接口,用于在Spring Boot项目启动之后执行特定的代码。 ```java import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class ApplicationRunnerImpl implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { // 这里写入初始化代码,例如加载配置、数据库连接等操作 System.out.println("Spring Boot项目启动初始化代码执行!"); } } ``` 2. 在启动类中,使用@SpringBootApplication注解启动Spring Boot应用程序。 ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` 在上述示例代码中,当Spring Boot项目启动之后,ApplicationRunnerImpl类中的run方法会被执行,可以在此方法中编写一些初始化的代码。比如加载配置文件、初始化数据库连接等。上面的例子中,run方法内只打印了一条信息。实际应用中,可以根据需要编写具体的初始化逻辑。 当代码执行时,控制台会打印出"Spring Boot项目启动初始化代码执行!"的信息,表示初始化代码成功执行。 这种方式非常适用于需要在项目启动时执行一些初始化操作的场景,可以方便地集成到Spring Boot框架中,实现项目的自动初始化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值