简体   繁体   中英

Issues with Spring Boot and Thymeleaf

Here are my configuration classes::

In the templateResolver() method without ServletContext parameter i get compile error so add it as a parameter and give it to ServletContextTemplateResolver(servletContext);

@Configuration
@EnableWebMvc
@ComponentScan(basePackages= {"com.packtpub.springsecurity"})
public class ThymeleafConfig {

    @Bean
    public ServletContextTemplateResolver templateResolver(ServletContext servletContext) {
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(servletContext);
        resolver.setPrefix("/WEB-INF/templates/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode("HTML5");
        resolver.setCacheable(false);
        resolver.setOrder(1);
        return resolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine(final ServletContextTemplateResolver templateResolver) {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver);
        return engine;
    }

    @Bean
    public ThymeleafViewResolver thymeleafViewResolver(final SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine);
        return resolver;
    }

}

When i run my application i get following error:::

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method templateResolver in com.packtpub.springsecurity.web.configuration.ThymeleafConfig required a bean of type 'javax.servlet.ServletContext' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'javax.servlet.ServletContext' in your configuration.

What am i doing wrong? thanks

Other config files are

<?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>chapter2</groupId>
    <artifactId>chapter2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>chapter2</name>
    <description>chapter 2 test</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.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-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>



        <!-- Thymeleaf -->  
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>





        <!-- Spring dependencies START-->
        <dependency>
           <groupId>org.springframework.security</groupId>
           <artifactId>spring-security-web</artifactId>
           </dependency>
        <dependency>
           <groupId>org.springframework.security</groupId>
           <artifactId>spring-security-config</artifactId>
           </dependency>
        <dependency>
           <groupId>org.springframework.security</groupId>
           <artifactId>spring-security-crypto</artifactId>
           </dependency>
        <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-webmvc</artifactId>
           </dependency>
        <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-jdbc</artifactId>
           </dependency>
        <!-- Servlet and JSP related dependencies -->
        <dependency>
           <groupId>javax.servlet</groupId>
           <artifactId>javax.servlet-api</artifactId>
           <scope>provided</scope>
        </dependency>
        <dependency>
           <groupId>javax.servlet.jsp</groupId>
           <artifactId>javax.servlet.jsp-api</artifactId>
           <version>2.3.1</version>
           <scope>provided</scope>
        </dependency>
        <dependency>
           <groupId>javax.servlet.jsp.jstl</groupId>
           <artifactId>javax.servlet.jsp.jstl-api</artifactId>
           <version>1.2.1</version>
        </dependency>
        <dependency>
           <groupId>taglibs</groupId>
           <artifactId>standard</artifactId>
           <version>1.1.2</version>
        </dependency>
        <!-- For datasource configuration -->
        <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-dbcp2</artifactId>
           </dependency>
        <!-- We will be using MySQL as our database server -->
        <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>6.0.6</version>
        </dependency>   
        <!-- Spring dependencies END -->    



        <dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20170516</version>
</dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>



@Configuration
//@Import({SecurityConfig.class, DataSourceConfig.class})
@ComponentScan(basePackages =
        {
                "com.packtpub.springsecurity.dataaccess",
                "com.packtpub.springsecurity.domain",
                "com.packtpub.springsecurity.service"
        }
)
@PropertySource(value = {"classpath:application.properties"})
public class JavaConfig {

    /**
     * Note: If you want to use @PropertySource, you must create a static
     * PropertySourcesPlaceholderConfigurer with the @Bean as seen here.
     * @return PropertySourcesPlaceholderConfigurer
     * @throws java.io.IOException
     */
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
        PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
        propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
        propertySourcesPlaceholderConfigurer.setProperties(yamlPropertiesFactoryBean().getObject());
        return propertySourcesPlaceholderConfigurer;
    }

    @Bean
    public static YamlPropertiesFactoryBean yamlPropertiesFactoryBean() {
        YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
        yaml.setResources(new ClassPathResource("application.yml"));
        return yaml;
    }
} // The end...



@Order(1)
public class SecurityWebAppInitializer
        extends AbstractSecurityWebApplicationInitializer {

    /**
     * Don't initialize the filter directly, the Spring WebApplicationInitializer
     * parent will take care of the initialization.
     */
    public SecurityWebAppInitializer() {
        super();
    }

} // The end...




