springboot整合freemarker的踩坑及解決
springboot整合freemarker踩坑
報(bào)錯(cuò)
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 "首頁(yè)" [in template "index.ftl" at line 2, column 1]
----] with root causefreemarker.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 "首頁(yè)" [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 "首頁(yè)" [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.
問(wèn)題原因
因?yàn)樵趕ervice里試著把redis邏輯代碼運(yùn)行,然后再控制臺(tái)上輸出,結(jié)果發(fā)現(xiàn)結(jié)果是沒(méi)有問(wèn)題的,而且通過(guò)報(bào)錯(cuò)信息初步認(rèn)為是前端的問(wèn)題,hots沒(méi)找到,但是跟之前的一個(gè)類(lèi)似功能相比較,然后通過(guò)不斷的對(duì)比,找到問(wèn)題是出在配置類(lèi)。
之前一直認(rèn)為template類(lèi)中的getname方法就能完成映射功能
其實(shí)需要在FreemarkerConfig類(lèi)里面進(jìn)行聲明
解決方法
再FreemarkerConfig類(lèi)先通過(guò)@Autowired導(dǎo)入template類(lèi),然后使用configuration.setSharedVariable(“hots”, hotsTemplate);進(jìn)行聲明,缺少這一步會(huì)使得前端對(duì)于<@hots>這個(gè)標(biāo)簽無(wú)法識(shí)別。
springboot freemarker基礎(chǔ)配置及使用
1.基礎(chǔ)配置
1.1引入依賴(lài)
<!-- 引入freemarker模板引擎的依賴(lài) --> <dependency> ? ? <groupId>org.springframework.boot</groupId> ? ? <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
1.2在application.properties配置文件中添加如下配置:
# 是否開(kāi)啟thymeleaf緩存,本地為false,生產(chǎn)建議為true spring.freemarker.cache=false spring.freemarker.charset=UTF-8 spring.freemarker.allow-request-override=false spring.freemarker.check-template-location=true #類(lèi)型 spring.freemarker.content-type=text/html spring.freemarker.expose-request-attributes=true spring.freemarker.expose-session-attributes=true # #文件后綴 spring.freemarker.suffix=.ftl #路徑 .ftl文件就創(chuàng)建在templates下面或者子目錄 spring.freemarker.template-loader-path=classpath:/templates/
2.基礎(chǔ)使用
首先我在controller類(lèi)中編寫(xiě)一個(gè)測(cè)試接口用來(lái)測(cè)試使用,編寫(xiě)內(nèi)容如下
@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調(diào)用map類(lèi)型數(shù)據(jù),實(shí)體類(lèi)也可使用此格式調(diào)用屬性
/*可直接獲取id對(duì)應(yīng)的v值*/ id:${map.id} name:${map.name}
2.2集合調(diào)用,set集合也可用此格式調(diào)用相應(yīng)的元素
list01:${list[0]} list02:${list[1]}
2.3字符串拼接的兩種方法
${"你好啊${map.name}"} ${"你好啊"+map.name}
2.4FreeMarker表達(dá)式中支持“+”、“-”、“*”、“/”、“%”運(yùn)算符
${list[1]+list[0]} ${list[1]-list[0]} ${list[1]*list[0]} ${list[1]/list[0]} ${list[1]%list[0]}
2.5表達(dá)式中支持的比較運(yùn)算符有如下幾種:
1. =(或者==):判斷兩個(gè)值是否相等;
2. !=:判斷兩個(gè)值是否不相等;
注: =和!=可以用作字符串、數(shù)值和日期的比較,但兩邊的數(shù)據(jù)類(lèi)型必須相同。而且FreeMarker的比較是精確比較,不會(huì)忽略大小寫(xiě)及空格。
3. >(或者gt):大于
4. >=(或者gte):大于等于
5. <(或者lt):小于
6. <=(或者lte):小于等于
注: 上面這些比較運(yùn)算符可以用于數(shù)字和日期,但不能用于字符串。大部分時(shí)候,使用gt比>有更好的效果,因?yàn)镕reeMarker會(huì)把>解釋成標(biāo)簽的結(jié)束字符。可以使用括號(hào)來(lái)避免這種
2.6時(shí)間格式轉(zhuǎn)換
${date?string("yyyy-MM-dd")}
2.7支持邏輯表達(dá)式
FreeMarker支持邏輯運(yùn)算但是只支持布爾型邏輯運(yùn)算兩個(gè)值進(jìn)行比較運(yùn)算的的時(shí)候需要添加括號(hào)才能進(jìn)行邏輯運(yùn)算,一般用于指令中
FreeMarker支持邏輯運(yùn)算但是只支持布爾型邏輯運(yùn)算兩個(gè)值進(jìn)行比較運(yùn)算的的時(shí)候需要添加括號(hào)才能進(jìn)行邏輯運(yùn)算,一般用于指令中
1. &&: 邏輯與;
2. ||:邏輯或;
3. !:邏輯非
邏輯運(yùn)算符只能用于布爾值。
2.8FreeMarker無(wú)法理解null值
FreeMarker的變量必須賦值,否則就會(huì)拋出異常。而對(duì)于FreeMarker來(lái)說(shuō),null值和不存在的變量是完全一樣的,因?yàn)镕reeMarker無(wú)法理解null值。
FreeMarker提供兩個(gè)運(yùn)算符來(lái)避免空值:
1.!運(yùn)算
1.1若是msg不存在不報(bào)異常默認(rèn)空值,若是不使用!會(huì)報(bào)異常<br>
${msg!}
1.2若是存在則輸出,若是不存在則默認(rèn)為abc<br>
${map.a!"abc"}<br>
2.??返回布爾值一般用在指令中下面會(huì)有引用此處不解釋<br>
2.9 if指令
<#if map.aa?? > ? ? 存在 ? ? <#else > ? ? 不存在 </#if> <br> <#if (list[0]>=3) > ? ? 優(yōu)秀 ? ? <#elseif (list[0]>=2)> ? ? 良好 ? ? <#else > ? ? 差勁 </#if>
2.10 list指令 遍歷
<#list list as li> ? ? ${li}<br> </#list>
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Springboot整合Freemarker的實(shí)現(xiàn)詳細(xì)過(guò)程
- springboot整合freemarker代碼自動(dòng)生成器
- Springboot整合freemarker和相應(yīng)的語(yǔ)法詳解
- 如何在SpringBoot+Freemarker中獲取項(xiàng)目根目錄
- Springboot整合FreeMarker的實(shí)現(xiàn)示例
- SpringBoot整合Freemarker的基本步驟
- SpringBoot整合freemarker實(shí)現(xiàn)代碼生成器
- SpringBoot整合FreeMarker的過(guò)程詳解
- SpringBoot結(jié)合FreeMarker視圖渲染的實(shí)現(xiàn)
相關(guān)文章
Java線(xiàn)程池運(yùn)行狀態(tài)監(jiān)控實(shí)現(xiàn)解析
這篇文章主要介紹了Java線(xiàn)程池運(yùn)行狀態(tài)監(jiān)控實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10SpringBoot引入swagger報(bào)錯(cuò)處理的解決方法
這篇文章主要給大家介紹SpringBoot引入swagger是會(huì)出現(xiàn)報(bào)錯(cuò)的處理解決方法,文中有詳細(xì)的解決過(guò)程,感興趣的小伙伴可以跟著小編一起來(lái)學(xué)習(xí)吧2023-06-06詳解mybatis plus使用insert沒(méi)有返回主鍵的處理
這篇文章主要介紹了詳解mybatis plus使用insert沒(méi)有返回主鍵的處理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java實(shí)現(xiàn)訂單超時(shí)未支付自動(dòng)取消的8種方法總結(jié)
這篇文章主要為大家介紹了Java實(shí)現(xiàn)訂單超時(shí)未支付自動(dòng)取消功能的8種不同方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-08-08java、spring、springboot中整合Redis的詳細(xì)講解
這篇文章主要介紹了java、spring、springboot中整合Redis的詳細(xì)講解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04