spring1.0(一)Spring boot中容器启动前后回调的方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/insis_mo/article/details/87888647

一、ApplicationContextInitializer接口是spring容器在执行refreshed之前的一个回调,回调ApplicationContextInitializer接口实现类中的initialize方法。容器加载时会先刷新容器,refreshed方法为容器的刷新方法,当刚加载容器的时候就会执行该方法。而在加载容器之前就会回调initialize方法。 
使用步骤:

  • 写一个实现类,实现CommandLineRunner接口
  • 把该类纳入到spring容器中进行管理

如下程序演示:

1、自定义一个类MyApplicationContextInitializer,实现ApplicationContextInitializer接口

package com.example.demo;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

/*ConfigurableApplicationContext为接口中传入的参数类型,在initialize中可以通过传入的参数执行所需要的操作*/
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("bean的个数:" + applicationContext.getBeanDefinitionCount());
        /*下面还可以用applicationContext获取bean*/
    }

}

启动类为:

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(App.class);

        /*把MyApplicationContextInitializer注入到容器中*/
        app.addInitializers(new MyApplicationContextInitializer());
        ConfigurableApplicationContext context = app.run(args);
        context.close();
    }
}

运行启动类,输出结果如下:

 .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.9.RELEASE)

bean的个数:6
2017-12-15 20:13:16.875  INFO 9528 --- [           main] com.example.demo.App                     : Starting App on DESKTOP-98FEQG9 with PID 9528 (E:\code\springboot\springboot-5\target\classes started by *** in E:\code\springboot\springboot-5)
2017-12-15 20:13:16.879  INFO 9528 --- [           main] com.example.demo.App                     : No active profile set, falling back to default profiles: default
……

从输出结果可以看出,MyApplicationContextInitializer 中的initialize方法在容器之前进行调用的。

2、创建MyApplicationContextInitializer 类时,就用@Component注解把该类注入到IOC容器中,在容器加载时就不需要显示加载了。

@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("bean的个数:" + applicationContext.getBeanDefinitionCount());
        /*下面还可以用applicationContext获取bean*/
    }

}
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(App.class);
        ConfigurableApplicationContext context = app.run(args);
        context.close();
    }
}

运行结果同上面结果相同。

3、可以同时指定多个ApplicationContextInitializer实现类,用以在容器加载前进行回调实现类中的initialize方法。在上面示例的基础上继续实现下面的类

@Component
public class MyApplicationContextInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("name:" + applicationContext.getDisplayName());
        /*下面还可以用applicationContext获取bean*/
    }

}

启动类和上面示例相同,运行启动类,输出结果如下:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.9.RELEASE)

bean的个数:6
name:org.springframework.context.annotation.AnnotationConfigApplicationContext@5fdba6f9
……

从输出结果可以看出,程序在加载容器之前先回调了两个实现类的初始化方法。

二、当spring boot加载完容器后,但还没有执行程序自定义的方法时,也可以回调指定的方法。如果有实现CommandLineRunner或者ApplicationRunner接口,并把实现类纳入到容器中进行管理,当容器加载完后,会回调实现类中的run方法。步骤同上面示例的步骤相同。如下程序示例所示:

1、自定义类MyCommandLineRunner,实现CommandLineRunner接口,并实现接口中的方法。

package com.example.demo;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyCommandLineRunner implements CommandLineRunner {

    @Override
    /*args为获取到的容器中启动时的参数*/
    public void run(String... args) throws Exception {
        System.out.println("----回调类MyCommandLineRunner中的run方法----");
        System.out.println(Arrays.asList(args));
    }

}

启动类为:

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(App.class);
        /*可以指定args参数,在这个地方为"aa"和"bb"*/
        ConfigurableApplicationContext context = app.run("aa", "bb");
        context.close();
    }
}

运行启动类,输出结果如下:

---MyApplicationRunner---
[aa, bb]

2、在1的实例下再自定义MyApplicationRunner类,实现ApplicationRunner接口,并实现接口中的run方法。

@Component
public class MyApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("---MyApplicationRunner---");
        System.out.println(Arrays.asList(args.getSourceArgs()));
    }

}

运行启动类,输出结果:

---MyApplicationRunner---
[aa, bb]
----回调类MyCommandLineRunner中的run方法----
[aa, bb]

3、ApplicationRunner接口与CommandLineRunner接口实现的功能相似,都是在容器加载完,执行后续程序之前发挥功能。类似于windows的开机自启功能。但ApplicationRunner功能比CommandLineRunner的功能更全面,因为CommandLineRunner中的run方法传入的参数类型为ApplicationArguments类型的,

public interface ApplicationRunner {

    /**
     * Callback used to run the bean.
     * @param args incoming application arguments
     * @throws Exception on error
     */
    void run(ApplicationArguments args) throws Exception;

}

而CommandLineRunner接口中的run方法传入的参数为string类型的,

public interface CommandLineRunner {

    /**
     * Callback used to run the bean.
     * @param args incoming main method arguments
     * @throws Exception on error
     */
    void run(String... args) throws Exception;

}

由于CommandLineRunner 中的run方法的参数类型为string类型的,只能获取到容器启动时传入的参数。而ApplicationRunner 接口中的run方法传入的参数类型为ApplicationArguments类型的,该类型是对容器启动参数的封装。如下所示:在run as -> run configurations -> arguments -> program arguments中设置启动参数为:--name=lzj

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(App.class);
        ConfigurableApplicationContext context = app.run(args);
        ApplicationArguments argsments = context.getBean(ApplicationArguments.class);
    /*getSourceArgs获取所有启动参数,返回一个字符串数组*/ System.out.println(argsments.getSourceArgs().length);
    System.out.println(Arrays.asList(argsments.getSourceArgs()));
    /*getOptionNames返回所有启动参数的键,然后把键放在set中进行返回*/ System.out.println(argsments.getOptionNames());
    /*getOptionValues根据启动参数中的键获取对应的值,返回字符串形式*/  System.out.println(argsments.getOptionValues("name"));
        context.close();
    }
}

运行启动类,输出结果:

1
[--name=lzj]
[name]
[lzj]

当然也可以在程序中指定启动参数,如:

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(App.class);
        ConfigurableApplicationContext context = app.run("aa", "bb");
        ApplicationArguments argsments = context.getBean(ApplicationArguments.class);
        System.out.println(argsments.getSourceArgs().length);
        System.out.println(Arrays.asList(argsments.getSourceArgs()));
        context.close();
    }
}

运行启动类,输出:

2
[aa, bb]

这种情况下就不方便通过键值的形式获取参数了。此时即使在run as -> run configurations -> arguments -> program arguments中设置启动参数也管用了,因为会被程序中设置的启动参数“aa”和”bb”覆盖掉。

猜你喜欢

转载自blog.csdn.net/insis_mo/article/details/87888647
今日推荐