public class WebAppInitializer
        extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { JavaConfig.class, SecurityConfig.class, DataSourceConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebMvcConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/*" };
    }

    @Override
    public void onStartup(final ServletContext servletContext)
            throws ServletException {

        // Register DispatcherServlet
        super.onStartup(servletContext);

        // Register H2 Admin console:
        ServletRegistration.Dynamic h2WebServlet = servletContext.addServlet("h2WebServlet",
                "org.h2.server.web.WebServlet");
        h2WebServlet.addMapping("/admin/h2/*");
        h2WebServlet.setInitParameter("webAllowOthers", "true");

    }

} // The End...




@Configuration
@EnableWebMvc
@Import({ThymeleafConfig.class})
@ComponentScan(basePackages = {
        "com.packtpub.springsecurity.web.controllers",
        "com.packtpub.springsecurity.web.model"
})
public class WebMvcConfig extends WebMvcConfigurerAdapter
{

    @Autowired
    private ThymeleafViewResolver thymeleafViewResolver;

    /**
     * We mention this in the book, but this helps to ensure that the intercept-url patterns prevent access to our
     * controllers. For example, once security has been applied for administrators try commenting out the modifications
     * to the super class and requesting <a
     * href="http://localhost:800/calendar/events/.html">http://localhost:800/calendar/events/.html</a>. You will
     * observe that security is bypassed since it did not match the pattern we provided. In later chapters, we discuss
     * how to secure the service tier which helps mitigate bypassing of the URL based security too.
     */
    // FIXME: FInd out what this is and why it is here.
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping result = new RequestMappingHandlerMapping();
        result.setUseSuffixPatternMatch(false);
        result.setUseTrailingSlashMatch(false);
        return result;
    }

    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        super.addResourceHandlers(registry);
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/resources/")
                .setCachePeriod(31_556_926)
        ;
    }

    @Override
    public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
        configurer
                .ignoreAcceptHeader(false)
                .favorPathExtension(true) // .html / .json / .ms
                .defaultContentType(MediaType.TEXT_HTML) // text/html
                .mediaTypes(
                        new HashMap<String, MediaType>(){
                            {
                                put("html", MediaType.TEXT_HTML);
                                put("xml", MediaType.APPLICATION_XML);
                                put("json", MediaType.APPLICATION_JSON);
                            }
                        })
        ;
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new MappingJackson2HttpMessageConverter());
    }

    /*@Bean
    public ContentNegotiatingViewResolver contentNegotiatingViewResolver() {

        ContentNegotiatingViewResolver result = new ContentNegotiatingViewResolver();
        Map<String, String> mediaTypes = new HashMap<>();
        mediaTypes.put("json", MediaType.APPLICATION_JSON_VALUE);
//        result.setMediaTypes(mediaTypes);

        result.setDefaultViews(Collections.singletonList(jacksonView()));

        return result;
    }*/

    @Bean
    public MappingJackson2JsonView jacksonView() {
        MappingJackson2JsonView jacksonView = new MappingJackson2JsonView();
        jacksonView.setExtractValueFromSingleKeyModel(true);

        Set<String> modelKeys = new HashSet<String>();
        modelKeys.add("events");
        modelKeys.add("event");
        jacksonView.setModelKeys(modelKeys);

        return jacksonView;
    }

    @Override
    public void configureViewResolvers(final ViewResolverRegistry registry) {
        registry.viewResolver(thymeleafViewResolver);
    }


    // i18N support
    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {
        ReloadableResourceBundleMessageSource resource = new ReloadableResourceBundleMessageSource();
        resource.setBasenames("/WEB-INF/locales/messages");
        resource.setDefaultEncoding("UTF-8");
        resource.setFallbackToSystemLocale(Boolean.TRUE);
        return resource;
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
        configurer.enable();
    }


}

UPDATED

I have deleted the following dependencies from the POM according to @Adina in the comment below but still get the errors

<!-- dependency>
           <groupId>javax.servlet</groupId>
           <artifactId>javax.servlet-api</artifactId>
           <scope>provided</scope>
        </dependency>
        <dependency>
           <groupId>javax.servlet.jsp</groupId>
           <artifactId>javax.servlet.jsp-api</artifactId>
           <version>2.3.1</version>
           <scope>provided</scope>
        </dependency>
        <dependency>
           <groupId>javax.servlet.jsp.jstl</groupId>
           <artifactId>javax.servlet.jsp.jstl-api</artifactId>
           <version>1.2.1</version>
        </dependency>
        <dependency>
           <groupId>taglibs</groupId>
           <artifactId>standard</artifactId>
           <version>1.1.2</version>
        </dependency-->

What else can i do again

After some debugging on your code, the real problem is that you are autowiring ThymeleafViewResolver in the configuration responsible to configure servlet context.

 public class WebMvcConfig implements WebMvcConfigurer{

     @Autowired
     private ThymeleafViewResolver thymeleafViewResolver;

The main problem is that before initializing your ServletContext, the application will try to initialize the ServletContextTemplateResolver (autowired will impose bean order initialization) and as you've noticed it depends on ServletContext.

Solution :

  • delete ThymeleafConfig class
  • don't autowire ThymeleafViewResolver in WebMvcConfig
  • and don't override method configureViewResolvers.

Don't worry, Thymeleaf will be set by default as the viewResolver.

Most of the config you've provided is already "taken care" by spring-boot-starter-thymeleaf. If you want just to change default view directory resolver, you can just add in application.properties

spring.mvc.view.prefix=/WEB-INF/templates/
spring.mvc.view.suffix=.html

PS: Take extra care when you override default definition from spring starters, as you can see these types of bugs are not easy to find.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM