同时使用 Thymeleaf 和 JSP

Posted

技术标签:

【中文标题】同时使用 Thymeleaf 和 JSP【英文标题】:Using both Thymeleaf and JSP 【发布时间】:2015-04-13 08:25:45 【问题描述】:

我使用的是 JSP + JSTL,但我对 c:if、c:choose 感到厌烦...

所以,我希望我的 JSP 页面同时使用 JSP 和 Thymeleaf 呈现(我计划尽快删除所有 JSTL)。我正在使用 Spring MVC 框架:

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/" />
    <property name="suffix" value=".jsp" />
    <property name="order" value="1" />
</bean>
<!-- Thymeleaf -->
<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
    <property name="prefix" value="/WEB-INF/pages/" />
    <property name="suffix" value=".html" />
    <property name="templateMode" value="HTML5" />
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
    <property name="templateResolver" ref="templateResolver" />
</bean>
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
    <property name="templateEngine" ref="templateEngine" />
    <property name="order" value="2" />
</bean> 

在我的控制器中,我只返回不带扩展名的 jsp。

return "folder/page";

可以先用 JSP 解析器再用 Thymeleaf 解析器渲染我的 JSP 页面吗?如果是,怎么做?

JSP 和 Thymeleaf 的链接似乎很复杂。所以,我想对 JSP 文件使用内部解析器,对 HTML 文件使用 Thymeleaf 模板解析器。我该怎么做?

【问题讨论】:

对您的想法的一些想法:您可以提供自己的 ViewResolver 来检测 ServletContextTemplateResolverInternalResourceViewResolver,但它会同时处理这两者,这可能会导致繁重的 WTF 场景。就像评论一样,因为我没有看到任何解决方案可以同时处理两者。 我了解如何将 JSP 用于 *.jsp 文件,将 Thymeleaf 用于 *.html 文件?我必须更改控制器中的内容吗? 如果我理解正确,您现在“只是”想根据返回的视图进行切换?所以没有混合视图处理。您可以通过将 Thymeleaf ViewResolver 的文件名后缀更改为 html 来尝试。它可能毫无例外地通过 InternalViewResolver 并触发 templateResolver。因此,您只需将新模板重命名为 *.html。我这里没有代码和库来测试这个想法。 如果不可能或真的很痛苦,我需要找到另一种解决方案。所以我现在想为 HTML 模板文件添加 Thymeleaf。我会保留 JSP 支持,因为我需要时间来重写它们。 【参考方案1】:

根据 Thymeleaf 论坛上的this post,您有两种解决方案。

第一个解决方案

删除 bean 声明(&lt;property name="suffix" value=".html" /&gt;&lt;property name="suffix" value=".jsp" /&gt;)中的后缀属性,并在控制器的返回值中传递后缀,例如:

@RequestMapping("/view1")
public String thymeleafView()
    return "mythymeleafview.html";


@RequestMapping("/view2")
public String jspView()
    return "myjspview.html";

第二种解决方案

viewNames 属性添加到解析器。该值是包含视图的文件夹的名称,具体取决于视图的扩展名。因此,您将有一个文件夹用于 JSP 文件,另一个文件夹用于 HTML (thymeleaf) 文件,例如:

配置

<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
     <property name="prefix" value="/WEB-INF/views/" />
     <property name="suffix" value=".html" />
     <property name="viewNames" value="thymeleaf/*" />
     <property name="templateMode" value="HTML5" />
</bean>

<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
     <property name="prefix" value="/WEB-INF/views/" />
     <property name="viewNames" value="jsp/*" />
     <property name="suffix" value=".jsp" />
</bean>

控制器

@RequestMapping("/view1") 
public String thymeleafView()  
     return "thymeleaf/mythymeleafview"; 
 

@RequestMapping("/view2") 
public String jspView()  
     return "jsp/myjspview"; 

项目文件夹

WEB-INF/views/jsp/myjspview.jsp
WEB-INF/views/thymeleaf/mythymeleafview.jsp

两种解决方案都有效,但有一些限制。无论您想使用 JSP 还是 Thymeleaf 解析,您都必须指定一种或另一种方式。

链接 JSP 和 Thymeleaf 的“完美”解决方案——包括在无法用 Thymeleaf 解决视图时尝试用 JSP 解决视图,或者反之亦然——是不可能的,Daniel Fernández (Thymeleaf 团队)在this same post 中解释了原因:

Thymeleaf 允许您创建任何您希望的 ITemplateResolver 实现,包括一些在实际阅读模板之前可能不允许确定模板是否存在的实现。 [...] 因此,Thymeleaf 在尝试处理模板之前无法确定模板是否可解析。这就是 ThymeleafViewResolver 必须求助于“viewNames”属性的原因。

