Spring Boot打包JAR或WAR后,Mybatis的Mapper無(wú)法掃描

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

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

MyBatis掃描通過(guò)VFS來(lái)實(shí)現(xiàn)

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

解決辦法,實(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容