声明:本文主要介绍在用springboot集成enjoy模板引擎后,修改java代码或者前台代码时项目热加载重启之后出现异常(com.jfinal.template.TemplateException: object is not an instance of declaring class)的解决方案以及原因;
简单介绍spring-boot-devtools:
当我们用springboot构建项目的时候,如果需要其支持热加载(即修改代码保存后项目自动重新启动,节省开发时间)则需要引入spring-boot-devtools依赖,maven坐标如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
如果我们打算集成enjoy模板引擎,则还需要引入enjoy的maven依赖,坐标如下:
<!-- enjoy模板引擎 begin --> <dependency> <groupId>com.jfinal</groupId> <artifactId>enjoy</artifactId> <version>3.2</version> </dependency> <!-- enjoy模板引擎 end -->
具体的配置这里不再赘述,请参考jfinal官方文档,其中有章节对springboot整合enjoy做出了详细介绍。做到这一步我们大多数人一般会觉得万事大吉可以愉快的在springboot项目中使用enjoy,而不用继续使用thymeleaf,freemarker诸如此类的模板引擎了(至少我是屁颠屁颠的准备愉快的干活了~),然而“惊喜”(异常)马上就要出现了!接下来贴测试代码,首先是实体类和controller代码:
/** * Description: 用户实体类 */ public class User { private String name; private int age; set get 方法忽略...... }
@Controller public class TestController { @RequestMapping("/test") public String test(HttpServletRequest request, HttpServletResponse response, Model model) { User user = new User(); user.setName("张三"); user.setAge(20); model.addAttribute("user", user); return "test"; } }
接着是html代码:
<!DOCTYPE html> <html> <head> <title>test</title> </head> <body> 用户姓名:<p>#(user.name)</p> 用户年龄:<p>#(user.age)</p> </body> </html>
接着不用说,启动项目,访问http://localhost:8080/test,页面如下图所示,如您所见,一切正常:
重点来了:此时你随便修改下代码,比如在页面上多加一行如下所示:
<!DOCTYPE html> <html> <head> <title>test</title> </head> <body> 用户姓名:<p>#(user.name)</p> 用户年龄:<p>#(user.age)</p> 用户年龄:<p>#(user.age)</p> <!-- 新加的一行 --> </body> </html>
此时项目会进行热加载,之后刷新页面,这时你一定会觉得一切照常,只不过页面上多了一行 用户年龄:20 罢了,但实际是:控制台会报如下异常:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is com.jfinal.template.TemplateException: object is not an instance of declaring class
Template: "/view/test.html". Line: 7
局部截图如下:
而此时如果你把服务关了,重新启动项目,在没有修改任何代码的情况下,刷新页面,呈现给你的将是你之前所期待的那样,页面上只不过多显示了一句 用户年龄:20,究竟是什么原因导致的呢?下面几句话简单分析下(引用百度来的内容,期待大神在评论中具体分析下~):
spring-boot-devtools帮助springboot项目实现热加载的原理是:其使用了两 个ClassLoader,一个Classloader加载那些不会改变的类(第三方Jar包,即我们通过 maven引进来的jar包),另一个ClassLoader加载会更改的类(即我们自己写的),称为 restartClassLoader,这样在有代码更改的时候,原来的restartClassLoader 被丢弃, 重新创建一个restartClassLoader,但是以jar形式引入的class文件(即编译后的文件) 会使用基础的ClassLoader(也就是AppClassLoader)加载,不是restartClassLoader, 也就是说我们修改了java代码或者html启动热加载后,第三方的jar和自己写的文件被加载 的classLoader并不是同一个。
说到这里,解决办法其实也就出来了,我们只需要让加载第三方jar的classLoader和自己写的文件的classLoader是同一个就OK,对于enjoy来说,只需要让enjoy-3.2.jar中的文件和我们自己写的文件是同一个classLoader加载即可,方法就是 在src/main/resources目录下面创建META-INF文件夹,然后创建spring-devtools.properties文件,文件里面的内容如下即可:
restart.include.thirdparty=/enjoy-3.2.jar
综上所述:在你用springboot项目打算集成enjoy模板引擎的时候,如果打算使用spring-boot-devtools来支持springboot项目实现热加载的话(方便开发的时候提高调试效率),在按照Jfinal官方文档中关于配置enjoy的步骤做完之后,只需要在src/main/resources目录下面创建META-INF文件夹,然后创建spring-devtools.properties文件,其中加上
restart.include.thirdparty=/enjoy-3.2.jar
即可。
------下面的不重要,看到这里即可
补充说明一个问题:如果你只想修改java文件的时候热加载,修改html css js时不热加载,只需要在application.properties文件中加上
spring.devtools.restart.exclude=static/**,view/**
即可,我的静态资源 css js在src/main/resources/static文件夹下放着,html在src/main/resources/view的子目录下放着