生成 Java 可执行 Jar 包实践

不要使用 maven-assembly-plugin

可能会报如下问题

Unable to locate Spring NamespaceHandler for XML schema namespace

这是由于所使用的 assembly 插件有 bug: 在将多个依赖打进同一个包时, 如果存在多个 spring 包的依赖, 则 META-INF/spring.handlersMETA-INF/spring.schemas 的内容会被覆盖. 详情参见这里

解决办法: 换另一个插件 maven-shade-plugin, 将打包插件替换为以下代码:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>1.6</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <finalName>${artifactId}-${env}-${version}</finalName>
                <transformers>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.example.yourMainClass</mainClass>
                    </transformer>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.handlers</resource>
                    </transformer>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.schemas</resource>
                    </transformer>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.tooling</resource>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

不要使用 java -jar

执行 jar 包时, 建议使用 java -cp, 而不要使用 java -jar 方式, 两者的区别参见这里, 命令如下

java -cp xxx.jar com.example.yourMainClass args

建议指定 JVM 的内存大小

随着逻辑越来越复杂, 执行 Job 需要更多内存, 这时可能报内存溢出错误 java.lang.OutOfMemoryError: Java heap space, 这时可以在 java 命令之后增加如下参数, 增大执行时的内存.

-Xms2048M -Xmx2048M

建议用 Spring 管理任务

随着项目越来越复杂, 可能会有很多任务, 建议用一个 Job, 多个 Task 的方式组织代码. 一个 Job 是代码入口, 内容如下

public class Job {
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("输入参数不够!");
            System.exit(0);
        }
        String cmd = args[0];

        ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:config/spring/local/appcontext-*.xml");
        AbstractTask task = (AbstractTask) context.getBean(cmd);
        task.setArgs(args);
        task.start();

        System.exit(0);
    }
}

然后写一个 Task 接口, 代码如下

public interface Task {
    String getTaskName();

    void run();

    void start();
}

接着写个抽象类 AbstractTask, 代码如下

@Slf4j
@Data
public abstract class AbstractTask implements Task {
    private String[] args;

    @Override
    public String getTaskName() {
        return getClass().getSimpleName();
    }

    @Override
    public void start() {
        log.info("Begin to run {} at {}", getTaskName(), new Date());
        run();
        log.info("Complete {} at {}", getTaskName(), new Date());
    }
}

接下来实现一个任务时, 只需要继承抽象类 AbstractTask, 然后实现 run() 方法即可. 在实现过程中, 可以通过 getArgs() 方法, 获取外部传入的参数. 执行任务统一调用 start() 方法.

这样子组织代码, 再多的任务的不用担心啦!