Docker: 运行Java应用

更方便的运行方式

在以前只能在Linux或者Windows系统中一一搭建所有应用程序的开发环境,包括MySQL,Redis,RabbitMQ等.但自从Docker出现之后,改变了这一切.开发环境和运行环境全都可以直接放到Docker容器中运行,最主要的是,搭建方便.基本只需一条命令就可以安装好.

关于Spring Boot的运行

完整源码: Gitee仓库

其实Spring Boot一般有两种运行方式:

  1. 放在Tomcat中,以war包的方式运行.
  2. 在Java命令行中,以jar包的方式运行.

Docker中,推荐以第二种方式更好,更方便一些.

image-2881
docker

推送方式

把Spring Boot打包之后的jar包,推送到Docker有两种方式:

  1. dockerfile Maven插件
  2. jib Maven插件

这里只介绍第一种用的比较多的dockerfile方式(但是,源码里面提供了两种方式的示例: Gitee仓库),源码如下:

pom.xml 文件:

<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>xyz.suancaiyu</groupId>
	<artifactId>custom-spring-boot-docker-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>custom-spring-boot-docker-demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<docker.image.prefix>192.168.37.130</docker.image.prefix>
		<java.version>12</java.version>
	</properties>


	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<!-- tag::plugin[] -->
			<plugin>
				<groupId>com.spotify</groupId>
				<artifactId>dockerfile-maven-plugin</artifactId>
				<version>1.4.9</version>
				<configuration>
					<repository>${docker.image.prefix}/${project.artifactId}</repository>
				</configuration>
			</plugin>
			<!-- end::plugin[] -->

			<!-- tag::unpack[] -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<executions>
					<execution>
						<id>unpack</id>
						<phase>package</phase>
						<goals>
							<goal>unpack</goal>
						</goals>
						<configuration>
							<artifactItems>
								<artifactItem>
									<groupId>${project.groupId}</groupId>
									<artifactId>${project.artifactId}</artifactId>
									<version>${project.version}</version>
								</artifactItem>
							</artifactItems>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>12</source>
					<target>12</target>
				</configuration>
			</plugin>
			<!-- end::unpack[] -->
		</plugins>
	</build>



	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>



</project>

需要注意: docker.image.prefix 这个值是Docker服务端的地址,需要修改为自己的.

java标志
image-2882

Dockerfile 文件:

定义程序的依赖,运行方式:

FROM openjdk:12-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT [“java”,”-cp”,”app:app/lib/*”,”xyz.suancaiyu.customspringbootdockerdemo.CustomSpringBootDockerDemoApplication”]

编译并运行应用程序:

1. (Windows)在本地机器上配置环境变量:

DOCKER_HOST=192.168.37.130

2. (Windows)在当前目录执行下面的语句编译(推荐从菜单栏单独打开Power Shell,或者cmd.):

mvn install dockerfile:build

3. (Linux)在192.168.37.130的机器上运行(启动应用):

docker run -p 8080:8080 -t 192.168.37.130/custom-spring-boot-docker-demo

4. (Windows)在浏览器中访问: http://192.168.37.130:8080/

更多可以参考: Spring官方示例

完整源码: Gitee仓库

Java: Spring Boot自定义控制台日志输出的颜色

简述

偶然发现,在Spring Boot中,可以自定义输出日志的颜色(之前其实是有的,但只有日志级别那个地方有颜色).

image-2874

主要改动的就是,日志消息的颜色跟日志级别的颜色统一了.

但还是有几点要说明:

  1. 仅支持Logback日志系统
  2. 控制台输出格式的默认配置在: org.springframework.boot.logging.logback.DefaultLogbackConfiguration.java的CONSOLE_LOG_PATTERN属性;
  3. 在application.properties中配置logging.pattern.console即可覆盖默认配置
  4. 更多配置可以参考: Spring Boot官方文档

源码

java标志
image-2875

完整源码: Gitee仓库

源码很简单,启动类如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@SpringBootApplication
@RestController
public class SpringBootLoggingColorCustomDemoApplication {


    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @RequestMapping("/")
    public Map<String,String> hello(){
        log.trace("test trace");
        log.debug("test debug");
        log.warn("test warn");
        log.info("test info");
        log.error("test error");
        return Map.of("Hello","Java");
    }


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

}

application.properties配置如下:

spring.output.ansi.enabled=ALWAYS # 启用ANSI输出
logging.level.root=TRACE # 配置日志级别,输出会比较多.默认级别是INFO

# 配置输出格式.
logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(—){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){red} %clr(%m){%5p} %n

完整源码: Gitee仓库

Java: 操作Redis

推荐使用Docker来搭建Redis环境.

连接的两种方式

会使用以下方式来操作Redis:

  1. Lettuce:Lettuce是基于Netty和Reactor的可伸缩线程安全的Redis客户端。Lettuce提供了同步,异步和反应的API来与Redis进行交互。
  2. Spring Data Redis:可以与Lettuce及其它Redis客户端整合.(暂未提供,待补充)

注意: 在Lettuce中,目前只提供了同步/异步的方式与Redis交互,暂无提供反应式方式与Redis交互的计划.

版本

  1. Redis: 4.*
  2. Lettuce:5.*
  3. Java:1.8 +(未在Java 1.9 中测试,不保证不会出现问题 – 2018/01/17)
  4. Maven:3.5

完整源码地址在: Gitee仓库

java标志
image-2863

单独使用Lettuce

请注意,该示例为一个Maven项目.

异步请求

异步使用的部分代码如下:

	/**
     * Redis事务使用异步执行
     * <p>
     * 方法会删除在本方法内添加的部分数据,具体看方法内部.
     */
    public static void asyncTransactionsSetAndGetData() throws ExecutionException, InterruptedException {
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);

        // 获取异步执行命令(这一步很关键)
        RedisAsyncCommands<String, String> asyncCommands = redisClient.connect().async();

        // 开启事务
        RedisFuture<String> stringRedisFuture = asyncCommands.multi();
        System.out.println("开启事务的执行反馈:" + stringRedisFuture.get());

        RedisFuture<String> rf = asyncCommands.set("MUL-" + AppConfig.DEFAULT_KEY, AppConfig.DEFAULT_VALUE);
        int count = 20;
        for (int i = 0; i < count; i++) {
            asyncCommands.incr("MUL-COUNT");
            asyncCommands.set(i + "-MUL-" + AppConfig.DEFAULT_KEY, i + "," + AppConfig.DEFAULT_VALUE);
        }

        // 删除刚才添加的数据
        for (int i = 0; i < count; i++) {
            asyncCommands.del(i + "-MUL-" + AppConfig.DEFAULT_KEY);
        }

        // 执行事务
        RedisFuture<TransactionResult> result = asyncCommands.exec();

        System.out.println("事务执行结果:" + result.get());
        System.out.println("命令执行结果:" + rf.get());

        redisClient.shutdownAsync();
    }


    /**
     * 异步执行批量设置及获取数据
     * <p>
     * <p>
     * 未删除本方法内添加的数据.
     */
    public static void asyncBatchSetAndGetData() {

        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);

        // 获取异步执行命令(这一步很关键)
        RedisAsyncCommands<String, String> asyncCommands = redisClient.connect().async();
        int count = 10;
        List<RedisFuture<String>> futures = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            futures.add(asyncCommands.set(AppConfig.DEFAULT_KEY + "-" + i, AppConfig.DEFAULT_VALUE + i));
        }

        System.out.println(LettuceFutures.awaitAll(1, TimeUnit.MINUTES, futures.toArray(new RedisFuture[futures.size()])));

        List<RedisFuture<String>> lists = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            lists.add(asyncCommands.get(AppConfig.DEFAULT_KEY + "-" + i));
        }
        lists.stream().filter(x -> !"".equals(x.getError())).forEach(y -> {
            try {
                System.out.println(y.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        redisClient.shutdownAsync();

    }

    /**
     * 异步设置与获取数据的基本用法
     * <p>
     * <p>
     * 方法内部会删除本方法添加的数据.
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void asyncSetAndGetData() throws ExecutionException, InterruptedException {
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);

        // 获取异步执行命令(这一步很关键)
        RedisAsyncCommands<String, String> asyncCommands = redisClient.connect().async();

        // 设置数据
        Future<String> setResult = asyncCommands.set(AppConfig.DEFAULT_KEY, AppConfig.DEFAULT_VALUE);
        System.out.println(setResult.get());


        // 读取数据
        Future<String> getResult = asyncCommands.get(AppConfig.DEFAULT_KEY);
        System.out.println(getResult.get());

        // 删除数据,可以传递多个key.
        asyncCommands.del(AppConfig.DEFAULT_KEY);

        /*
         * 关闭此客户端并异步关闭所有打开的连接。
         * 调用此方法后客户端的操作被丢弃。
         * 关机时间为2秒,超时时间为15秒。
         */
        redisClient.shutdownAsync();
    }


    /**
     * 异步设置与获取数据的高级用法.
     * <p>
     * 方法内部会删除本方法添加的数据.
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void asyncAdvancedSetAndGetData() throws ExecutionException, InterruptedException {
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);
        // 获取异步执行命令(这一步很关键)
        RedisAsyncCommands<String, String> asyncCommands = redisClient.connect().async();

        // 编解码器
        StringCodec codec = StringCodec.UTF8;
        // 状态消息输出
        StatusOutput sot = new StatusOutput<>(codec);

        // 添加命令
        RedisFuture<String> response = asyncCommands.dispatch
                (CommandType.SET, sot,
                        new CommandArgs<>(codec).addKey(AppConfig.DEFAULT_KEY)
                                .addValue(AppConfig.DEFAULT_VALUE));


        // 可以检查是否已经执行完成(执行完成不表示执行成功与否)
        if (response.isDone()) {
            // 会返回执行结果
            System.out.println(response.get());
        }

        // 获取刚才设置的结果
        RedisFuture<String> getInfo = asyncCommands.dispatch(CommandType.GET, sot,
                new CommandArgs<>(codec)
                        .addKey(AppConfig.DEFAULT_KEY));

        // 下面会显示上面设置的值:
        System.out.println(getInfo.get());

        // 删除键值
        System.out.println("删除结果(已删除的键数量):"+asyncCommands.dispatch(CommandType.DEL, new IntegerOutput<>(codec),
                new CommandArgs<>(codec)
                        .addKey(AppConfig.DEFAULT_KEY)).get());

        /*
         * 关闭这个客户端并关闭所有打开的连接。 调用关机后应该丢弃客户端。 关机没有安静的时间和2秒的超时。
         */
        redisClient.shutdown();
    }

image-2864

同步请求

同步请求处理的部分代码:

	/**
     * Redis事务使用同步执行
     * <p>
     * 方法会删除在本方法内添加的部分数据,具体看方法内部.
     */
    public static void syncTransactionsSetAndGetData() {
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);

        // 获取同步执行命令(这一步很关键)
        RedisCommands<String, String> redisCommands = redisClient.connect().sync();

        // 开启事务
        String stringRedisFuture = redisCommands.multi();
        System.out.println("开启事务的执行反馈:" + stringRedisFuture);

        String rf = redisCommands.set("MUL-" + AppConfig.DEFAULT_KEY, AppConfig.DEFAULT_VALUE);
        int count = 20;
        for (int i = 0; i < count; i++) {
            redisCommands.incr("MUL-COUNT");
            redisCommands.set(i + "-MUL-" + AppConfig.DEFAULT_KEY, i + "," + AppConfig.DEFAULT_VALUE);
        }


        // 删除刚才添加的数据
        for (int i = 0; i < count; i++) {
            redisCommands.del(i + "-MUL-" + AppConfig.DEFAULT_KEY);
        }


        // 执行事务
        TransactionResult result = redisCommands.exec();

        System.out.println("事务执行结果:" + result.get(0));
        System.out.println("命令执行结果:" + rf);

        redisClient.shutdown();
    }


    /**
     * 同步设置与获取数据的基本用法
     * <p>
     * <p>
     * 方法内部会删除本方法添加的数据.
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void syncSetAndGetData(){
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);

        // 获取同步执行命令(这一步很关键)
        RedisCommands<String, String> redisCommands = redisClient.connect().sync();

        // 设置数据
        String setResult = redisCommands.set(AppConfig.DEFAULT_KEY, AppConfig.DEFAULT_VALUE);
        System.out.println(setResult);


        // 读取数据
        String getResult = redisCommands.get(AppConfig.DEFAULT_KEY);
        System.out.println(getResult);

        // 删除数据,可以传递多个key.
        redisCommands.del(AppConfig.DEFAULT_KEY);

        /*
         * 关闭此客户端并同步关闭所有打开的连接。
         * 调用此方法后客户端的操作被丢弃。
         * 关机时间为2秒,超时时间为15秒。
         */
        redisClient.shutdown();
    }


    /**
     * 同步设置与获取数据的高级用法.
     * <p>
     * 方法内部会删除本方法添加的数据.
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void syncAdvancedSetAndGetData() {
        // 设置Redis连接信息
        RedisURI redisURI = RedisURI.Builder.redis(AppConfig.REDIS_HOST, AppConfig.REDIS_PORT)
                .withPassword(AppConfig.REDIS_PASSWORD).withDatabase(AppConfig.REDIS_DATABASE).build();
        // 用连接信息,创建Redis客户端
        RedisClient redisClient = RedisClient.create(redisURI);
        // 获取同步执行命令(这一步很关键)
        RedisCommands<String, String> redisCommands = redisClient.connect().sync();

        // 编解码器
        StringCodec codec = StringCodec.UTF8;
        // 状态消息输出
        StatusOutput sot = new StatusOutput<>(codec);

        // 添加命令

        Object response = redisCommands.dispatch
                (CommandType.SET, sot,
                        new CommandArgs<>(codec).addKey(AppConfig.DEFAULT_KEY)
                                .addValue(AppConfig.DEFAULT_VALUE));


        System.out.println(response);

        ///

        // 获取刚才设置的结果
        Object getInfo = redisCommands.dispatch(CommandType.GET, sot,
                new CommandArgs<>(codec)
                        .addKey(AppConfig.DEFAULT_KEY));

        // 下面会显示上面设置的值:
        System.out.println(getInfo);

        // 删除键值
        System.out.println("删除结果(已删除的键数量): " + redisCommands.dispatch(CommandType.DEL, new IntegerOutput(codec),
                new CommandArgs<>(codec)
                        .addKey(AppConfig.DEFAULT_KEY)).toString());

        /*
         * 关闭这个客户端并关闭所有打开的连接。 调用关机后应该丢弃客户端。 关机没有安静的时间和2秒的超时。
         */
        redisClient.shutdown();
    }

