Spring boot中Freemarker依賴(lài)父級(jí)項(xiàng)目井厌,尋找資源

問(wèn)題

為了繼承公共的父項(xiàng)目html靜態(tài)資源,致讥,我們希望在子項(xiàng)目相同路徑下有文件時(shí)仅仆,覆蓋父項(xiàng)目資源文件,沒(méi)有的時(shí)候直接獲取父項(xiàng)目垢袱。
但是freemark在尋找視圖的時(shí)候墓拜,發(fā)現(xiàn)無(wú)法找到父類(lèi)靜態(tài)視圖資源。

解決

在配置文件中增加以下配置

# Whether to prefer file system access for template loading. 
#File system access enables hot detection of template changes.
spring.freemarker.prefer-file-system-access=false

根據(jù)官方解釋為:
是否優(yōu)先從從文件系統(tǒng)中獲取模板请契,以支持熱加載咳榜,默認(rèn)為true。

從官方文檔描述中爽锥,得出以下結(jié)論:
1涌韩、如果設(shè)置true,會(huì)優(yōu)先使用文件路徑獲取【咦氯夷,這不是廢話嗎臣樱?】。
2、如果設(shè)置為false雇毫,不支持熱加載數(shù)據(jù)玄捕。
但是經(jīng)過(guò)實(shí)踐發(fā)現(xiàn),以上結(jié)論都是錯(cuò)誤的E锓拧C墩场!

我們要繼承父項(xiàng)目席吴,讀取父模板內(nèi)容赌结,需要設(shè)置prefer-file-system-access=false,否則會(huì)報(bào)404無(wú)法找到視圖孝冒。
并且設(shè)置為false后柬姚,數(shù)據(jù)熱加載測(cè)試依然可以正常運(yùn)行。

那是什么原因?qū)е耡pi文檔和實(shí)際操作過(guò)程中截然不同的答案呢庄涡?我們研究下源碼

原因

從入口開(kāi)始追蹤

@ConfigurationProperties(prefix = "spring.freemarker")
public class FreeMarkerProperties extends AbstractTemplateViewResolverProperties {
    public boolean isPreferFileSystemAccess() {
        return this.preferFileSystemAccess;
    }

    public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {
        this.preferFileSystemAccess = preferFileSystemAccess;
    }
}

看下圖量承,我們可以看到freferFileSystemAccess字段,只被方法isPreferFileSystemAccess調(diào)用穴店。

preferFileSystemAccess調(diào)用鏈.png

跟蹤方法到了核心判定方法:

public class FreeMarkerConfigurationFactory {
    protected TemplateLoader getTemplateLoaderForPath(String templateLoaderPath) {
        if (isPreferFileSystemAccess()) {
            // Try to load via the file system, fall back to SpringTemplateLoader
            // (for hot detection of template changes, if possible).
            try {
                Resource path = getResourceLoader().getResource(templateLoaderPath);
                File file = path.getFile();  // will fail if not resolvable in the file system
                if (logger.isDebugEnabled()) {
                    logger.debug(
                            "Template loader path [" + path + "] resolved to file path [" + file.getAbsolutePath() + "]");
                }
                return new FileTemplateLoader(file);
            }
            catch (Exception ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Cannot resolve template loader path [" + templateLoaderPath +
                            "] to [java.io.File]: using SpringTemplateLoader as fallback", ex);
                }
                return new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);
            }
        }
        else {
            // Always load via SpringTemplateLoader (without hot detection of template changes).
            logger.debug("File system access not preferred: using SpringTemplateLoader");
            return new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);
        }
    }
}

我們看到撕捍,這里進(jìn)行邏輯區(qū)分,看起來(lái)true沒(méi)問(wèn)題泣洞。只能跟蹤下代碼
true:優(yōu)先從資源文件中獲取忧风,如果異常,走fasle邏輯
false:new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);

跟蹤代碼發(fā)現(xiàn):
這個(gè)代碼僅在初始化執(zhí)行球凰,判定文件目錄是否存在狮腿,并非文件是否存在。
所以會(huì)導(dǎo)致后續(xù)使用時(shí)呕诉,直接使用FileTemplateLoader,導(dǎo)致無(wú)法正常加載缘厢。我們?cè)賮?lái)驗(yàn)證下:

/*---------FreeMarkerConfigurationFactory begin---------*/

List<TemplateLoader> templateLoaders = new ArrayList<>(this.templateLoaders);
if (this.templateLoaderPaths != null) {
            for (String path : this.templateLoaderPaths) {
                templateLoaders.add(getTemplateLoaderForPath(path));
            }
        }
...
//對(duì)象轉(zhuǎn)數(shù)組,創(chuàng)建TemplateLoader 對(duì)象
TemplateLoader loader = getAggregateTemplateLoader(templateLoaders);
//config設(shè)置loader對(duì)象
config.setTemplateLoader(loader);
        
/*---------FreeMarkerConfigurationFactory end--------*/

講loader放入對(duì)象

public class TemplateCache {
    private final TemplateLoader templateLoader;
    public TemplateCache(TemplateLoader templateLoader, ...) {
        this.templateLoader = templateLoader;
   }
}

我們可以看到templateLoader最終使用場(chǎng)景

