I heard that wechat search “Java fish” will change strong oh!

This article is posted on Github and Gitee, and contains my entire Java series of articles for study and interview purposes

(1) Overview

Recently I met a function point, a very simple table in the database has more than 1000 data, the data here mainly achieve the role of value mapping, simply speaking, I can get the corresponding code value in the database through the Chinese name. The original implementation is to check the SQL after each use, although there will be no problem, but as long as the network IO, will consume time. So this plan needs to be optimized.

The way of optimization is actually very simple, the amount of data is not much, more than a thousand data in the memory does not take up much space. Therefore, it is perfectly possible to load data into memory once, and then only need to go into memory each time.

(2) Implementation scheme

When the Spring container is initialized, the data is taken from the database into memory, and then called directly.

SpringBoot has two interfaces to implement this function: CommandLineRunner and ApplicationRunner.

2.1 CommandLineRunner

CommandLineRunner can be used to execute the run method after the system is started

@Component
public class DataPrepare implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner Performs data initialization"); }}Copy the code

If you have more than one class, you can also specify the Order of execution for each class using the @order annotation.

Then you can write code implementation, first define a class to store Mysql data in memory, through static Map storage

public class DataMap {
    public static Map<String, String> map = new HashMap<>();
    public static void putData(String key, String value) {
        map.put(key, value);
    }
    public static String getDataByKey(String key) {
        returnmap.get(key); }}Copy the code

The DataPrepare class then statically stores the data into the Map.

@Component
public class DataPrepare implements CommandLineRunner {
    @Autowired
    private DataMapper dataMapper;
    @Override
    public void run(String... args) throws Exception {
        // Fetch data from the database
        List<DataDO> dataDOS = dataMapper.selectList(Wrappers.emptyWrapper());
        // Write to DataMapdataDOS.forEach(item -> DataMap.putData(item.getName(), item.getCode())); }}Copy the code

To use it, just call the datamap.getDatabyKey () method.

2.2 ApplicationRunner

ApplicationRunner and CommandLineRunner have similar functions and are implemented in much the same way. It also inherits the interface and implements the interface’s run method.

@Component
public class ApplicationDataPrepare implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner performs data initialization"); }}Copy the code

In cases where the @Order annotation is not specified, ApplicationRunner takes precedence over CommandLineRunner.

The difference between the two:

CommandLineRunner and ApplicationRunner function almost the same, but the main difference is that the input arguments in the run method are different. CommandLineRunner receives startup parameters through a String array, ApplicationRunner receives this via an ApplicationArguments object.

When used, either String arrays or ApplicationArguments can be retrieved from the JVM’s startup arguments.

(3) source code analysis

Why is it that by implementing an interface that overrides the run method you can automatically execute your code once you start the program? We can look at the source of SpringBoot:

Points into SpringApplication. The run () method, has entered the public ConfigurableApplicationContext run (String… After a series of initialization methods are performed, the this.Callrunners (Context, applicationArguments) method is executed

The callRunners approach is simple: you first define a runners collection and put the beans you want to execute into it. You can see that ApplicationRunner and CommandLineRunner are put into runners here, the Order annotations are then sorted, and the execution is finally traversed.

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    Iterator var4 = (new LinkedHashSet(runners)).iterator();

    while(var4.hasNext()) {
        Object runner = var4.next();
        if (runner instanceof ApplicationRunner) {
            this.callRunner((ApplicationRunner)runner, args);
        }

        if (runner instanceof CommandLineRunner) {
            this.callRunner((CommandLineRunner)runner, args); }}}Copy the code

(4) Summary

A small detail can save multiple Sql calls. ApplicationRunner and CommandLineRunner can also be used lazily to load data into a static Map for the first time to achieve cache-like effects.