Spring Boot打包JAR或WAR后鹊奖,Mybatis的Mapper無法掃描

https://github.com/mybatis/mybatis-3/issues/325

https://github.com/StripesFramework/stripes/issues/35

MyBatis掃描通過VFS來實(shí)現(xiàn)

在Spring?Boot中,由于是嵌套Jar膝昆,導(dǎo)致Mybatis默認(rèn)的VFS實(shí)現(xiàn)DefaultVFS無法掃描嵌套Jar中的類娃弓。

解決辦法,實(shí)現(xiàn)自定義的VFS廓潜,參考DefaultVFS增加對(duì)Spring?Boot嵌套JAR的處理。

package?com.qiyun.mybatis;

import?java.io.BufferedReader;

import?java.io.File;

import?java.io.FileNotFoundException;

import?java.io.IOException;

import?java.io.InputStream;

import?java.io.InputStreamReader;

import?java.io.UnsupportedEncodingException;

import?java.net.MalformedURLException;

import?java.net.URL;

import?java.net.URLEncoder;

import?java.util.ArrayList;

import?java.util.Arrays;

import?java.util.List;

import?java.util.jar.JarEntry;

import?java.util.jar.JarInputStream;

import?org.apache.ibatis.io.VFS;

import?org.apache.ibatis.logging.Log;

import?org.apache.ibatis.logging.LogFactory;

/**

*?Modified?DefaultVFS?for?handling?nested?jar.

*

*?https://github.com/mybatis/mybatis-3/issues/325

*?https://github.com/StripesFramework/stripes/issues/35

*

*?@version?1.0

*?@since?1.0

*/

