SpringBoot集成FreeMarker 作者: nbboy 时间: 2020-04-02 分类: 默认分类,Java 评论 ## SpringBoot 集成Freemarker Freemarker是一个Java模板系统,集成非常简单。 1. 第一步在POM文件中引入自动配置包 ``` org.springframework.boot spring-boot-starter-freemarker ``` 2. 第二部在模板目录下创建模板test.ftlh,内容为 ```html ${msg} ``` 3. 第三部创建调用的Controller ```java @Controller @RequestMapping("/auth") public class AuthController { @RequestMapping("/test") public String test(Model model){ model.addAttribute("msg", "Hello,Freemarker"); return "auth/test"; } } ``` 4.运行,就能看到动态内容已经渲染出来了。 ## Freemarker API简单使用 ## SpringMVC调用Freemarker浅析 完整的MVC调用链这里先隐去,从org.springframework.web.servlet.DispatcherServlet#doDispatch看下调用的逻辑: ```java protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } //这里的开始处理控制器返回的内容,渲染视图就在这里面处理 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } } ``` 在org.springframework.web.servlet.DispatcherServlet#processDispatchResult里面,我们看到了渲染的方法。 ```java private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; //省略部分代码 // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } //省略部分代码 } ``` 再看下这个方法org.springframework.web.servlet.DispatcherServlet#render,完整的查找模板解析器和解析器渲染的过程就在这里。 ```java protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine locale for request and apply it to the response. //省略部分代码 View view; //获得视图名称 String viewName = mv.getViewName(); if (viewName != null) { // We need to resolve the view name. //这里查找到模板解析器 view = resolveViewName(viewName, mv.getModelInternal(), locale, request); } try { if (mv.getStatus() != null) { response.setStatus(mv.getStatus().value()); } //做真正的渲染 view.render(mv.getModelInternal(), request, response); } //省略部分代码 } ``` OK,我们看下org.springframework.web.servlet.DispatcherServlet#resolveViewName的方法是怎样的。 ```java protected View resolveViewName(String viewName, @Nullable Map model, Locale locale, HttpServletRequest request) throws Exception { if (this.viewResolvers != null) { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } } return null; } ``` 我们的Freemarker解析器FreeMarkerViewResolver也在里面,这个解析器FreeMarkerViewResolver实现了ViewResolver接口就可以了。 至于渲染,刚才也看到会调用view的render方法,然后通过层层调用,最后调用org.springframework.web.servlet.view.freemarker.FreeMarkerView#doRender方法,方法如下所述: ```java protected void doRender(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Expose model to JSP tags (as request attributes). exposeModelAsRequestAttributes(model, request); // Expose all standard FreeMarker hash models. SimpleHash fmModel = buildTemplateModel(model, request, response); // Grab the locale-specific version of the template. Locale locale = RequestContextUtils.getLocale(request); processTemplate(getTemplate(locale), fmModel, response); } protected void processTemplate(Template template, SimpleHash model, HttpServletResponse response) throws IOException, TemplateException { template.process(model, response.getWriter()); } ``` 至此,MVC里的调用就结束了,Template是Freemarker包下的模板对象。也可看到最后带过去的是数据模型和输出流,有了这两个东西,要动态输出是轻而易举的事情了。 ## SpringBoot Freemarker 自动配置如何实现 配置Freemarker主要是通过org.springframework.boot.autoconfigure.freemarker.FreeMarkerProperties实现的,不过有一些属性是来自基类org.springframework.boot.autoconfigure.template.AbstractViewResolverProperties,其中属性还是挺多,主要常用的比如 ```java /** * Whether to enable MVC view resolution for this technology. */ private boolean enabled = true; /** * Whether to enable template caching. */ private boolean cache; /** * Content-Type value. */ private MimeType contentType = DEFAULT_CONTENT_TYPE; /** * Template encoding. */ private Charset charset = DEFAULT_CHARSET; /** * Prefix that gets prepended to view names when building a URL. */ private String prefix; /** * Suffix that gets appended to view names when building a URL. */ private String suffix; ``` ## 总结 虽然现在都提倡前后端分离,不过适当了解了解模板渲染有关的内容也是挺有帮助的,因为就目前来说毕竟用SPA一体化富应用在SEO方面做得不够理想,还有对于比较简单不怎么交互的页面也完全可以用后端渲染完成,所以在这方面花适当的时间还是值得的。
通过Tomcat部署SpringBoot程序 作者: nbboy 时间: 2020-03-17 分类: Java 评论 # 概述 因为以前没有通过Tomcat方式部署过程序,所以才有了这次踩坑之旅。这里记录下,权当备忘吧。 # POM文件改动 先把打包方式改为打War包 ``` war ``` 增加Tomcat的依赖,需要说明的是SpringBoot本来就自带一个Tomcat,但是在生产环境中不推荐用这个Tomcat,就是打包的时候不要把他打包进去。 ``` org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat ``` 然后再自己增加一个Tomcat依赖,但是把作用域设置为provided,意思是说在本地开发的时候还是要这个Tomcat包的。 ``` org.springframework.boot spring-boot-starter-tomcat provided ``` 另外再修改servlet-api也根据环境打包,因为生产环境Tomcat有自己的servlet-api包。 ``` javax.servlet javax.servlet-api provided ``` # 安装Tomcat 这个在网上教程比较多,也没有什么坑,我参考的是这篇:[Ubuntu安装Tomcat](https://www.howtoing.com/how-to-install-apache-tomcat-8-on-ubuntu-16-04 "Ubuntu安装Tomcat") # 继承SpringBootServletInitializer 在启动类上继承这个SpringBootServletInitializer,然后重载configure方法,代码如下: ```java @SpringBootApplication @EnableSwagger2Doc @MapperScan(basePackages = "org.chenxf.studystore.dao") public class StudyStoreApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(StudyStoreApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(StudyStoreApplication.class); } } ``` # 项目打包 最后一步,执行mvn clean install -Dmaven.test.skip=true进行打包,打包完成后再target目录下生成的*.war包拷贝到Tomcat的webapps目录下即可。 # 总结 在实际操作中,我没有执行最后一步的打包命令,而是执行了SpringBoot配置的Plugins里的war命令去打包的,结果总是404错误,后来发现打的war包其实并没有把classes下的编译文件打进去。 另外也参考了这两篇文章提供的环境配置思路和tomcat的war包部署方法。基础还是很薄弱呀,需要继续加油,和读者共勉!!! 参考资料: [springboot+tomcat不同环境采用不同配置文件](https://www.cnblogs.com/GreenMountain/p/9720070.html "springboot+tomcat不同环境采用不同配置文件") [tomcat部署war包的三种方式](https://blog.csdn.net/sinat_36710456/article/details/83308236 "tomcat部署war包的三种方式")