gradle項(xiàng)目中資源文件的相對(duì)路徑打包技巧必看
開(kāi)發(fā)java application時(shí),不管是用ant/maven/gradle中的哪種方式來(lái)構(gòu)建,通常最后都會(huì)打包成一個(gè)可執(zhí)行的jar包程序,而程序運(yùn)行所需的一些資源文件(配置文件),比如jdbc.properties, log4j2.xml,spring-xxx.xml這些,可以一起打包到j(luò)ar中,程序運(yùn)行時(shí)用類(lèi)似classpath*:xxx.xml的去加載,大多數(shù)情況下,這樣就能工作得很好了。
但是,如果有一天,需要修正配置,比如:一個(gè)應(yīng)用上線初期,為了調(diào)試方便,可能會(huì)把log的日志級(jí)別設(shè)置低一些,比如:INFO級(jí)別,運(yùn)行一段時(shí)間穩(wěn)定以后,只需要記錄WARN或ERROR級(jí)別的日志,這時(shí)候就需要修改log4j2.xml之類(lèi)的配置文件,如果把配置文件打包在jar文件內(nèi)部,改起來(lái)就比較麻煩,要把重新打包部署,要么在線上,先用jar命令將jar包解壓,改好后,再打包回去,比較繁瑣。
面對(duì)這種需求,更好的方式是把配置文件放在jar文件的外部相對(duì)目錄下,程序啟動(dòng)時(shí)去加載相對(duì)目錄下的配置文件,這樣改起來(lái),就方便多了,下面演示如何實(shí)現(xiàn):(以gradle項(xiàng)目為例)
主要涉及以下幾點(diǎn):
1、如何不將配置文件打包到j(luò)ar文件內(nèi)
既然配置文件放在外部目錄了,jar文件內(nèi)部就沒(méi)必要再重復(fù)包含這些文件了,可以修改build.gradle文件,參考下面這樣:
processResources {
exclude { "**/*.*" }
}
相當(dāng)于覆蓋了默認(rèn)的processResouces task,這樣gradle打包時(shí),資源目錄下的任何文件都將排除。
2、log4j2的配置加載處理
log4j2加載配置文件時(shí),默認(rèn)情況下會(huì)找classpath下的log4j2.xml文件,除非手動(dòng)給它指定配置文件的位置,分析它的源碼,
可以找到下面這段:
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 (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";
從這段代碼可以看出,只要在第一次調(diào)用log4j2的getLogger之前設(shè)置系統(tǒng)屬性,將其指到配置文件所在的位置即可。
3、其它一些配置文件(比如spring配置)的相對(duì)路徑加載
這個(gè)比較容易,spring本身就支持從文件目錄加載配置的能力。
綜合以上分析,可以封裝一個(gè)工具類(lèi):
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 {
//注:這一句必須放在整個(gè)應(yīng)用第一次LoggerFactory.getLogger(XXX.class)前執(zhí)行
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!");
}
}
}
注:這里約定了配置文件放在相對(duì)目錄resources下,而且log4j2的配置文件名為log4j2.xml,spring的入口配置文件為spring-context.xml(如果不想按這個(gè)約定來(lái),可參考這段代碼自行修改)
有了這個(gè)工具類(lèi),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的實(shí)例化,一定要放在ApplicationContextUtil.getInstance().getContext();之后,否則logger在第一次初始化時(shí),仍然嘗試會(huì)到classpath下去找log4j2.xml文件,實(shí)例化之后,后面再設(shè)置系統(tǒng)屬性就沒(méi)用了。
4、gradle 打包的處理
代碼寫(xiě)完了,還有最后一個(gè)工作沒(méi)做,既然配置文件不打包到j(luò)ar里了,那就得復(fù)制到j(luò)ar包的相對(duì)目錄resources下,可以修改build.gradle腳本,讓計(jì)算機(jī)處理處理,在代替手動(dòng)復(fù)制配置文件。
task pack(type: Copy, dependsOn: [clean, installDist]) {
sourceSets.main.resources.srcDirs.each {
from it
into "$buildDir/install/$rootProject.name/bin/resources"
}
}
增加這個(gè)task后,直接用gradle pack 就可以實(shí)現(xiàn)打包,并自動(dòng)復(fù)制配置文件到相對(duì)目錄resources目錄下了,參考下圖:

最后國(guó)際慣例,給個(gè)示例源碼:https://github.com/yjmyzz/config-load-demo
gradle pack 后,可進(jìn)入build/install/config-load-demo/bin 目錄,運(yùn)行./config-load-demo (windows下運(yùn)行config-load-demo.bat) 查看效果,然后嘗試修改resources/log4j2.xml里的日志級(jí)別,再次運(yùn)行,觀察變化 。
以上這篇gradle項(xiàng)目中資源文件的相對(duì)路徑打包技巧必看就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
idea maven編譯報(bào)錯(cuò)Java heap space的解決方法
這篇文章主要為大家詳細(xì)介紹了idea maven編譯報(bào)錯(cuò)Java heap space的相關(guān)解決方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04
JAVA并發(fā)中VOLATILE關(guān)鍵字的神奇之處詳解
這篇文章主要給大家介紹了關(guān)于JAVA并發(fā)中VOLATILE關(guān)鍵字的神奇之處的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
Java實(shí)現(xiàn)簡(jiǎn)易GUI貪吃蛇小游戲
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)易GUI貪吃蛇小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
將InputStream轉(zhuǎn)化為base64的實(shí)例
這篇文章主要介紹了將InputStream轉(zhuǎn)化為base64的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
javaweb圖書(shū)商城設(shè)計(jì)之用戶模塊(1)
這篇文章主要介紹了javaweb圖書(shū)商城設(shè)計(jì)之用戶模塊的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
關(guān)于設(shè)置Mybatis打印調(diào)試sql的兩種方式
這篇文章主要介紹了關(guān)于設(shè)置Mybatis打印調(diào)試sql的兩種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
Java之如何讀取Excel獲取真實(shí)行數(shù)
這篇文章主要介紹了Java之如何讀取Excel獲取真實(shí)行數(shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2023-06-06
java實(shí)現(xiàn)KFC點(diǎn)餐系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)KFC點(diǎn)餐系統(tǒng),模擬肯德基快餐店的收銀系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01