public?class?SpringBootExecutableJarVFS?extends?VFS?{

private?static?final?Log?log?=?LogFactory.getLog(SpringBootExecutableJarVFS.class);

/**?The?magic?header?that?indicates?a?JAR?(ZIP)?file.?*/

private?static?final?byte[]?JAR_MAGIC?=?{?'P',?'K',?3,?4?};

@Override

public?boolean?isValid()?{

return?true;

}

@Override

public?List?list(URL?url,?String?path)?throws?IOException?{

InputStream?is?=?null;

try?{

List?resources?=?new?ArrayList();

//?First,?try?to?find?the?URL?of?a?JAR?file?containing?the?requested?resource.?If?a?JAR

//?file?is?found,?then?we'll?list?child?resources?by?reading?the?JAR.

URL?jarUrl?=?findJarForResource(url);

if?(jarUrl?!=?null)?{

is?=?jarUrl.openStream();

if?(log.isDebugEnabled())?{

log.debug("Listing?"?+?url);

}

resources?=?listResources(new?JarInputStream(is),?path);

}?else?{

List?children?=?new?ArrayList();

try?{

if?(isJar(url))?{

//?Some?versions?of?JBoss?VFS?might?give?a?JAR?stream?even?if?the?resource

//?referenced?by?the?URL?isn't?actually?a?JAR

is?=?url.openStream();

JarInputStream?jarInput?=?new?JarInputStream(is);

if?(log.isDebugEnabled())?{

log.debug("Listing?"?+?url);

}

for?(JarEntry?entry;?(entry?=?jarInput.getNextJarEntry())?!=?null;)?{

if?(log.isDebugEnabled())?{

log.debug("Jar?entry:?"?+?entry.getName());

}

children.add(entry.getName());

}

jarInput.close();

}?else?{

/*

*?Some?servlet?containers?allow?reading?from?directory?resources?like?a

*?text?file,?listing?the?child?resources?one?per?line.?However,?there?is?no

*?way?to?differentiate?between?directory?and?file?resources?just?by?reading

*?them.?To?work?around?that,?as?each?line?is?read,?try?to?look?it?up?via

*?the?class?loader?as?a?child?of?the?current?resource.?If?any?line?fails

*?then?we?assume?the?current?resource?is?not?a?directory.

*/

is?=?url.openStream();

BufferedReader?reader?=?new?BufferedReader(new?InputStreamReader(is));

List?lines?=?new?ArrayList();

for?(String?line;?(line?=?reader.readLine())?!=?null;)?{

if?(log.isDebugEnabled())?{

log.debug("Reader?entry:?"?+?line);

}

lines.add(line);

if?(getResources(path?+?"/"?+?line).isEmpty())?{

lines.clear();

break;

}

}

if?(!lines.isEmpty())?{

if?(log.isDebugEnabled())?{

log.debug("Listing?"?+?url);

}

children.addAll(lines);

}

}

}?catch?(FileNotFoundException?e)?{

/*

*?For?file?URLs?the?openStream()?call?might?fail,?depending?on?the?servlet

*?container,?because?directories?can't?be?opened?for?reading.?If?that?happens,

*?then?list?the?directory?directly?instead.

*/

if?("file".equals(url.getProtocol()))?{

File?file?=?new?File(url.getFile());

if?(log.isDebugEnabled())?{

log.debug("Listing?directory?"?+?file.getAbsolutePath());

}

if?(file.isDirectory())?{

if?(log.isDebugEnabled())?{

log.debug("Listing?"?+?url);

}

children?=?Arrays.asList(file.list());

}

}?else?{

//?No?idea?where?the?exception?came?from?so?rethrow?it

throw?e;

}

}

//?The?URL?prefix?to?use?when?recursively?listing?child?resources

String?prefix?=?url.toExternalForm();

if?(!prefix.endsWith("/"))?{

prefix?=?prefix?+?"/";

}

//?Iterate?over?immediate?children,?adding?files?and?recursing?into?directories

for?(String?child?:?children)?{

String?resourcePath?=?path?+?"/"?+?child;

resources.add(resourcePath);

URL?childUrl?=?new?URL(prefix?+?child);

resources.addAll(list(childUrl,?resourcePath));

}

}

return?resources;

}?finally?{

if?(is?!=?null)?{

try?{

is.close();

}?catch?(Exception?e)?{

//?Ignore

}

}

}

}

/**

*?List?the?names?of?the?entries?in?the?given?{@link?JarInputStream}?that?begin?with?the

*?specified?{@code?path}.?Entries?will?match?with?or?without?a?leading?slash.

*

*?@param?jar?The?JAR?input?stream

*?@param?path?The?leading?path?to?match

*?@return?The?names?of?all?the?matching?entries

*?@throws?IOException?If?I/O?errors?occur

*/

protected?List?listResources(JarInputStream?jar,?String?path)?throws?IOException?{

//?Include?the?leading?and?trailing?slash?when?matching?names

if?(!path.startsWith("/"))?{

path?=?"/"?+?path;

}

if?(!path.endsWith("/"))?{

path?=?path?+?"/";

}

//?Iterate?over?the?entries?and?collect?those?that?begin?with?the?requested?path

List?resources?=?new?ArrayList();

for?(JarEntry?entry;?(entry?=?jar.getNextJarEntry())?!=?null;)?{

if?(!entry.isDirectory())?{

//?Add?leading?slash?if?it's?missing

String?name?=?entry.getName();

if?(!name.startsWith("/"))?{

name?=?"/"?+?name;

}

//?Check?file?name

if?(name.startsWith(path))?{

if?(log.isDebugEnabled())?{

log.debug("Found?resource:?"?+?name);

}

//?Trim?leading?slash

resources.add(name.substring(1));

}

}

}

return?resources;

}

/**

*?Attempts?to?deconstruct?the?given?URL?to?find?a?JAR?file?containing?the?resource?referenced

*?by?the?URL.?That?is,?assuming?the?URL?references?a?JAR?entry,?this?method?will?return?a?URL

*?that?references?the?JAR?file?containing?the?entry.?If?the?JAR?cannot?be?located,?then?this

*?method?returns?null.

*

*?@param?url?The?URL?of?the?JAR?entry.

*?@return?The?URL?of?the?JAR?file,?if?one?is?found.?Null?if?not.

*?@throws?MalformedURLException

*/

protected?URL?findJarForResource(URL?url)?throws?MalformedURLException?{

if?(log.isDebugEnabled())?{

log.debug("Find?JAR?URL:?"?+?url);

}

if?(isNestedJar(url))?{

//?Retain?jar:?protocol?as?a?workaround?for?#325

if?(log.isDebugEnabled())?{

log.debug("It?is?a?nested?JAR:?"?+?url);

}

}?else?{

//?If?the?file?part?of?the?URL?is?itself?a?URL,?then?that?URL?probably?points?to?the?JAR

try?{

for?(;;)?{

url?=?new?URL(url.getFile());

if?(log.isDebugEnabled())?{

log.debug("Inner?URL:?"?+?url);

}

}

}?catch?(MalformedURLException?e)?{

//?This?will?happen?at?some?point?and?serves?as?a?break?in?the?loop

}

}

//?Look?for?the?.jar?extension?and?chop?off?everything?after?that

StringBuilder?jarUrl?=?new?StringBuilder(url.toExternalForm());

int?index?=?jarUrl.lastIndexOf(".jar");

if?(index?>=?0)?{

jarUrl.setLength(index?+?4);

if?(log.isDebugEnabled())?{

log.debug("Extracted?JAR?URL:?"?+?jarUrl);

}

}?else?{

if?(log.isDebugEnabled())?{

log.debug("Not?a?JAR:?"?+?jarUrl);

}

return?null;

}

//?Try?to?open?and?test?it

try?{

URL?testUrl?=?new?URL(jarUrl.toString());

if?(isJar(testUrl))?{

return?testUrl;

}?else?{

//?WebLogic?fix:?check?if?the?URL's?file?exists?in?the?filesystem.

if?(log.isDebugEnabled())?{

log.debug("Not?a?JAR:?"?+?jarUrl);

}

jarUrl.replace(0,?jarUrl.length(),?testUrl.getFile());

File?file?=?new?File(jarUrl.toString());

//?File?name?might?be?URL-encoded

if?(!file.exists())?{

try?{

file?=?new?File(URLEncoder.encode(jarUrl.toString(),?"UTF-8"));

}?catch?(UnsupportedEncodingException?e)?{

throw?new?RuntimeException("Unsupported?encoding???UTF-8???That's?unpossible.");

}

}

if?(file.exists())?{

if?(log.isDebugEnabled())?{

log.debug("Trying?real?file:?"?+?file.getAbsolutePath());

}

testUrl?=?file.toURI().toURL();

if?(isJar(testUrl))?{

return?testUrl;

}

}

}

}?catch?(MalformedURLException?e)?{

log.warn("Invalid?JAR?URL:?"?+?jarUrl);

}

if?(log.isDebugEnabled())?{

log.debug("Not?a?JAR:?"?+?jarUrl);

}

return?null;

}

protected?boolean?isNestedJar(URL?url)?{

if?(!"jar".equals(url.getProtocol()))

return?false;

String?urlStr?=?url.toExternalForm();

int?indexOfWar?=?urlStr.indexOf(".war!/");

int?indexOfJar?=?urlStr.indexOf(".jar!/");

if?(indexOfWar?!=?-1)?{?//?Executable?War

return?indexOfWar?!=?urlStr.lastIndexOf(".jar!/");

}?else?{?//?Executable?Jar

return?indexOfJar?!=?urlStr.lastIndexOf(".jar!/");

}

}

/**

*?Converts?a?Java?package?name?to?a?path?that?can?be?looked?up?with?a?call?to

*?{@link?ClassLoader#getResources(String)}.

*

*?@param?packageName?The?Java?package?name?to?convert?to?a?path

*/

protected?String?getPackagePath(String?packageName)?{

return?packageName?==?null???null?:?packageName.replace('.',?'/');

}

/**

*?Returns?true?if?the?resource?located?at?the?given?URL?is?a?JAR?file.

*

*?@param?url?The?URL?of?the?resource?to?test.

*/

protected?boolean?isJar(URL?url)?{

return?isJar(url,?new?byte[JAR_MAGIC.length]);

}

/**

*?Returns?true?if?the?resource?located?at?the?given?URL?is?a?JAR?file.

*

*?@param?url?The?URL?of?the?resource?to?test.

*?@param?buffer?A?buffer?into?which?the?first?few?bytes?of?the?resource?are?read.?The?buffer

*????????????must?be?at?least?the?size?of?{@link?#JAR_MAGIC}.?(The?same?buffer?may?be?reused

*????????????for?multiple?calls?as?an?optimization.)

*/

protected?boolean?isJar(URL?url,?byte[]?buffer)?{

InputStream?is?=?null;

try?{

is?=?url.openStream();

is.read(buffer,?0,?JAR_MAGIC.length);

if?(Arrays.equals(buffer,?JAR_MAGIC))?{

if?(log.isDebugEnabled())?{

log.debug("Found?JAR:?"?+?url);

}

return?true;

}

}?catch?(Exception?e)?{

//?Failure?to?read?the?stream?means?this?is?not?a?JAR

}?finally?{

if?(is?!=?null)?{

try?{

is.close();

}?catch?(Exception?e)?{

//?Ignore

}

}

}

return?false;

}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末善榛,一起剝皮案震驚了整個(gè)濱河市茉帅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锭弊,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擂错,死亡現(xiàn)場(chǎng)離奇詭異味滞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)钮呀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門剑鞍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人爽醋,你說我怎么就攤上這事蚁署。” “怎么了蚂四?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵光戈,是天一觀的道長哪痰。 經(jīng)常有香客問我,道長久妆,這世上最難降的妖魔是什么晌杰? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮筷弦,結(jié)果婚禮上肋演,老公的妹妹穿的比我還像新娘。我一直安慰自己烂琴,他們只是感情好爹殊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奸绷,像睡著了一般梗夸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上健盒,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天绒瘦,我揣著相機(jī)與錄音,去河邊找鬼扣癣。 笑死惰帽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的父虑。 我是一名探鬼主播该酗,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼士嚎!你這毒婦竟也來了呜魄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤莱衩,失蹤者是張志新(化名)和其女友劉穎爵嗅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笨蚁,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡睹晒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了括细。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伪很。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖奋单,靈堂內(nèi)的尸體忽然破棺而出锉试,到底是詐尸還是另有隱情,我是刑警寧澤览濒,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布呆盖,位于F島的核電站拖云,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏絮短。R本人自食惡果不足惜江兢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丁频。 院中可真熱鬧杉允,春花似錦、人聲如沸席里。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奖磁。三九已至改基,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咖为,已是汗流浹背秕狰。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留躁染,地道東北人鸣哀。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像吞彤,于是被迫代替她去往敵國和親我衬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容