首頁 > 軟體

springboot整合freemarker的踩坑及解決

2022-05-21 19:00:42

springboot整合freemarker踩坑

報錯

2021-04-23 02:01:18.148 ERROR 9484 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:
==> hots  [in template "inc/right.ftl" at line 6, column 11]

----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
    - Failed at: @hots  [in template "inc/right.ftl" at line 6, column 9]
    - Reached through: #include "/inc/right.ftl"  [in template "index.ftl" at line 52, column 13]
    ~ Reached through: #nested  [in template "inc/layout.ftl" in macro "layout" at line 25, column 5]
    ~ Reached through: @layout "首頁"  [in template "index.ftl" at line 2, column 1]
----] with root cause

freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:
==> hots  [in template "inc/right.ftl" at line 6, column 11]

----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
    - Failed at: @hots  [in template "inc/right.ftl" at line 6, column 9]
    - Reached through: #include "/inc/right.ftl"  [in template "index.ftl" at line 52, column 13]
    ~ Reached through: #nested  [in template "inc/layout.ftl" in macro "layout" at line 25, column 5]
    ~ Reached through: @layout "首頁"  [in template "index.ftl" at line 2, column 1]
----
    at freemarker.core.InvalidReferenceException.getInstance(InvalidReferenceException.java:134) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.UnifiedCall.accept(UnifiedCall.java:106) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Environment.visit(Environment.java:330) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Environment.visit(Environment.java:336) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Environment.include(Environment.java:2582) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Include.accept(Include.java:171) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Environment.visit(Environment.java:366) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Environment.invokeNestedContent(Environment.java:618) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.BodyInstruction.accept(BodyInstruction.java:60) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Environment.visit(Environment.java:366) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Environment.invoke(Environment.java:775) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.UnifiedCall.accept(UnifiedCall.java:83) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Environment.visit(Environment.java:330) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Environment.visit(Environment.java:336) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.core.Environment.process(Environment.java:309) ~[freemarker-2.3.28.jar:2.3.28]
    at freemarker.template.Template.process(Template.java:384) ~[freemarker-2.3.28.jar:2.3.28]
    at org.springframework.web.servlet.view.freemarker.FreeMarkerView.processTemplate(FreeMarkerView.java:389) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.view.freemarker.FreeMarkerView.doRender(FreeMarkerView.java:302) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.view.freemarker.FreeMarkerView.renderMergedTemplateModel(FreeMarkerView.java:253) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.view.AbstractTemplateView.renderMergedOutputModel(AbstractTemplateView.java:178) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1370) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1116) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1055) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.14.jar:9.0.14]
    at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

2021-04-23 02:01:18.177 ERROR 9484 --- [nio-8080-exec-1] s.e.ErrorMvcAutoConfiguration$StaticView : Cannot render error page for request [/] and exception [The following has evaluated to null or missing:
==> hots  [in template "inc/right.ftl" at line 6, column 11]

----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
    - Failed at: @hots  [in template "inc/right.ftl" at line 6, column 9]
    - Reached through: #include "/inc/right.ftl"  [in template "index.ftl" at line 52, column 13]
    ~ Reached through: #nested  [in template "inc/layout.ftl" in macro "layout" at line 25, column 5]
    ~ Reached through: @layout "首頁"  [in template "index.ftl" at line 2, column 1]
----] as the response has already been committed. As a result, the response may have the wrong status code.

問題原因

因為在service裡試著把redis邏輯程式碼執行,然後再控制檯上輸出,結果發現結果是沒有問題的,而且通過報錯資訊初步認為是前端的問題,hots沒找到,但是跟之前的一個類似功能相比較,然後通過不斷的對比,找到問題是出在設定類。

之前一直認為template類中的getname方法就能完成對映功能

其實需要在FreemarkerConfig類裡面進行宣告

解決方法

再FreemarkerConfig類先通過@Autowired匯入template類,然後使用configuration.setSharedVariable(“hots”, hotsTemplate);進行宣告,缺少這一步會使得前端對於<@hots>這個標籤無法識別。

springboot freemarker基礎設定及使用