【讨论】:

【参考方案2】:

另外,两个 servlet 也可以正常工作。关键是尽量减少 servlet 配置,并包含一个 appConfig.xml 用于数据库和其他服务(这样可以避免大量重复配置)

Web.xml:

<web-app id="WebApp_ID" version="2.4"
   xmlns="http://java.sun.com/xml/ns/j2ee" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

   <display-name>Spring MVC Application</display-name>

   <servlet>
      <servlet-name>AssessmentAdmin</servlet-name>
      <servlet-class>
         org.springframework.web.servlet.DispatcherServlet
      </servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>
 <servlet-mapping>
      <servlet-name>AssessmentAdmin</servlet-name>
      <url-pattern>/xz/*</url-pattern>
   </servlet-mapping>
   
   
<servlet>
      <servlet-name>AssessmentAdminTL</servlet-name>
      <servlet-class>
         org.springframework.web.servlet.DispatcherServlet
      </servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>   
   <servlet-mapping>
      <servlet-name>AssessmentAdminTL</servlet-name>
      <url-pattern>/xztl/*</url-pattern>
   </servlet-mapping>
   ........

jsp 的 servlet:

<mvc:annotation-driven />
	
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/" />
		 <!--  <property name="viewNames" value="jsp/*" />-->
		  <property name="suffix" value=".jsp" />
	</bean>



	..........



	
	<import resource="applicationContext.xml" />



</beans>

百里香的servlet

<mvc:annotation-driven />
	
<!-- Thymeleaf -->

<bean id="templateResolver"
        class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
    <property name="prefix" value="/WEB-INF/html/" />
    <property name="suffix" value=".html" />
    <property name="templateMode" value="HTML5" />
    <property name="cacheable" value="false" />
  </bean>
    
  <bean id="templateEngine"
        class="org.thymeleaf.spring4.SpringTemplateEngine">
    <property name="templateResolver" ref="templateResolver" />
  </bean>
   
  <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
    <property name="templateEngine" ref="templateEngine" />
  </bean> 
    
 
   
  <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
    <property name="templateEngine" ref="templateEngine" />
  </bean> 	
  
  


	
	<import resource="applicationContext.xml" />

试过了,效果很好

【讨论】:

【参考方案3】:

这是基于@Igd 响应的答案

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/" />
    <property name="viewNames" value="*.jsp" />
</bean>
<!-- Thymeleaf -->
<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
    <property name="prefix" value="/WEB-INF/pages/" />
    <property name="templateMode" value="HTML5" />
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewNames" value="redirect*" />
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
    <property name="templateResolver" ref="templateResolver" />
</bean>
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
    <property name="templateEngine" ref="templateEngine" />
    <property name="viewNames" value="*.html" />
</bean> 

我将它用于映射:

@RequestMapping("/view1")
public String thymeleafView()
    return "mythymeleafview.html";


@RequestMapping("/view2")
public String jspView()
    return "myjspview.jsp";

【讨论】:

【参考方案4】:

根据@Athanor 的回答,我们可能有另一种选择。

我们使用属性“viewNames”来控制模板选择哪个解析器

<!-- jsp -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/" />
    <property name="suffix" value=".jsp" />
    <property name="order" value="1" />
    <property name="viewNames" value="*admin/*,*packer/*,*courier/*,/" />
</bean>
<!-- thymeleaf -->
<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
    <property name="prefix" value="/" />
    <property name="suffix" value=".html" />
    <property name="templateMode" value="HTML5" />
    <property name="cacheable" value="false"/>
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
    <property name="templateResolver" ref="templateResolver" />
</bean>
<bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
    <property name="characterEncoding" value="UTF-8"/>
    <property name="templateEngine" ref="templateEngine" />
    <property name="viewNames" value="*thymeleaf/*" />
    <property name="order" value="2" />
</bean>

和控制器

@RequestMapping(value="/test")
public ModelAndView dboxPrint(Model model)
    ModelAndView modelAndView = new ModelAndView("thymeleaf/dbox_print");

    return modelAndView;

【讨论】:

以上是关于同时使用 Thymeleaf 和 JSP的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC、Tiles2、ThymeLeaf 和自然模板

Blade 到 Thymeleaf 模板和视图转换的疑惑

Spring Boot、Spring Security 和 Thymeleaf:将 CsrfFilter 应用到带有表单的网站

spring boot thymeleaf和jsp可以共存吗

为什么Thymeleaf找不到我的物体?

使用 thymeleaf 在浏览器中打印 Spring Boot JSON RESTful 响应