Search code examples
springthymeleaf

Multiple folder configuration for thymeleaf templates


I have the a simple spring based project which uses thymeleaf, being defined in my context.xml file. For better organization of the project, the re-usable chunk of template is clubbed in a child folder. This pic will give you a better idea.

enter image description here

I am able to "include" templates defined in all the html pages under templates, but have so far been unsuccessful in including templates defined under layup folder.

The following are the bean definitions in my context.xml file

<bean id="templateResolver"
    class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
    <property name="prefix" value="/WEB-INF/templates/" />
    <property name="suffix" value=".html" />
    <property name="templateMode" value="HTML5" />
    <property name="characterEncoding" value="UTF-8" />
    <property name="order" value="1" />
    <property name="cacheable" value="false" />
</bean>


<bean id="formTemplateResolver"
    class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
    <property name="prefix" value="/WEB-INF/templates/layup/" />
    <property name="suffix" value=".html" />
    <property name="templateMode" value="HTML5" />
    <property name="order" value="2" />
    <property name="cacheable" value="false" />
</bean>

<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />

<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
    <property name="templateResolvers">
        <set>
            <ref bean="templateResolver" />
            <ref bean="formTemplateResolver" />
        </set>
    </property>
    <property name="additionalDialects">
        <set>
            <bean
                class="org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect" />
        </set>
    </property>
</bean>

<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
    <property name="templateEngine" ref="templateEngine" />
    <property name="characterEncoding" value="UTF-8" />
</bean>

I did refer to the this link in reaching here.

This is the error stack I am obtaining.

SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'templateResolver' defined in class path resource [servlet-context.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.thymeleaf.templateresolver.ServletContextTemplateResolver]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.thymeleaf.templateresolver.ServletContextTemplateResolver.<init>()
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1076)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1021)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:700)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:381)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:293)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4236)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4739)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:803)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:780)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:583)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:944)
at org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:779)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:505)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1322)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:325)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:142)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1069)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:822)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1061)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
at org.apache.catalina.core.StandardService.start(StandardService.java:525)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:759)
at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414) Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.thymeleaf.templateresolver.ServletContextTemplateResolver]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.thymeleaf.templateresolver.ServletContextTemplateResolver.<init>()
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:85)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1069)
... 37 more Caused by: java.lang.NoSuchMethodException: org.thymeleaf.templateresolver.ServletContextTemplateResolver.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:80)
... 38 more

How do I resolve this issue?

Edit - Updating the question with controller code as requested

@RequestMapping(value = "/firstTab", method = RequestMethod.GET)
public String firstTab(){
    return "firstTabFormPage :: firstTabForm";
}

I am facing exceptions as soon as I deploy the .war files in my webapps folder and start tomcat. The obtained error stack is posted in the main question.


Solution

  • I was finally able to figure out my mistakes. I made a couple of them.

    • My return statement in the controller is wrong. It should

       @RequestMapping(value = "/firstTab", method = RequestMethod.GET)
        public String firstTab(){
        return "layup/firstTabFormPage :: firstTabForm";
       }
      

      I forgot to include the parent folder name in my return statement.

    • I am including JavaScript code in my *.html page between the following

      <th:block th:fragment="myScripts">
      <script type="text/javascript" th:inline="javascript">
      /*<![CDATA[*/
           <_my_scripts_here_>
      /*]]>*/
      </script>
      </th:block>
      

      and including them directly in my html th segment as so

      <th:block th:include="myHtmlPage::myScripts"></th:block>
      

    I am suppose to include it like

    <th:block th:include="layup/myHtmlPage::myScripts"></th:block>
    

    i.e the complete address including parent_folder(layup) and all.

    On the same lines it is possible to have a complex folder structure and all of them can be easily mapped.