美文网首页Java编程
SpringBoot系列之集成jsp模板引擎

SpringBoot系列之集成jsp模板引擎

作者: smileNicky | 来源:发表于2019-12-08 19:34 被阅读0次

    SpringBoot系列之集成jsp模板引擎
    @[toc]

    1、模板引擎简介

    引用百度百科的模板引擎解释:

    模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。

    在JavaEE领域有几中比较常用的模板引擎,分别是Jsp、Velocity、Freemarker、Thymeleaf,不过对于前端页面渲染效率来说,jsp其实还是最快的,Velocity次之。Thymeleaf虽然渲染效率不是很快,但是语法方面是比较轻巧的,Thymeleaf语法比Velocity轻巧,但是渲染效率不如Velocity

    2、环境准备

    ok,Springboot是一款javaee框架,使用非常简捷,创建工程也是默认打成jar包的,启动jar包就可以直接运行嵌入式的Servlet容器,比如Tomcat等等,不过Springboot要集成模板引擎的话,是默认不支持jsp的,但是并不表示不能使用,首先Springboot项目默认是jar方式运行的,而我们之前的jsp项目大部分都是war包方式,jar方式打包的项目默认就没有webapp等等这些文件,所以我们要创建jsp项目,就可以创建工程时候改成war包形式

    ok,进行jsp实验,环境准备:

    • 版本:
      • Maven3.9+
      • SpringBoot2.2.1
    • IDE:
      • IntelliJ IDEA

    创建Springboot Initializer项目,打包方式选择war方式


    在这里插入图片描述

    创建好的项目,默认是没有webapp文件的,所以我们可以手动加上,或者在idea这样做:

    在这里插入图片描述

    新增web.xml,记得改下默认路径


    在这里插入图片描述

    创建好之后,我们可以看看自动生成的项目有什么特征:

    • 首先是多了一个ServletInitializer类,这个类是干什么的?后面再详讲


      在这里插入图片描述
    • Pom文件,翻了一下,发现spring-boot-starter-tomcat的作用范围被改成provided的,这个是什么意思?需要补充一下maven的基础知识,maven中三种classpath 编译,测试,运行
      • 1.compile:默认范围,编译测试运行都有效
      • 2.provided:在编译和测试时有效
      • 3.runtime:在测试和运行时有效
      • 4.test:只在测试时有效
      • 5.system:在编译和测试时有效,与本机系统关联,可移植性差
        修改scope为provided,也就是在运行时不起效,也就是打成war包时候,就不引入对应的Tomcat jar包,不使用嵌入式的Tomcat容器,使用外部的Tomcat容器
        在这里插入图片描述

    3、外部Servlet容器

    Springboot项目创建之后,其实就可以直接创建jsp应用了,然后从其自动生成的配置可以看出我们在创建war包时,是可以使用外部的Tomcat容器的,所以,我们引入一下外部Tomcat


    在这里插入图片描述 在这里插入图片描述

    部署时候,直接使用暴露的war即可,Application context可以写上也可以不管


    在这里插入图片描述

    直接创建一个jsp页面


    在这里插入图片描述
    进行页面跳转,写个Controller类:
    @Controller
    public class HelloController {
    
        @RequestMapping(value = {"/success"})
        public String toSuccess(){
            return "success";
        }
    }
    
    

    注意:还要向以前那样定义一下mvc的一下配置:

    spring.mvc.view.prefix=/WEB-INF/
    spring.mvc.view.suffix=.jsp
    
    

    ok,我之前博客SpringBoot源码学习系列之嵌入式Servlet容器
    已经比较详细地介绍了Springboot嵌入式Servlet容器的知识,所以本博客有必要对比一下嵌入式的Servlet容器和本博客介绍的外部Servlet容器的区别

    • 外部Servlet容器:maven打包是war形式,先启动Servlet容器,在创建ioc容器
    • 嵌入式Servlet容器:maven打包是jar形式,启动时候先创建ioc容器,再启动嵌入式的Servlet容器,比如Tomcat、undertow等等

    4、源码原理简介

    尚硅谷视频介绍过Servlet的规范,翻下文档,找到如图章节,这个章节介绍了创建war包项目时候会自动创建ServletInitializer类,主要介绍共享库和运行时插件,里面介绍了ServletContainerInitializer,也就是Servlet容器的一个初始化类的使用,启动时候会通过配置在META-INF/services的文件找对应类,通过@HandlesTypes注解在启动时候将需要的类引进来


    全局搜索,在Spring-web项目找到对应配置


    在这里插入图片描述
    @HandlesTypes(WebApplicationInitializer.class)//SpringServletContainerInitializer 容器类启动时候会一起创建WebApplicationInitializer类
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
    
        @Override
        public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
                throws ServletException {
    
            List<WebApplicationInitializer> initializers = new LinkedList<>();
    
            if (webAppInitializerClasses != null) {
                for (Class<?> waiClass : webAppInitializerClasses) {
                    // Be defensive: Some servlet containers provide us with invalid classes,
                    // no matter what @HandlesTypes says...
                    //检验WebApplicationInitializer不是一个接口、抽象类就进行实例
                    if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                            WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                        try {
                            initializers.add((WebApplicationInitializer)
                                    ReflectionUtils.accessibleConstructor(waiClass).newInstance());
                        }
                        catch (Throwable ex) {
                            throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                        }
                    }
                }
            }
    
            if (initializers.isEmpty()) {
                servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
                return;
            }
    
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            //遍历获取到的WebApplicationInitializer 类,调用其对应的onStartup方法
            for (WebApplicationInitializer initializer : initializers) {
                initializer.onStartup(servletContext);
            }
        }
    
    }
    
    

    WebApplicationInitializer 其实就是一个接口类,所以打开其实现类,可以看到SpringBootServletInitializer类


    在这里插入图片描述

    SpringBootServletInitializer类implements WebApplicationInitializer接口,所以在Servlet容器启动时候也会被创建,同时执行onStartup方法,如图,找关键点:

    在这里插入图片描述

    createRootApplicationContext方法是创建根据的容器,看看源码:

    protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
            //创建SpringApplication的构建器
            SpringApplicationBuilder builder = createSpringApplicationBuilder();
            builder.main(getClass());//设置main方法
            ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
            if (parent != null) {
                this.logger.info("Root context already created (using as parent).");
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
                //构建器初始化
                builder.initializers(new ParentContextApplicationContextInitializer(parent));
            }
            builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
            builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
            //关键点,调用configure方法
            builder = configure(builder);
            builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
            SpringApplication application = builder.build();
            if (application.getAllSources().isEmpty()
                    && MergedAnnotations.from(getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) {
                application.addPrimarySources(Collections.singleton(getClass()));
            }
            Assert.state(!application.getAllSources().isEmpty(),
                    "No SpringApplication sources have been defined. Either override the "
                            + "configure method or add an @Configuration annotation");
            // Ensure error pages are registered
            if (this.registerErrorPageFilter) {
                application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
            }
            //启动Application类
            return run(application);
        }
    

    从这个源码找到关键点configure方法,继续跟一下这个方法,基类的方法很简单,只是返回构造器

    在这里插入图片描述
    所以,在idea里ctrl+alt+B打开其实现方法,如图看到关键点,这个不就是自动创建的ServletInitializer类?而且重写了configure方法,而且将Springboot的Application类传给构造器类,所以跟到这里就明白了,为什么Servlet容器启动时候就会触发Springboot的ioc容器创建
    在这里插入图片描述

    代码例子下载:github下载链接

    相关文章

      网友评论

        本文标题:SpringBoot系列之集成jsp模板引擎

        本文链接:https://www.haomeiwen.com/subject/bbzmgctx.html