【进行中】使用Spring Boot创建命令行应用程序的基础结构

简要描述

这篇文章介绍了如何使用Spring Boot创建一个命令行应用程序的框架。(尚未完成)
该应用程序的用途是用于定期执行批处理作业,可以通过cron或jenkins来触发。
(虽然有适用于批处理的Spring Batch框架,但在这个例子中并没有使用它。)

环境

    • Windows10

 

    • Java 1.8.0_92

 

    • Spring-Boot 1.4.0

Spring JDBC
MyBatis
MySQL 5.6

请参考

    • Spring Bootでコマンドラインアプリを作る時の注意点

 

    mybatis-spring-boot-starterの使い方

源代码

目前而言,源代码尚未完成,但其部分已存在于 sbcl-example。

创建雏形

我是用SPRING INITIALIZR创建了原型。暂时包括了以下三个依赖关系。

    • JDBC

 

    • MySQL

 

    DevTools
si.png

修改过的状态

设定

pom.xml -> 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>com.example</groupId>
    <artifactId>sbcl-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>sbcl-example</name>
    <description>CommandLine Application project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
             </plugin>
        </plugins>
    </build>

</project>

应用程序属性文件

只需要进行datasource的配置,假设要连接到MySQL数据库。

spring.profiles.active              = local

# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url               = jdbc:mysql://localhost/test
spring.datasource.username          = test_user
spring.datasource.password          = test_user
spring.datasource.driver-class-name     = com.mysql.jdbc.Driver

spring.datasource.schema            = import.sql

# MyBatis
mybatis.config-location             = mybatis-config.xml
# mybatis.mapper-locations          = 
# mybatis.type-aliases-package          = 
# mybatis.type-handlers-package         = 
# mybatis.executor-type             = SIMPLE
# mybatis.configuration             = 

# JOOQ (JooqAutoConfiguration)
# spring.jooq.sql-dialect=

请你参考一下。

    Appendix A. Common application properties

logback-spring.xml 改写成中文: 《logback-spring.xml》

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <property name="LOG_DIR" value="D:/logs" />

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{yyyy-MMM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{35} - %msg %n</pattern>
    </encoder>
  </appender>
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
     <file>${LOG_DIR}/sbcl-example.log</file>
     <encoder>
       <charset>UTF-8</charset>
       <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] - %msg %n</pattern>
     </encoder>
  </appender>

  <logger name="com.example" level="DEBUG" />

  <logger name="org.springframework" level="INFO"/>
  <root>
    <appender-ref ref="STDOUT" />
    <appender-ref ref="FILE" />
  </root>
</configuration>

实施的功能

命令行应用程序所需功能

    1. 参数处理

 

    1. 数据库连接

 

    1. 多重启动控制

 

    1. 处理结果的外部输出

 

    通知处理

处理参数

    • Commons CLI

 

    args4j

如果使用Commons CLI

pom.xml 的中文翻译可以是:“项目对象模型.xml”
<!-- https://mvnrepository.com/artifact/commons-cli/commons-cli -->
<dependency>
    <groupId>commons-cli</groupId>
    <artifactId>commons-cli</artifactId>
    <version>1.3.1</version>
</dependency>

如果使用args4j parent

pom.xml的汉语翻译:项目对象模型文件
<!-- https://mvnrepository.com/artifact/args4j/args4j -->
<dependency>
    <groupId>args4j</groupId>
    <artifactId>args4j</artifactId>
    <version>2.33</version>
</dependency>

数据库连接

    • Spring JDBC (spring-boot-starter-jdbc)

 

    • MyBatis (mybatis-spring-boot-starter)

 

    • JOOQ (spring-boot-starter-jooq)

 

    JPA (spring-boot-starter-data-jpa)

如果使用Spring JDBC

    1. 使用JDBC进行数据访问

 

    http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jdbc.html
pom.xml的中文释义是项目对象模型的XML配置文件。

加入spring-boot-starter-jdbc库来建立依赖关系。

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

如果要使用MyBatis的话

我的Batis-Spring-Boot-启动器

pom.xml 可以被简述为一个项目的配置文件。

添加mybatis-spring-boot-starter依赖。

<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>1.1.1</version>
</dependency>
我的batis-config.xml文件

src/java/resources/mybatis-config.xml 的中文同意译文:src/java/resources/mybatis-config.xml。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.example.domain"/>
    </typeAliases>
    <mappers>
        <mapper resource="mapper/CityMapper.xml"/>
        <mapper resource="mapper/HotelMapper.xml"/>
    </mappers>
</configuration>
导入.sql文件

src/java/resources/import.sql 可在项目的 Java 资源文件夹中找到 import.sql 文件。

drop table if exists city;
drop table if exists hotel;

