Spring-Boot + Spring-MVC + Thymeleaf + Apache Tiles

前端 未结 2 1200
时光取名叫无心
时光取名叫无心 2020-12-10 20:06

I have an already existing client module with apache tiles and thymeleaf, what works well. I wanted to convert it to spring-boot and wanted to do it step by step, but I am r

相关标签:
2条回答
  • 2020-12-10 20:53

    Okay guys,

    I got it and I hope it helps for other developers with similar problems.

    In POM I removed all Spring-Dependencies and I use only Spring-Starter-Dependencies like following snippet:

    ...
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    
        <!-- Apache Tiles -->
        <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-template</artifactId>
            <version>${org.apache.tiles-version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tiles</groupId>
            <artifactId>tiles-servlet</artifactId>
            <version>${org.apache.tiles-version}</version>
        </dependency>
    
        <!-- Thymeleaf ... -->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-tiles2-spring4</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>
    
    ....
    

    In my Starter-Class I actived Thymeleaf Autoconfiguration again, my full class looks like follows. Be attended, I use two different servlets, one for REST, one for Thymeleaf.

    @Configuration
    @Import({ ServiceConfig.class, RestMvcConfig.class, WebMvcConfig.class })
    @EnableAutoConfiguration
    public class ApplicationClientMvc {
        public static void main(final String[] args) {
            SpringApplication.run(ApplicationClientMvc.class, args);
        }
    
        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }
    
        @Bean
        public ServletRegistrationBean applicationDispatcherRegistration(final DispatcherServlet dispatcherServlet) {
            final ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet);
            registration.setName("applicationServlet");
            registration.addUrlMappings("/app/*");
            registration.setAsyncSupported(true);
            registration.setLoadOnStartup(1);
            registration.addInitParameter("contextConfigLocation", "a.c.b.c.m.w.WebMvcConfig");
            registration.setOrder(1);
            return registration;
        }
    
        @Bean
        public ServletRegistrationBean restDispatcherRegistration(final DispatcherServlet dispatcherServlet) {
            final ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet);
            registration.setName("restServlet");
            registration.addUrlMappings("/rest/*");
            registration.setAsyncSupported(true);
            registration.setLoadOnStartup(0);
            registration.addInitParameter("contextConfigLocation", "a.c.b.c.m.r.RestMvcConfig");
            registration.setOrder(0);
            return registration;
        }
    }
    

    ====

    My WebMVCConfig Class looks like follows, is in package ....mvc.rest:

    @Configuration
    @ComponentScan
    public class WebMvcConfig extends WebMvcAutoConfigurationAdapter {
    
        @Autowired public SpringTemplateEngine templateEngine;
    
        @Bean
        public ThymeleafTilesConfigurer tilesConfigurer() {
            final ThymeleafTilesConfigurer configurer = new ThymeleafTilesConfigurer();
            configurer.setDefinitions(ThymeleafAutoConfiguration.DEFAULT_PREFIX + "**/views.xml");
            return configurer;
        }
    
        @Bean
        public ThymeleafViewResolver thymeleafViewResolver() {
            final ThymeleafViewResolver resolver = new ThymeleafViewResolver();
            resolver.setViewClass(ThymeleafTilesView.class);
            resolver.setTemplateEngine(templateEngine);
            resolver.setCharacterEncoding(UTF_8);
            return resolver;
        }
    
        @Bean
        public TilesDialect tilesDialect() {
            return new TilesDialect();
        }
    
        //
    
        @Value("${server.session-timeout}") private Long sessionTimeOut;
    
        @Override
        public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
            configurer.setDefaultTimeout(sessionTimeOut * 1000L);
            configurer.registerCallableInterceptors(timeoutInterceptor());
        }
    
        @Bean
        public TimeoutCallableProcessingInterceptor timeoutInterceptor() {
            return new TimeoutCallableProcessingInterceptor();
        }
    
    }
    

    ===

    My RestConfig Class is pretty simple

    @Configuration
    @ComponentScan
    public class RestMvcConfig {}
    

    ===

    I moved all my html-files or thymeleaf-templates to template-folder under src/main/resources. Also in same folder you can put all client resources like images, js and css under the folder static. The folder static and templates are keywords and will be found by Spring-Boot automatically. There are no special configurations in application.properties what would be needed for thymeleaf and apache tiles.

    project structure

    ====

    That your application is able to find the resources, you can configure the path like follows:

    <link rel="stylesheet" type="text/css" media="screen, projection" 
              href="/static/css/bootstrap.css" 
              th:href="@{/css/bootstrap.css}" />
    
    <link rel="stylesheet" type="text/css" media="screen, projection" 
              href="/static/css/bootstrap-responsive.css" 
              th:href="@{/css/bootstrap-responsive.css}" />
    

    ===

    Folder java/src/webapp is no more needed!!!

    enter image description here

    ====

    If this helps for you, dont hesitate to vote ;-). If you need more information, just comment, I will try to answer within few days.

    ======

    SECOND STEP

    Okay, until now it only works under Eclipse. When I do executable jar, I get still an error. Maybe someone wants to help ;-)

    Stacktrace is :

    org.thymeleaf.exceptions.TemplateInputException: Error resolving template "/layout/login", template might not exist or might not be accessible by any of the configured Template Resolvers
        at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:245)
        at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104)
        at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
        at org.thymeleaf.extras.tiles2.renderer.ThymeleafAttributeRenderer.write(ThymeleafAttributeRenderer.java:155)
        at org.apache.tiles.renderer.impl.AbstractBaseAttributeRenderer.render(AbstractBaseAttributeRenderer.java:106)
        at org.thymeleaf.extras.tiles2.renderer.MetadataCleaningAttributeRendererWrapper.render(MetadataCleaningAttributeRendererWrapper.java:111)
        at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:670)
        at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:690)
        at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:644)
        at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:627)
        at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:321)
        at org.thymeleaf.extras.tiles2.spring4.web.view.ThymeleafTilesView.render(ThymeleafTilesView.java:125)
        at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1221)
        at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1005)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:952)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:280)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration$MetricsFilter.doFilterInternal(MetricFilterAutoConfiguration.java:90)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1736)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1695)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Unknown Source)
    

    The Structure of my JAR is like:

    enter image description here

    ======

    SOLUTION:

    I don't know exactly why, but additional I have to fix the url-paths for my templates, what works like it is in Eclipse, but not as executable jar. To get it run in both environments, I had to fix all template urls like follows, have a special look on attribute value:

    BEFORE:

    <definition name="home" extends="standard1ColLayout">
        <put-attribute name="title" value="/home/title :: content" type="thymeleaf" />
        <put-attribute name="body" value="/home/body :: content" type="thymeleaf" />
    </definition>
    

    AFTER:

    <definition name="home" extends="standard1ColLayout">
        <put-attribute name="title" value="./home/title :: content" type="thymeleaf" />
        <put-attribute name="body" value="./home/body :: content" type="thymeleaf" />
    </definition>
    

    Finally it works under Eclipse and as executable jars. Converting from normal Spring-Project to Spring-Boot Project was not really easy running, I hope it will help to other developers and I hope that post is worth to vote. ;-)

    0 讨论(0)
  • 2020-12-10 21:07

    This example demonstrate how to configure Thymeleaf with Apache tiles, but it is letting spring boot to autoconfigure Thymeleaf. This way you can still use all spring-boot predefined environment variables like spring.thymeleaf.* in your application.properties

    @Configuration
    @AutoConfigureAfter(ThymeleafAutoConfiguration.class)//tweak autoconfiguration for apache tiles after spring boot
    @EnableConfigurationProperties(ThymeleafProperties.class)
    public class ThymeleafTilesConfig {
    
        @Autowired//this bean is autoconfigured with spring-boot-thymeleaf
        private ThymeleafViewResolver thymeleafViewResolver;
    
        @Autowired
        private ThymeleafProperties properties;
    
        @PostConstruct
        public void setThymeleafTilesViewClass() {
            //just set view class for thymeleaf-tiles
            thymeleafViewResolver.setViewClass(ThymeleafTilesView.class);
        }
    
        @Bean  
        TilesDialect tilesDialect() {       
        // This bean will be auto picked-up by spring-boot and will be autoconfigured :)
            return new TilesDialect();
        }
    
        @Bean//create tiles configurer for your needs
        ThymeleafTilesConfigurer tilesConfigurer() {
            final ThymeleafTilesConfigurer configurer = new ThymeleafTilesConfigurer();
            configurer.setDefinitions("classpath:/templates/**/views.xml");
            configurer.setCheckRefresh(!properties.isCache());
            return configurer;
        }
    }
    

    https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java#L116

    0 讨论(0)
提交回复
热议问题