解读Jfinal异常:com.jfinal.render.RenderException: freemarker.template.TemplateNotFoundException

JFinal 是基于Java 语言的极速 web 开发框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。更多查看 Jfinal官网

com.jfinal.render.RenderException: freemarker.template.TemplateNotFoundException ,见图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2020-05-20 16:02:45
[ERROR]-[Thread: http-apr-80-exec-2]-[com.jfinal.core.ActionHandler.handle()]: /test/test
com.jfinal.render.RenderException: freemarker.template.TemplateNotFoundException: Template not found for name "/test.html".
The name was interpreted by this TemplateLoader: WebappTemplateLoader(subdirPath="/", servletContext={contextPath="", displayName=null}).
    at com.jfinal.render.FreeMarkerRender.render(FreeMarkerRender.java:147)
    at com.jfinal.core.ActionHandler.handle(ActionHandler.java:97)
    at com.jfinal.ext.handler.ContextPathHandler.handle(ContextPathHandler.java:47)
    at com.yoncent.handler.SessionHandler.handle(SessionHandler.java:96)
    at com.yoncent.handler.FrontHandler.handle(FrontHandler.java:55)
    at com.jfinal.core.JFinalFilter.doFilter(JFinalFilter.java:72)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:492)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:165)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:1025)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:452)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1201)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:654)
    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2532)
    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2521)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
Caused by: freemarker.template.TemplateNotFoundException: Template not found for name "/test.html".
The name was interpreted by this TemplateLoader: WebappTemplateLoader(subdirPath="/", servletContext={contextPath="", displayName=null}).
    at freemarker.template.Configuration.getTemplate(Configuration.java:2797)
    at freemarker.template.Configuration.getTemplate(Configuration.java:2599)
    at com.jfinal.render.FreeMarkerRender.render(FreeMarkerRender.java:143)
    ... 23 more

报这个异常一般来说两种情况
①文件找不到(大多数是路径写的有问题)
②文件不存在或没有(少见)
很多博客说的解决办法是需要导入freemarker的jar包(不排除这种情况,如果你的项目是之前没有用这个,可能是;如果是接手的项目,人家之前已经有做好的功能,那就不是jar包的问题)
不是jar的问题或者不确定,看完这篇文章,你可能就明白了
在这里插入图片描述
首先配置configConstant(…)——Jfinal常量值,这里只关心baseViewPath,我的是“/WEB-INF/page”

1
2
3
4
5
6
7
8
9
10
@Override
    public void configConstant(Constants me) {
        PropKit.use("../config/druid.properties");
        me.setMaxPostSize(204857600);
        //配置 baseViewPath
        me.setBaseViewPath("/WEB-INF/page");
        me.setError404View("/404.html");
        me.setError500View("/500.html");
        me.setDevMode(PropKit.getBoolean("devMode", false));
    }

接下来是配置configRoute(…)——Jfinal路由,有很多个,我只写了测试的

1
2
3
4
5
6
@Override
    public void configRoute(Routes me) {
//      me.add("/test", TestController.class,"/sys");
        me.add("/test", TestController.class);
//      me.add("/test", TestController.class,"/sys/test");
    }

在看下控制器

1
2
3
4
5
6
7
8
9
//一定要继承Controller这个类
public class TestController extends Controller {
    public void test() {
        System.out.println("进入 TestController 中的test方法");
//        render("../test.html");
//        render("test.html");
        render("/test.html");
    }
}

再看下目录结构,对于非maven项目,存放资源的文件夹一般是WebRoot或者WebContent
在这里插入图片描述
配置路由Routes 类中添加路由的方法有两个

1
2
public Routes add(String controllerKey, Class<? extends Controller> controllerClass, String viewPath)
public Routes add(String controllerKey, Class<? extends Controller> controllerClass)

先说第一种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
    说明:这是两个类的代码块,这里写在一起了
*/
//config类
@Override
    public void configRoute1(Routes me) {
        //一、写成下面这样,对应的test文件夹是  web-inf/page/test
        me.add("/test", TestController.class,"/test");
        //二、写成下面这样,对应的test文件夹是  web-inf/page/sys/test  
        me.add("/test", TestController.class,"/sys/test");
    }
   
//controller类
public void test() {
    //写一行太少了,加个打印语句吧
    System.out.println("进入 TestController 中的test方法");
    render("test.html");
    /**
    configRoute1()写的是第一种,render()不变,跳转的页面是web-inf/page/test/test.html,对应效果图⑤
   
    configRoute1()写的是第二种,render()不变,跳转的页面是web-inf/page/sys/test/test.html,对应效果图⑥
    */
}

再看第二种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
    说明:这是两个类的代码块,这里写在一起了
*/
//config类
@Override
    public void configRoute1(Routes me) {
        me.add("/test", TestController.class);
    }
   
//controller类
public void test() {
    //写一行太少了,加个打印语句吧
    System.out.println("进入 TestController 中的test方法");
    render("test.html");
}

这样写就会报错 com.jfinal.render.RenderException:freemarker.template.TemplateNotFoundException 为什么???
因为最终的视图路径,规则如下:

1
finalView = baseViewPath + viewPath + view

baseViewPath和view已经设置,viewPath咱们没有设置,理论上没有问题,但是,当viewPath未指定时默认值为controllerKey(第一个参数,靠它找对应的Controller的),而controllerkey可视为一个文件夹(我们忽略了这个文件夹,把test.html直接放在了page文件夹下面,也就是③处的test.html),返回的finalView找不到这个文件夹(③处的test文件夹),就会报错,解决办法有两个:
第一种、加上这个文件夹,就没有问题了
第二种、不加这个文件夹(③处的test.html还是在page文件夹下),把Controller类中的render(“test.html”)改成render("…/test.html"),…/表示上级(父级),这样就没问题了

注意:当view以 “/” 字符打头时表示绝对路径,baseViewPath 与 viewPath 将被忽略。

解释下:拿我测试来说,当Controller类中的render("/test.html")写成了这样子,前面的baseViewPath (我写的是WEB-INF/page)和controllerkey("/test")都是无效的,那最终的finalView 就是WebRoot(有的项目是WebContent)文件夹下的test.html(①处)点击查看官方说法
在这里插入图片描述
能力有限,如有错误,欢迎指正