1.基礎設定

1.1引入依賴

<!-- 引入freemarker模板引擎的依賴 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

1.2在application.properties組態檔中新增如下設定:

# 是否開啟thymeleaf快取,本地為false,生產建議為true
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.allow-request-override=false
spring.freemarker.check-template-location=true
#型別
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=true
spring.freemarker.expose-session-attributes=true
#
#檔案字尾
spring.freemarker.suffix=.ftl
#路徑 .ftl檔案就建立在templates下面或者子目錄
spring.freemarker.template-loader-path=classpath:/templates/

2.基礎使用

首先我在controller類中編寫一個測試介面用來測試使用,編寫內容如下

@Controller
public class FreeMarkerController {

    Map<String ,Object> map = new HashMap<>();
    List<Object> list = new ArrayList();
    @GetMapping("/freemaker/{id}/{name}")
    public String demo(@PathVariable("id")String id ,@PathVariable("name") String name,Model model){

        map.clear();
        map.put("id",id);
        map.put("name",name);
        list.add(2);
        list.add(4);
        map.put("aa",null);
        model.addAttribute("map",map);
        model.addAttribute("list",list);
        model.addAttribute("date",new Date());
        return "index";
    }
}

以下所有變數都是有上面介面提供

2.1呼叫map型別資料,實體類也可使用此格式呼叫屬性

/*可直接獲取id對應的v值*/
id:${map.id}
name:${map.name}

2.2集合呼叫,set集合也可用此格式呼叫相應的元素

list01:${list[0]}
list02:${list[1]}

2.3字串拼接的兩種方法

${"你好啊${map.name}"}
${"你好啊"+map.name}

2.4FreeMarker表示式中支援“+”、“-”、“*”、“/”、“%”運運算元

${list[1]+list[0]}
${list[1]-list[0]}
${list[1]*list[0]}
${list[1]/list[0]}
${list[1]%list[0]}

2.5表示式中支援的比較運運算元有如下幾種:

1. =(或者==):判斷兩個值是否相等;
2. !=:判斷兩個值是否不相等;
注: =和!=可以用作字串、數值和日期的比較,但兩邊的資料型別必須相同。而且FreeMarker的比較是精確比較,不會忽略大小寫及空格。
3. >(或者gt):大於
4. >=(或者gte):大於等於
5. <(或者lt):小於
6. <=(或者lte):小於等於
注: 上面這些比較運運算元可以用於數位和日期,但不能用於字串。大部分時候,使用gt比>有更好的效果,因為FreeMarker會把>解釋成標籤的結束字元。可以使用括號來避免這種

2.6時間格式轉換

${date?string("yyyy-MM-dd")}

2.7支援邏輯表示式

FreeMarker支援邏輯運算但是隻支援布林型邏輯運算兩個值進行比較運算的的時候需要新增括號才能進行邏輯運算,一般用於指令中

FreeMarker支援邏輯運算但是隻支援布林型邏輯運算兩個值進行比較運算的的時候需要新增括號才能進行邏輯運算,一般用於指令中
1. &&: 邏輯與;
2. ||:邏輯或;
3. !:邏輯非
邏輯運運算元只能用於布林值。

2.8FreeMarker無法理解null值

FreeMarker的變數必須賦值,否則就會丟擲異常。而對於FreeMarker來說,null值和不存在的變數是完全一樣的,因為FreeMarker無法理解null值。
FreeMarker提供兩個運運算元來避免空值:
1.!運算
1.1若是msg不存在不報異常預設空值,若是不使用!會報異常<br>
${msg!}
1.2若是存在則輸出,若是不存在則預設為abc<br>
${map.a!"abc"}<br>
2.??返回布林值一般用在指令中下面會有參照此處不解釋<br>

2.9 if指令

<#if map.aa?? >
    存在
    <#else >
    不存在
</#if>
<br>
<#if (list[0]>=3) >
    優秀
    <#elseif (list[0]>=2)>
    良好
    <#else >
    差勁
</#if>

2.10 list指令 遍歷

<#list list as li>
    ${li}<br>
</#list>

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com