查看原文
其他

深入 Spring Boot :实现对 Fat Jar jsp 的支持

ImportNew 2019-12-01

(点击上方公众号,可快速关注)


来源:Hengyunabc ,

hengyunabc.github.io/spring-boot-fat-jar-jsp-sample/


spring boot 对于jsp支持的限制


对于jsp的支持,Spring Boot官方只支持了war的打包方式,不支持fat jar。参考官方文档


https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-jsp-limitations


这里spring boot官方说是tomcat的问题,实际上是spring boot自己改变了打包格式引起的。参考之前的文章


http://hengyunabc.github.io/spring-boot-classloader/#spring-boot-1-3-和-1-4-版本的区别


原来的结构之下,tomcat是可以扫描到fat jar里的META-INF/resources目录下面的资源的。在增加了BOOT-INF/classes之后,则tomcat扫描不到了。


那么怎么解决这个问题呢?下面给出一种方案,来实现对spring boot fat jar/exploded directory的jsp的支持。


个性化配置tomcat,把BOOT-INF/classes 加入tomcat的ResourceSet


在tomcat里,所有扫描到的资源都会放到所谓的ResourceSet里。比如servlet 3规范里的应用jar包的META-INF/resources就是一个ResourceSet。


现在需要想办法把spring boot打出来的fat jar的BOOT-INF/classes目录加到ResourceSet里。


下面通过实现tomcat的 LifecycleListener接口,在Lifecycle.CONFIGURE_START_EVENT事件里,获取到BOOT-INF/classes的URL,再把这个URL加入到WebResourceSet里。


/**

 * Add main class fat jar/exploded directory into tomcat ResourceSet.

 *

 * @author hengyunabc 2017-07-29

 *

 */

public class StaticResourceConfigurer implements LifecycleListener {

 

    private final Context context;

 

    StaticResourceConfigurer(Context context) {

        this.context = context;

    }

 

    @Override

    public void lifecycleEvent(LifecycleEvent event) {

        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {

            URL location = this.getClass().getProtectionDomain().getCodeSource().getLocation();

 

            if (ResourceUtils.isFileURL(location)) {

                // when run as exploded directory

                String rootFile = location.getFile();

                if (rootFile.endsWith("/BOOT-INF/classes/")) {

                    rootFile = rootFile.substring(0, rootFile.length() - "/BOOT-INF/classes/".length() + 1);

                }

                if (!new File(rootFile, "META-INF" + File.separator + "resources").isDirectory()) {

                    return;

                }

 

                try {

                    location = new File(rootFile).toURI().toURL();

                } catch (MalformedURLException e) {

                    throw new IllegalStateException("Can not add tomcat resources", e);

                }

            }

 

            String locationStr = location.toString();

            if (locationStr.endsWith("/BOOT-INF/classes!/")) {

                // when run as fat jar

                locationStr = locationStr.substring(0, locationStr.length() - "/BOOT-INF/classes!/".length() + 1);

                try {

                    location = new URL(locationStr);

                } catch (MalformedURLException e) {

                    throw new IllegalStateException("Can not add tomcat resources", e);

                }

            }

            this.context.getResources().createWebResourceSet(ResourceSetType.RESOURCE_JAR, "/", location,

                    "/META-INF/resources");

 

        }

    }

}


为了让spring boot embedded tomcat加载这个 StaticResourceConfigurer,还需要一个EmbeddedServletContainerCustomizer的配置:


@Configuration

@ConditionalOnProperty(name = "tomcat.staticResourceCustomizer.enabled", matchIfMissing = true)

public class TomcatConfiguration {

    @Bean

    public EmbeddedServletContainerCustomizer staticResourceCustomizer() {

        return new EmbeddedServletContainerCustomizer() {

            @Override

            public void customize(ConfigurableEmbeddedServletContainer container) {

                if (container instanceof TomcatEmbeddedServletContainerFactory) {

                    ((TomcatEmbeddedServletContainerFactory) container)

                            .addContextCustomizers(new TomcatContextCustomizer() {

                                @Override

                                public void customize(Context context) {

                                    context.addLifecycleListener(new StaticResourceConfigurer(context));

                                }

                            });

                }

            }

        };

    }

}


这样子的话,spring boot就可以支持fat jar里的jsp资源了。


demo地址: 


https://github.com/hengyunabc/spring-boot-fat-jar-jsp-sample


总结


  • spring boot改变了打包结构,导致tomcat没有办法扫描到fat jar里的/BOOT-INF/classes

  • 通过一个StaticResourceConfigurer把fat jar里的/BOOT-INF/classes加到tomcat的ResourceSet来解决问题


【关于投稿】


如果大家有原创好文投稿,请直接给公号发送留言。


① 留言格式:
【投稿】+《 文章标题》+ 文章链接

② 示例:
【投稿】《不要自称是程序员,我十多年的 IT 职场总结》:http://blog.jobbole.com/94148/

③ 最后请附上您的个人简介哈~



看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存