在基于 Web 的 Spring 范围中使用 Thymeleaf 处理 HTML 文件并将处理后的模板存储为字符串
Posted
技术标签:
【中文标题】在基于 Web 的 Spring 范围中使用 Thymeleaf 处理 HTML 文件并将处理后的模板存储为字符串【英文标题】:Process HTML file using Thymeleaf in Web based Scopes of Spring and store the processed template as String 【发布时间】:2018-09-29 14:12:32 【问题描述】:我正在尝试使用 thymeleaf 呈现 html 文件,并将生成的 HTML 内容保存在 web-based scopes of Spring
的 String 变量中,以便以后可以使用它来发送电子邮件或将内容转换为 pdf。我已经完成了this 网站中给出的实现,但它给了我我无法弄清楚的错误。我正在使用带有 thymeleaf 3.0.9 n thymeleaf dialect 2.2.2 的 spring boot 1.5.12.RELEASE。下面是我的实现。请帮忙。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
@Controller
public class jataController
@GetMapping(value = "/manual-thym")
@ResponseBody
public void justSample()
Context context = new Context();
String filename = "templates/view/failure";
String html = renderHtml(filename, context);
System.out.println("template\n" + html);
private String renderHtml(String filename, Context context)
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCacheable(false);
templateResolver.setOrder(1);
templateResolver.setCharacterEncoding("UTF-8");
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
String html = templateEngine.process(filename, context);
return html;
还有 StackTrace:
2018-04-19 12:26:43.742 ERROR 6375 --- [nio-9000-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: ognl/PropertyAccessor] with root cause
java.lang.ClassNotFoundException: ognl.PropertyAccessor
at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_162]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_162]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338) ~[na:1.8.0_162]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_162]
at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_162]
at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[na:1.8.0_162]
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.8.0_162]
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) ~[na:1.8.0_162]
at java.net.URLClassLoader.access$100(URLClassLoader.java:73) ~[na:1.8.0_162]
at java.net.URLClassLoader$1.run(URLClassLoader.java:368) ~[na:1.8.0_162]
at java.net.URLClassLoader$1.run(URLClassLoader.java:362) ~[na:1.8.0_162]
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_162]
at java.net.URLClassLoader.findClass(URLClassLoader.java:361) ~[na:1.8.0_162]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_162]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338) ~[na:1.8.0_162]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_162]
at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.<init>(OGNLVariableExpressionEvaluator.java:83) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.standard.StandardDialect.getVariableExpressionEvaluator(StandardDialect.java:179) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.standard.StandardDialect.getExecutionAttributes(StandardDialect.java:393) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.DialectSetConfiguration.build(DialectSetConfiguration.java:263) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.EngineConfiguration.<init>(EngineConfiguration.java:123) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.TemplateEngine.initialize(TemplateEngine.java:336) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1079) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1059) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1048) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at com.siqes.flight.controller.jataController.renderHtml(jataController.java:37) ~[classes/:na]
at com.siqes.flight.controller.jataController.justSample(jataController.java:19) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_162]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_162]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_162]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_162]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55) ~[spring-boot-1.5.12.RELEASE.jar:1.5.12.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:111) ~[spring-boot-actuator-1.5.12.RELEASE.jar:1.5.12.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106) ~[spring-boot-actuator-1.5.12.RELEASE.jar:1.5.12.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:613) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.29.jar:8.5.29]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_162]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_162]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.29.jar:8.5.29]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_162]
【问题讨论】:
【参考方案1】:如果TemplateEngine
在singleton
bean 中自动装配,那么下面提到的代码就完美了。
@Controller
public class jataController
@Autowired
private TemplateEngine templateEngine;
@GetMapping(value = "/manual-thym")
@ResponseBody
public void justSample()
Context context = new Context();
String filename = "templates/view/generated-ticket.html";
String html = renderHtml(filename, context);
System.out.println("template\n" + html);
private String renderHtml(String filename, Context context)
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCacheable(false);
templateResolver.setOrder(1);
templateResolver.setCharacterEncoding("UTF-8");
templateEngine.setTemplateResolver(templateResolver);
String html = templateEngine.process(filename, context);
return html;
但是如果 TemplateEngine
自动装配在 request
范围 bean 类型上,它会给出异常,thymeleaf
会造成内存泄漏。所以最后经过大量的尝试和尝试,我得到了一个可行的解决方案,感谢@Paizo。它可能包含一些不好的做法,但这就是它的工作原理:
@Controller
@Configuration
@EnableWebMvc
@ApplicationScope
public class MyThymeleafConfig
@GetMapping("/view-template")
@ResponseBody
public void viewTemplates()
Context context = new Context();
context.setVariable("mydata", "this is it");
String html = templateEngine().process("templates/view-to-process.html", context);
System.out.println(html);
/*
configuration for thymeleaf and template processing
*/
@Bean
public SpringTemplateEngine templateEngine()
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
return templateEngine;
@Bean
public SpringResourceTemplateResolver thymeleafTemplateResolver()
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("classpath:");
templateResolver.setSuffix(".html");
templateResolver.setCacheable(false);
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
@Bean
public ThymeleafViewResolver thymeleafViewResolver()
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
为了提供静态资源,我们需要定义另一个bean,如下:
@Configuration
@EnableWebMvc
public class StaticResourceConfig implements WebMvcConfigurer
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
registry
.addResourceHandler("/**")
.addResourceLocations("/static/", "classpath:static/");
在任何论坛上都找不到此解决方案。但是,我会要求任何Spring
开发人员为上述代码提供更好的实现。
【讨论】:
因为它使用标准配置的默认@Bean,在你创建一个没有配置的新实例之前。另外,您为什么要手动转换模板并将其返回到控制器中? 我想用 thymeleaf 处理 html 页面,然后将其存储在字符串变量中,以便我可以将其用于 html 到 pdf 的转换。 这意味着自动装配采用spring默认bean的默认配置。 为什么不将内容保存在 Redis 中? 我应该在redis中坚持什么【参考方案2】:见下方更新
当您配置 Thymeleaf 时,您应该定义模板引擎和模板解析器,否则当您使用自动装配默认值时。如果您每次都创建一个实例,这不是一个好习惯。这里是一个示例配置:
@Configuration
@EnableWebMvc
public class ThymeleafConfiguration
@Bean
public SpringTemplateEngine templateEngine()
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
return templateEngine;
@Bean
public SpringResourceTemplateResolver thymeleafTemplateResolver()
SpringResourceTemplateResolver templateResolver
= new SpringResourceTemplateResolver();
templateResolver.setPrefix("/WEB-INF/views/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
然后,如果您想以编程方式进行试验,您可以自动装配它们,但使用 thymeleaf 提供 html 页面的通常流程也是定义视图解析器:
@Bean
public ThymeleafViewResolver thymeleafViewResolver()
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
一切就绪后,您可以将控制器编写为:
@Controller
public class MyController
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test()
return "yourTemplateName";
可以使用模型属性将参数传递给模板。
2018 年 7 月 31 日更新
不幸的是,我没有时间完成工作概念验证,但是我认为下面的代码足以显示流程。如果您运行它并调用localhost:8080/test
,您应该能够在控制台中看到输出html。 pdf 生成可以作为视图解析器添加和/或以编程方式调用,在此示例中使用 xhtmlrenderer
;我没有时间完成它,所以我把它注释掉了,但你可以明白这一点:一个服务通过自动装配模板引擎来提供 html 和 pdf 生成。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.paizo</groupId>
<artifactId>html2pdf</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>html2pdf</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.14</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
ThymeleafConfiguration.java
@Configuration
public class ThymeleafConfiguration
@Bean
public SpringTemplateEngine templateEngine()
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
return templateEngine;
@Bean
public ClassLoaderTemplateResolver thymeleafTemplateResolver()
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
HelloWorldController.java
@Controller
public class HelloWorldController
@Autowired
private Html2PdfService pdfService;
@GetMapping(path = "/test")
public String hello()
Map parameters = new HashMap();
parameters.put("name", "Borat");
System.out.println(pdfService.template2Html("test", parameters));
return "test";
// @ResponseBody
// @GetMapping
// public ResponseEntity helloPdf()
// Map parameters = new HashMap();
// parameters.put("name", "Borat");
// pdfService.template2Pdf("test", parameters);
// String filePath = "PATH_HERE";
// InputStream inputStream = new FileInputStream(new File(filePath));
// InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
// HttpHeaders headers = new HttpHeaders();
// headers.setContentLength();
// return new ResponseEntity(inputStreamResource, headers, HttpStatus.OK);
//
Html2PdfService.java
@Service
public class Html2PdfService
@Autowired
private TemplateEngine templateEngine;
public OutputStream template2Pdf(String templateName, Map parameters)
// OutputStream outputStream = new BufferedOutputStream();
// IOUtils.copy()
//
// Context ctx = new Context();
// String processedHtml = templateEngine.process(templateName, ctx);
// ITextRenderer renderer = new ITextRenderer();
// renderer.setDocumentFromString(processedHtml);
// renderer.layout();
// renderer.createPDF(os, false);
// renderer.finishPDF();
return null;
public String template2Html(String templateName, Map parameters)
Context ctx = new Context();
ctx.setVariable("name", "pippo");
String processedHtml = templateEngine.process(templateName, ctx);
return processedHtml;
Html2pdfApplication.java
@SpringBootApplication
public class Html2pdfApplication
public static void main(String[] args)
SpringApplication.run(Html2pdfApplication.class, args);
作为旁注,如果您计划动态生成 pdf 并将其作为控制器中的响应,我建议使用流而不是字节数组或临时文件。
【讨论】:
您好@Paizo,您想查看我的答案,以便我可以给您赏金 嗨@SAM我刚刚用一个工作示例更新了答案,对于html部分来说更干净,而不是在你的代码上点对点;我不确定您提到的泄漏,它可能与控制器类中定义的所有注释的混合有关 是的,我使用 ByteArrayInputStream 避免临时文件。 感谢您抽出宝贵的时间。我能够处理模板并使用 itext 生成 pdf。我相信飞碟也使用itext。但问题在于 bean 的范围。如果我切换到请求、会话、globalSession 等基于 Web 的范围,我会遇到异常。但是使用 thymeleaf 配置描述的 beans 进行模板处理在应用程序范围内没有任何问题。当用配置和控制器注释一个类时,我觉得这是一种不好的做法。关于如何重构我的代码的任何解决方案。 是的,看我的:D 提供@Service
的功能(默认为单例),不要手动创建ClassLoaderTemplateResolver
的实例,只需在@Configuration
类中定义bean 并自动装配它们在服务中,无论请求如何,都应该为您提供 1 个 bean 实例。不要混合多个类注解,@EnableWebMvc
不需要。该服务为您提供了一个抽象层,使您可以轻松更改实现。如果你可以直接从 itext->service->controller 传递 pdf 流,性能和内存会更好【参考方案3】:
我正在使用类似的 Springboot 和 Thymeleaf 版本,这样的东西在一些项目中对我有用:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
@Component
public class EmailProcessor
private TemplateEngine htmlTemplateEngine;
@Autowired
public EmailProcessor(TemplateEngine templateEngine)
this.htmlTemplateEngine = templateEngine;
public String process(User user)
final Context ctx = new Context();
if (user != null)
ctx.setVariable("user", user);
return htmlTemplateEngine.process("emails/template", ctx);
电子邮件模板只是一个常规的 Thymeleaf 模板,位于:
资源/模板/电子邮件/template.html
【讨论】:
当 bean 类型为“单例”时,这也对我有用。但是当 bean 类型是“请求”时,它会给你错误。试试看。【参考方案4】:使用 Maven,将 ognl 依赖项添加到您的 pom.xml 文件中,例如(在MVNRepository 中找到最新的依赖项/版本):
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.1.12</version>
</dependency>
【讨论】:
【参考方案5】:就我而言,我的 html 模板不在正确的目录中。将其更改为“src/main/resources/abc.html”,这修复了我的错误:
java.lang.ClassNotFoundException: ognl.PropertyAccessor
【讨论】:
以上是关于在基于 Web 的 Spring 范围中使用 Thymeleaf 处理 HTML 文件并将处理后的模板存储为字符串的主要内容,如果未能解决你的问题,请参考以下文章
springboot thymeleaf引入css和js必须添加th吗