create table city (
   `id` int not null
 , `name` varchar(30)
 , `state` varchar(6)
 , `country` varchar(6)
 , primary key(id)
) engine = INNODB;

create table hotel (
    `id` int not null
  , `city` int not null
  , `name` varchar(60)
  , `address` varchar(60)
  , `zip` varchar(12)
  , primary key(id)
) engine = INNODB;

insert into city (id, name, state, country) values (1, 'San Francisco', 'CA', 'US');
insert into city (id, name, state, country) values (2, 'New York City', 'NY', 'US');
insert into city (id, name, state, country) values (3, 'Los Angeles', 'CA', 'US');
insert into city (id, name, state, country) values (4, 'Chicago', 'IL', 'US');
insert into city (id, name, state, country) values (5, 'Houston', 'TX', 'US');

insert into hotel(id, city, name, address, zip) values (1, 1, 'Conrad Treasury Place', 'William & George Streets', '4001');
insert into hotel(id, city, name, address, zip) values (2, 1, 'The Westin St. Francis San Francisco on Union Square', 'Powell Street, Union Square, San Francisco, CA', '335');
insert into hotel(id, city, name, address, zip) values (3, 1, 'The Fairmont San Francisco', 'Mason Street, San Francisco, CA', '950');
地图绘制程序/城市地图绘制程序.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.CityMapper">
    <select id="selectCityById" resultType="City">
        SELECT * FROM city WHERE id = #{id}
    </select>
</mapper>
@Mapper
public interface CityMapper {

    City selectCityById(final Long id);

    @Select("SELECT * FROM city WHERE state = #{state}")
    List<City> findByState(@Param("state") String state);

}
映射器/HotelMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.HotelMapper">

    <select id="selectHotelById" resultType="Hotel">
        SELECT * FROM hotel WHERE id = #{id}
    </select>

    <resultMap id="selectByCityIdMap" type="Hotel">
      <id property="id" column="ホテルID" />
      <result property="city" column="都市ID" />
      <result property="name" column="ホテル名" />
      <result property="address" column="住所" />
      <result property="zip" column="郵便番号" />
    </resultMap>

    <select id="selectByCityId" resultMap="selectByCityIdMap">
        SELECT * FROM hotel WHERE city = #{id}
    </select>

</mapper>
@Mapper
public interface HotelMapper {

    Hotel selectHotelById(final Long id);

    List<Hotel> selectByCityId(final Long cityId);

}
我的蝙蝠信号灯。

Eclipse的插件

如果使用JOOQ

春季起步器-jooq

请帮我将pom.xml文件进行一下语义改写,只需要一个版本即可。

在项目中添加spring-boot-starter-jooq依赖。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jooq</artifactId>
</dependency>
请参考
    SpringBoot : Working with JOOQ

如果使用JPA的话

春季启动器-数据-JPA

pom.xml 的中文释义是什么?

添加spring-boot-starter-data-jpa以满足依赖关系。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

多重启动控制

如果使用FileChannel

处理结果的外部输出

如果要将其输出到Google Sheets的话

Google Sheets API
https://developers.google.com/sheets/
谷歌表格 API
https://developers.google.com/sheets/

pom.xml的中文原生释义是什么?

所需的库文件

    • google-api-client

 

    • google-oauth-client-jetty

 

    google-api-services-sheets
<!-- https://mvnrepository.com/artifact/com.google.api-client/google-api-client -->
<dependency>
    <groupId>com.google.api-client</groupId>
    <artifactId>google-api-client</artifactId>
    <version>1.22.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.oauth-client/google-oauth-client-jetty -->
<dependency>
    <groupId>com.google.oauth-client</groupId>
    <artifactId>google-oauth-client-jetty</artifactId>
    <version>1.22.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.apis/google-api-services-sheets -->
<dependency>
    <groupId>com.google.apis</groupId>
    <artifactId>google-api-services-sheets</artifactId>
    <version>v4-rev21-1.22.0</version>
</dependency>

处理通知

想要定制的是通知处理的“开始、进行、结束、异常发生”等时间和结果,以及常驻型情况下的状态通知等处理。

    • spring-boot-starter-mail

 

    Simple Slack API

如果您选择通过电子邮件通知的话(spring-boot-starter-mail),

pom.xml –> 项目对象模型文件
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
应用程序配置文件

谷歌邮箱

# Email (MailProperties)
spring.mail.default-encoding = UTF-8
spring.mail.host = smtp.gmail.com
#spring.mail.jndi-name= # Session JNDI name. When set, takes precedence to others mail settings.
spring.mail.password = ********** # Google App password
spring.mail.port = 578
#spring.mail.properties.*= # Additional JavaMail session properties.
spring.mail.protocol = smtp
spring.mail.test-connection = false
spring.mail.username = *****.*****.*****@gmail.com # Google account mail address

