Spring-Boot with JSP Tag Libs in embedded Tomcat

后端 未结 5 1567
离开以前
离开以前 2021-01-19 00:52

I am currently migrating a Spring MVC Webapp (xml-config to java-config, tomcat to embedded tomcat via spring-boot).

The webapp uses freemarker as templating engine

5条回答
  •  后悔当初
    2021-01-19 01:33

    This really should be built-in.

    First, disable the built in FreeMarkerAutoConfiguration on your Application:

    @SpringBootApplication
    @EnableAutoConfiguration(exclude = {FreeMarkerAutoConfiguration.class})
    public class Application extends WebMvcConfigurerAdapter {
        ...
    ]
    

    Then add this custom configuration:

    (adapted from https://github.com/isopov/fan/blob/master/fan-web/src/main/java/com/sopovs/moradanen/fan/WebApplicationConfiguration.java; Added an ObjectWrapper to the TaglibFactory and removed the addResourceHandlers() override)

    import freemarker.cache.ClassTemplateLoader;
    import freemarker.ext.jsp.TaglibFactory;
    import freemarker.template.TemplateException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.support.ReloadableResourceBundleMessageSource;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
    import org.springframework.web.servlet.i18n.SessionLocaleResolver;
    import org.springframework.web.servlet.view.freemarker.FreeMarkerConfig;
    import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
    import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
    
    import javax.servlet.ServletContext;
    import java.io.IOException;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Locale;
    import java.util.Properties;
    
    @Configuration
    public class CustomFreemarkerConfiguration extends WebMvcConfigurerAdapter {
    
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
            localeChangeInterceptor.setParamName("lang");
            registry.addInterceptor(localeChangeInterceptor);
        }
    
        @Bean
        public ReloadableResourceBundleMessageSource messageSource() {
            ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
            messageSource.setBasename("classpath:messages");
            messageSource.setFallbackToSystemLocale(false);
            messageSource.setDefaultEncoding("UTF-8");
            return messageSource;
        }
    
        @Bean
        public SessionLocaleResolver localeResolver() {
            SessionLocaleResolver localeResolver = new SessionLocaleResolver();
            localeResolver.setDefaultLocale(Locale.ENGLISH);
            return localeResolver;
        }
    
        @Bean
        @Autowired
        public freemarker.template.Configuration freeMarkerConfig(ServletContext servletContext) throws IOException,
                TemplateException {
            FreeMarkerConfigurer freemarkerConfig = configFreeMarkerConfigurer(servletContext);
            return freemarkerConfig.getConfiguration();
        }
    
        @Bean
        @Autowired
        public TaglibFactory taglibFactory(ServletContext servletContext) throws IOException, TemplateException {
            FreeMarkerConfigurer freemarkerConfig = configFreeMarkerConfigurer(servletContext);
            TaglibFactory taglibFactory = freemarkerConfig.getTaglibFactory();
            taglibFactory.setObjectWrapper(freemarker.template.Configuration.getDefaultObjectWrapper(freemarker.template.Configuration.getVersion()));
            return taglibFactory;
        }
    
        @Autowired
        @Bean
        public FreeMarkerConfig springFreeMarkerConfig(ServletContext servletContext) throws IOException, TemplateException {
            return new MyFreeMarkerConfig(freeMarkerConfig(servletContext), taglibFactory(servletContext));
        }
    
        private static FreeMarkerConfigurer configFreeMarkerConfigurer(ServletContext servletContext) throws IOException,
                TemplateException {
            FreeMarkerConfigurer freemarkerConfig = new FreeMarkerConfigurer();
            freemarkerConfig
                    .setPreTemplateLoaders(new ClassTemplateLoader(CustomFreemarkerConfiguration.class, "/templates/"));
            ServletContext servletContextProxy = (ServletContext) Proxy.newProxyInstance(
                    ServletContextResourceHandler.class.getClassLoader(),
                    new Class[] { ServletContext.class },
                    new ServletContextResourceHandler(servletContext));
            freemarkerConfig.setServletContext(servletContextProxy);
            Properties settings = new Properties();
            settings.put("default_encoding", "UTF-8");
            freemarkerConfig.setFreemarkerSettings(settings);
            freemarkerConfig.afterPropertiesSet();
            return freemarkerConfig;
        }
    
        @Bean
        public FreeMarkerViewResolver viewResolver() {
            FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver();
            viewResolver.setCache(false);
            viewResolver.setSuffix(".ftl");
            viewResolver.setContentType("text/html;charset=UTF-8");
            return viewResolver;
        }
    
    
        private static class ServletContextResourceHandler implements InvocationHandler
        {
    
            private final ServletContext target;
    
            private ServletContextResourceHandler(ServletContext target) {
                this.target = target;
            }
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("getResourceAsStream".equals(method.getName())) {
                    Object result = method.invoke(target, args);
                    if (result == null) {
                        result = CustomFreemarkerConfiguration.class.getResourceAsStream((String) args[0]);
                    }
                    return result;
                } else if ("getResource".equals(method.getName())) {
                    Object result = method.invoke(target, args);
                    if (result == null) {
                        result = CustomFreemarkerConfiguration.class.getResource((String) args[0]);
                    }
                    return result;
                }
    
                return method.invoke(target, args);
            }
        }
    
        private static class MyFreeMarkerConfig implements FreeMarkerConfig {
    
            private final freemarker.template.Configuration configuration;
            private final TaglibFactory taglibFactory;
    
            private MyFreeMarkerConfig(freemarker.template.Configuration configuration, TaglibFactory taglibFactory) {
                this.configuration = configuration;
                this.taglibFactory = taglibFactory;
            }
    
            @Override
            public freemarker.template.Configuration getConfiguration() {
                return configuration;
            }
    
            @Override
            public TaglibFactory getTaglibFactory() {
                return taglibFactory;
            }
        }
    }
    

    Add the following to your pom.xml:

        
            org.springframework
            spring-webmvc
        
        
            javax.servlet.jsp
            jsp-api
            2.0
        
    

    Then you can load in your template:

    <#assign s=JspTaglibs["/META-INF/spring.tld"] />
    
    Home
    

提交回复
热议问题