gradle项目中资源文件的相对路径打包技巧必看

网友投稿 296 2022-11-12


gradle项目中资源文件的相对路径打包技巧必看

开发java application时,不管是用ant/maven/gradle中的哪种方式来构建,通常最后都会打包成一个可执行的jar包程序,而程序运行所需的一些资源文件(配置文件),比如jdbc.properties, log4j2.xml,spring-xxx.xml这些,可以一起打包到jar中,程序运行时用类似classpath*:xxx.xml的去加载,大多数情况下,这样就能工作得很好了。

但是,如果有一天,需要修正配置,比如:一个应用上线初期,为了调试方便,可能会把log的日志级别设置低一些,比如:INFO级别,运行一段时间稳定以后,只需要记录WARN或ERROR级别的日志,这时候就需要修改log4j2.xml之类的配置文件,如果把配置文件打包在jar文件内部,改起来就比较麻烦,要把重新打包部署,要么在线上,先用jar命令将jar包解压,改好后,再打包回去,比较繁琐。

面对这种需求,更好的方式是把配置文件放在jar文件的外部相对目录下,程序启动时去加载相对目录下的配置文件,这样改起来,就方便多了,下面演示如何实现:(以gradle项目为例)

主要涉及以下几点:

1、如何不将配置文件打包到jar文件内

既然配置文件放在外部目录了,jar文件内部就没必要再重复包含这些文件了,可以修改build.gradle文件,参考下面这样:

processResources {

exclude { "**/*.*" }

}

相当于覆盖了默认的processResouces task,这样gradle打包时,资源目录下的任何文件都将排除。

2、log4j2的配置加载处理

log4j2加载配置文件时,默认情况下会找classpath下的log4j2.xml文件,除非手动给它指定配置文件的位置,分析它的源码,

可以找到下面这段:

org.apache.logging.log4j.core.config.ConfigurationFactory.Factory#getConfiguration(java.lang.String, java.net.URI)

public Configuration getConfiguration(final String name, final URI configLocation) {

if (configLocation == null) {

final String configLocationStr = this.substitutor.replace(PropertiesUtil.getProperties()

.getStringProperty(CONFIGURATION_FILE_PROPERTY));

if (configLocationStr != null) {

ConfigurationSource source = null;

try {

source = getInputFromUri(NetUtils.toURI(configLocationStr));

} catch (final Exception ex) {

// Ignore the error and try as a String.

LOGGER.catching(Level.DEBUG, ex);

}

if (source == null) {

final ClassLoader loader = LoaderUtil.getThreadContextClassLoader();

source = getInputFromString(configLocationStr, loader);

}

if (source != null) {

for (final ConfigurationFactory factory : getFactories()) {

final String[] types = factory.getSupportedTypes();

if (types != null) {

for (final String type : types) {

if (type.equals("*") || configLocationStr.endsWith(type)) {

final Configuration config = factory.getConfiguration(source);

if (config != null) {

return config;

}

}

}

}

}

}

} else {

for (final ConfigurationFactory factory : getFactories()) {

final String[] types = factory.getSupportedTypes();

if (types != null) {

for (final String type : types) {

if (type.equals("*")) {

final Configuration config = factory.getConfiguration(name, configLocation);

if (config != null) {

return config;

}

}

}

}

}

}

} else {

// configLocation != null

final String configLocationStr = configLocation.toString();

for (final ConfigurationFactory factory : getFactories()) {

final String[] types = factory.getSupportedTypes();

if (types != null) {

for (final String type : types) {

if (type.equals("*") || configLocationStr.endsWith(type)) {

final Configuration config = factory.getConfiguration(name, configLocation);

if (config != null) {

return config;

}

}

}

}

}

}

Configuration config = getConfiguration(true, name);

if http://(config == null) {

config = getConfiguration(true, null);

if (config == null) {

config = getConfiguration(false, name);

if (config == null) {

config = getConfiguration(false, null);

}

}

}

if (config != null) {

return config;

}

LOGGER.error("No log4j2 configuration file found. Using default configuration: logging only errors to the console.");

return new DefaultConfiguration();

}

其中常量CONFIGURATION_FILE_PROPERTY的定义为:

public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile";

从这段代码可以看出,只要在第一次调用log4j2的getLogger之前设置系统属性,将其指到配置文件所在的位置即可。

3、其它一些配置文件(比如spring配置)的相对路径加载

这个比较容易,spring本身就支持从文件目录加载配置的能力。

综合以上分析,可以封装一个工具类:

import org.springframework.context.ConfigurableApplicationContext;

import org.springframework.context.support.FileSystemXmlApplicationContext;

import java.io.File;

public class ApplicationContextUtil {

private static ConfigurableApplicationContext context = null;

private static ApplicationContextUtil instance = null;

public static ApplicationContextUtil getInstance() {

if (instance == null) {

synchronized (ApplicationContextUtil.class) {

if (instance == null) {

instance = new ApplicationContextUtil();

}

}

}

return instance;

}

public ConfigurableApplicationContext getContext() {

return context;

}

private ApplicationContextUtil() {

}

static {

//加载log4j2.xml

String configLocation = "resources/log4j2.xml";

File configFile = new File(configLocation);

if (!configFile.exists()) {

System.err.println("log4j2 config file:" + configFile.getAbsolutePath() + " not exist");

System.exit(0);

}

System.out.println("log4j2 config file:" + configFile.getAbsolutePath());

try {

//注:这一句必须放在整个应用第一次LoggerFactory.getLogger(XXX.class)前执行

System.setProperty("log4j.configurationFile", configFile.getAbsolutePath());

} catch (Exception e) {

System.err.println("log4j2 initialize error:" + e.getLocalizedMessage());

System.exit(0);

}

//加载spring配置文件

configLocation = "resources/spring-context.xml";

configFile = new File(configLocation);

if (!configFile.exists()) {

System.err.println("spring config file:" + configFile.getAbsolutePath() + " not exist");

System.exit(0);

}

System.out.println("spring config file:" + configFile.getAbsolutePath());

if (context == null) {

context = new FileSystemXmlApplicationContext(configLocation);

System.out.println("spring load success!");

}

}

}

注:这里约定了配置文件放在相对目录resources下,而且log4j2的配置文件名为log4j2.xml,spring的入口配置文件为spring-context.xml(如果不想按这个约定来,可参考这段代码自行修改)

有了这个工具类,mainclass入口程序上可以这么用:

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.context.ApplicationContext;

/**

* Created by yangjunming on 12/15/15.

* author: yangjunming@huijiame.com

*/

public class App {

private static ApplicationContext context;

private static Logger logger;

public static void main(String[] args) {

context = ApplicationContextUtil.getInstance().getContext();

logger = LoggerFactory.getLogger(App.class);

System.out.println("start ...");

logger.debug("debug message");

logger.info("info message");

logger.warn("warn message");

logger.error("error message");

System.out.println(context.getBean(SampleObject.class));

}

}

再次友情提醒:logger的实例化,一定要放在ApplicationContextUtil.getInstance().getContext();之后,否则logger在第一次初始化时,仍然尝试会到classpath下去找log4j2.xml文件,实例化之后,后面再设置系统属性就没用了。

4、gradle 打包的处理

代码写完了,还有最后一个工作没做,既然配置文件不打包到jar里了,那就得复制到jar包的相对目录resources下,可以修改build.gradle脚本,让计算机处理处理,在代替手动复制配置文件。

task pack(type: Copy, dependsOn: [clean, installDist]) {

sourceSets.main.resources.srcDirs.each {

from it

into "$buildDir/install/$rootProject.name/bin/resources"

}

}

增加这个task后,直接用gradle pack 就可以实现打包,并自动复制配置文件到相对目录resources目录下了,参考下图:

最后国际惯例,给个示例源码:https://github.com/yjmyzz/config-load-demo

gradle pack 后,可进入build/install/config-load-demo/bin 目录,运行./config-load-demo (windows下运行config-load-demo.bat) 查看效果,然后尝试修改resources/log4j2.xml里的日志级别,再次运行,观察变化 。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java 基于UDP协议实现消息发送
下一篇:Java 实现常见的非对称加密算法
相关文章

 发表评论

暂时没有评论,来抢沙发吧~