templateloader調(diào)用鏈.png

太多了甩挫,不過(guò)沒(méi)關(guān)系贴硫,研究過(guò)freemarker渲染邏輯知道。獲取視圖核心源碼:

 final MaybeMissingTemplate maybeTemp = cache.getTemplate(name, locale, customLookupCondition, encoding, parseAsFTL);
//繼續(xù)跟進(jìn)
 Template template = getTemplateInternal(name, locale, customLookupCondition, encoding, parseAsFTL);

可以看到我們440行伊者,既是讀取loader英遭。

    lastModified = lastModified == Long.MIN_VALUE ? templateLoader.getLastModified(source) : lastModified;            
            Template template = loadTemplate(
                    templateLoader, source,
                    name, newLookupResult.getTemplateSourceName(), locale, customLookupCondition,
                    encoding, parseAsFTL);
            cachedTemplate.templateOrException = template;
            cachedTemplate.lastModified = lastModified;
            storeCached(tk, cachedTemplate);

但是,通過(guò)斷點(diǎn)返現(xiàn)亦渗,沒(méi)有運(yùn)行到440行挖诸,被前面420行代碼截胡了

                newLookupResult = lookupTemplate(name, locale, customLookupCondition);
                
                if (!newLookupResult.isPositive()) {
                    storeNegativeLookup(tk, cachedTemplate, null);
                    return null;
                }

最終結(jié)果策略模式一陣?yán)@,到了代碼代碼791即央碟,上面以后一行

       //策略模式?
       @Override
        public TemplateLookupResult lookup(TemplateLookupContext ctx) throws IOException {
            return ctx.lookupWithLocalizedThenAcquisitionStrategy(ctx.getTemplateName(), ctx.getTemplateLocale());
        }

    private Object findTemplateSource(String path) throws IOException {
        final Object result = templateLoader.findTemplateSource(path);
        if (LOG.isDebugEnabled()) {
            LOG.debug("TemplateLoader.findTemplateSource(" +  StringUtil.jQuote(path) + "): "
                    + (result == null ? "Not found" : "Found"));
        }
        return modifyForConfIcI(result);
    }

ok,至此,我們可以確認(rèn)亿虽,最后的加載策略菱涤,就是通過(guò)初始化的loader進(jìn)行加載的.
我們來(lái)看下,兩種classLoader最后的區(qū)別:

spring.freemarker.prefer-file-system-access=true
spring.freemarker.prefer-file-system-access=false

可以看到洛勉,如果設(shè)置為false粘秆,我們使用的是SpringTemplateLoader.
SpringTemplateLoader如何實(shí)現(xiàn)讀取父目錄的代碼的呢?

2個(gè)問(wèn)題

為什么要用策略模式
中間420都截胡了收毫,后面440代碼還有什么用呢攻走。

繼續(xù)未完的游戲

templateLoader.findTemplateSource(path);
如何可以實(shí)現(xiàn),有文件的時(shí)候優(yōu)先讀取文件此再,沒(méi)有文件的時(shí)候讀取父項(xiàng)目中的內(nèi)容昔搂。

        for (TemplateLoader templateLoader : templateLoaders) {
            if (lastTemplateLoader != templateLoader) {
                Object source = templateLoader.findTemplateSource(name);
                if (source != null) {
                    if (sticky) {
                        lastTemplateLoaderForName.put(name, templateLoader);
                    }
                    return new MultiSource(source, templateLoader);
                }
            }
        }

擁有兩個(gè)對(duì)象,file對(duì)象在上面输拇,classLoader在下面摘符,故會(huì)優(yōu)先讀取file中的內(nèi)容。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末策吠,一起剝皮案震驚了整個(gè)濱河市逛裤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猴抹,老刑警劉巖带族,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蟀给,居然都是意外死亡蝙砌,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)坤溃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)拍霜,“玉大人,你說(shuō)我怎么就攤上這事薪介§艚龋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵汁政,是天一觀的道長(zhǎng)道偷。 經(jīng)常有香客問(wèn)我,道長(zhǎng)记劈,這世上最難降的妖魔是什么勺鸦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮目木,結(jié)果婚禮上换途,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好军拟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布剃执。 她就那樣靜靜地躺著,像睡著了一般懈息。 火紅的嫁衣襯著肌膚如雪肾档。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,258評(píng)論 1 300
  • 那天辫继,我揣著相機(jī)與錄音怒见,去河邊找鬼。 笑死姑宽,一個(gè)胖子當(dāng)著我的面吹牛遣耍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播低千,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼配阵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了示血?” 一聲冷哼從身側(cè)響起棋傍,我...
    開(kāi)封第一講書(shū)人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎难审,沒(méi)想到半個(gè)月后瘫拣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡告喊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年麸拄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片黔姜。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拢切,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秆吵,到底是詐尸還是另有隱情淮椰,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布纳寂,位于F島的核電站主穗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏毙芜。R本人自食惡果不足惜忽媒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腋粥。 院中可真熱鬧晦雨,春花似錦架曹、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至夹抗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纵竖,已是汗流浹背漠烧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留靡砌,地道東北人已脓。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像通殃,于是被迫代替她去往敵國(guó)和親度液。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

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