Spring Data Redis和Lettuce搭配使用

请注意,该示例为一个Spring Boot项目.

部分示例代码如下:

   private static final String DEFAULT_KEY = "SUANCAIYU.XYZ-SPRINGBOOT";


    @Autowired
    private StringRedisTemplate stringRedisTemplate;



    @RequestMapping("writeSync")
    private String writeSync(){
        int length=5000;
        for (int i = 0; i <length ; i++) {
            stringRedisTemplate.opsForValue().set(DEFAULT_KEY+i,i+DEFAULT_KEY+i);
        }
        System.out.println("abc");
        return "sync";
    }



    @RequestMapping("asyncRead")
    private String read() {
        return stringRedisTemplate.execute((RedisCallback<String>) redisConnection -> {
            System.out.println(redisConnection.setCommands().sMembers(DEFAULT_KEY.getBytes()).stream().map(x->new String(x)).collect(Collectors.joining(",")));
            return "success";
        });
    }

    @RequestMapping("asyncWrite")
    private String write() {
        stringRedisTemplate.execute((RedisCallback<String>) redisConnection -> {
            Long add = redisConnection.setCommands().sAdd(DEFAULT_KEY.getBytes(), "test123456".getBytes());
            if (add > 0) {
                return "success";
            }
            return "faild";
        });
        return "write";
    }