如果使用Slack进行通知(简单的Slack API)

pom.xml 文件
<!-- https://mvnrepository.com/artifact/com.ullink.slack/simpleslackapi -->
<dependency>
    <groupId>com.ullink.slack</groupId>
    <artifactId>simpleslackapi</artifactId>
    <version>0.6.0</version>
</dependency>

实施代码

Application.java应用程序.java

package com.example;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import com.example.service.ServiceAlpha;
import com.example.service.ServiceBeta;
import com.example.service.ServiceGamma;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@SpringBootApplication
public class Application {

    @Autowired
    ServiceAlpha alpha;

    @Autowired
    ServiceBeta beta;

    @Autowired
    ServiceGamma gamma;

    public static void main(String[] args) {
        log.debug(">>> call main");
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args)) {
            Application app = ctx.getBean(Application.class);
            app.run(args);
        }
        log.debug("<<< main end");
    }

    @PostConstruct
    public void init() {
        log.debug("application init");
    }

    @PreDestroy
    public void destory() {
        log.debug("application finish");
    }

    public void run(final String... args) {
        log.debug("application run");
        alpha.execute();
        beta.execute(args);
        gamma.execute();
    }

}

服务阿尔法

package com.example.service;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class ServiceAlpha {

    @Autowired
    private JdbcTemplate jdbc;

    public void execute() {
        log.debug("*** alpha in ***");

        if (jdbc == null) {
            throw new RuntimeException("JdbcTemplate is null");
        }
        Map<String, Object> result = jdbc.queryForMap("select now() as current");
        if (result == null) {
            log.debug("result null");
            return;
        }
        result.forEach((k,v)->{
            log.debug("key:{} value:{}", k, v);
        });

        log.debug("*** alpha out ***");
    }
}

服务测试版

package com.example.service;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class ServiceBeta {

    public void execute(final String... args) {
        log.debug("*** beta in ***");

        if (args!= null && args.length > 0) {
            log.debug("UpperCase:{}", StringUtils.join(args, ",").toUpperCase());
        }

        log.debug("*** beta out ***");
    }
}

服务伽马

package com.example.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.example.domain.City;
import com.example.domain.Hotel;
import com.example.mapper.CityMapper;
import com.example.mapper.HotelMapper;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class ServiceGamma {

    @Autowired
    private CityMapper cityMapper;

    @Autowired
    private HotelMapper hotelMapper;

    public void execute() {
        log.debug("*** gamma in ***");

        City city = cityMapper.selectCityById(1L);
        log.debug("city:{}", city);

        List<City> cityList = cityMapper.findByState("CA");
        if (cityList != null) {
            cityList.forEach(c -> {
                log.debug("city:{}",c);
            });
        } else {
            log.debug("city list is null");
        }

        Hotel hotel = hotelMapper.selectHotelById(1L);
        log.debug("hotel:{}", hotel);

        List<Hotel> hotelList = hotelMapper.selectByCityId(1L);
        if (hotelList != null) {
            hotelList.forEach(h -> {
                log.debug("hotel:{}",h);
            });
        } else {
            log.debug("hotel list is null");
        }

        log.debug("*** gamma out ***");
    }

}

构建和执行

在项目的根目录下执行以下的mvn命令。

> mvn clean package

...省略...

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ sbcl-example ---
[INFO] Building jar: D:\dev\eclipse-jee-neon-workspace\sbcl-example\target\sbcl-example-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:1.4.0.RELEASE:repackage (default) @ sbcl-example ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.619 s
[INFO] Finished at: 2016-08-30T21:44:51+09:00
[INFO] Final Memory: 27M/269M
[INFO] ------------------------------------------------------------------------

执行在目标目录下创建的jar文件。

> java -jar .\target\sbcl-example-0.0.1-SNAPSHOT.jar abc xyz

...省略...

DEBUG [main] com.example.Application - application init
INFO  [main] o.s.j.e.a.AnnotationMBeanExporter - Registering beans for JMX exposure on startup
INFO  [main] com.example.Application - Started Application in 3.771 seconds (JVM running for 4.699)
DEBUG [main] com.example.Application - application run
DEBUG [main] com.example.service.ServiceAlpha - *** alpha in ***
DEBUG [main] com.example.service.ServiceAlpha - key:current value:2016-08-30 21:46:11.0
DEBUG [main] com.example.service.ServiceAlpha - *** alpha out ***
DEBUG [main] com.example.service.ServiceBeta - *** beta in ***
DEBUG [main] com.example.service.ServiceBeta - UpperCase:ABC,XYZ
DEBUG [main] com.example.service.ServiceBeta - *** beta out ***
DEBUG [main] com.example.Application - application finish
DEBUG [main] com.example.Application - <<< main end
广告
将在 10 秒后关闭
bannerAds