完整源码地址在: Gitee仓库

Spring Boot: 起步依赖的实现原理

Spring Boot 版本: 2.1.4.RELEASE

简要说明

Spring Boot依赖项管理定义:

Spring Boot中在spring-boot-dependencies*.jar的pom.xml中定义了所依赖的依赖项的名称和版本信息.(如果要覆盖Spring Boot默认引用的版本信息,可以在自己的pom.xml文件中定义与spring-boot-dependencies中的pom.xml中版本名称一致的配置,配置自己的版本号即可.)

pom.xml所在网址:

https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-dependencies/pom.xml

替换Spring Boot配置的默认Thymeleaf版本

<properties>
		<!-- 省略上面的版本定义... -->
		<thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
		<thymeleaf-extras-springsecurity.version>3.0.4.RELEASE</thymeleaf-extras-springsecurity.version>
		<thymeleaf-layout-dialect.version>2.4.1</thymeleaf-layout-dialect.version>
		<thymeleaf-extras-data-attribute.version>2.0.1</thymeleaf-extras-data-attribute.version>
		<thymeleaf-extras-java8time.version>3.0.4.RELEASE</thymeleaf-extras-java8time.version>
		<!-- 省略下面的版本定义...end -->
	</properties>

替换thymeleaf的版本,在自己的pom.xml文件中定义:

<properties>
		<!-- 省略上面的版本定义...下面是示例版本号,实际可能并不存在5.X版本的信息 -->
		<thymeleaf.version>5.0.11.RELEASE</thymeleaf.version>
		<!-- 省略下面的版本定义...end -->
	</properties>

java标志
image-2809

起步依赖的特点

  1. 直接面向功能;
  2. 一站获取所有相关依赖,不再复制粘贴
  3. 官方的Starters

    命名: spring-boot-starter-*

    起步依赖实现原理

    其实质就是通过Maven依赖管理来实现起步依赖.

    例如:要使用jdbc,只需要引入:spring-boot-starter-jdbc,而spring-boot-starter-jdbc的pom.xml文件是如下定义的:

    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starters</artifactId>
    		<version>${revision}</version>
    	</parent>
    	<artifactId>spring-boot-starter-jdbc</artifactId>
    	<name>Spring Boot JDBC Starter</name>
    	<description>Starter for using JDBC with the HikariCP connection pool</description>
    	<properties>
    		<main.basedir>${basedir}/../../..</main.basedir>
    	</properties>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>com.zaxxer</groupId>
    			<artifactId>HikariCP</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-jdbc</artifactId>
    		</dependency>
    	</dependencies>
    </project>

    也就是Spring Boot在Maven的pom.xml文件里面引入了我们自己之前需要引入的Maven依赖.

    继承关系

    spring-boot-starter-jdbc继承了spring-boot-starters;
    而spring-boot-starters继承了spring-boot-parent;
    而spring-boot-parent继承了spring-boot-dependencies;
    spring-boot-dependencies继承了spring-boot-build.

    继承关系如下图(最上面是最底层的):

    Spring Boot起步依赖
    image-2810

Java:Spring Boot 中SQL自动初始化的步骤

版本:

  1. Spring FrameWork 5.1.4
  2. Spring Boot 2.1.2

java标志
image-2740

SQL初始化

1.1 data.sql的加载

通过org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker(实现ApplicationEvent接口)注册事件,在applicationContext注册时会回调注册的事件(DataSourceInitializerInvoker的onApplicationEvent),来触发初始化data.sql.

通过org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer该类(createSchema方法用于执行schema.sql文件,initSchema()方法用于执行data.sql文件),查找SQL文件,并执行.

1.2 schema.sql的加载

在(spring-beans.*.jar)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory的:
invokeInitMethods方法中此时已经初始化完成了Bean,在此方法中回调org.springframework.beans.factory.InitializingBean接口(实际调用:org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker类)的afterPropertiesSet方法,

在DataSourceInitializerInvoker类的afterPropertiesSet方法中调用了org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer类的createSchema()方法,以执行schema.sql文件.