美文网首页安全技术JavaWeb
【JinJava】一个Java使用的Jinja模板

【JinJava】一个Java使用的Jinja模板

作者: crossous | 来源:发表于2018-09-29 14:01 被阅读236次
    jinjava logo

    一、前言

      从我过去的文章可以看出,我更喜欢使用Python,但作为一个一半技术在Web上的程序员,JavaWeb也是无法全部逃过的。
      学过JSP、Servlet,看得懂代码,但自己写很慢,需要查不少资料,同时无比想念写flask时用的Jinja2模板,于是上网搜Java有没有封装Jinja,百度没查到,但Google立刻就搜到了。
    Jinjava github
    Jinjava doc
      不得不佩服这起名的巧妙,Jinja与Java拼接成为了Jinjava,话不多说,让我们具体使用一下吧。

    二、使用

      Jinja模板是通过模板语言,将变量代入模板生成文本,用于生成HTML是最常用的操作,Python Flask中的render_template函数就是将模板与变量合成生成Response对象。
      在Servlet中,Servlet类并非是返回Response对象,而是将通过改变参数传递进来的Response对象改变返回响应结果。
      在此之前,让我们先准备一个Maven+Servlet项目,可以参考我之前的文章【JavaWeb】IDEA创建Maven项目,配置Web项目
      然后在pom文件中引入Jinjava,将下面的依赖放在project>dependencies标签下,其中,com.hubspot.jinjava是本体,后面两个是jinjava用于记录日志的依赖,最后的com.google.guava是google的java开源库,里面有很多实用的方法。

    <dependency>
      <groupId>com.hubspot.jinjava</groupId>
      <artifactId>jinjava</artifactId>
      <version>2.4.8</version>
    </dependency>
    
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.7</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-jdk14</artifactId>
      <version>1.7.8</version>
    </dependency>
    
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>18.0</version>
    </dependency>
    

      根据我上个文章配完的在com.demo目录下新建一个类,如果没看过上个文章也没关系,在java目录下新建一个包,我命名为com.demo,并在下面新建两个servlet类,分别命名为HelloWorld和JinjavaHello

    项目结构
      webapp>source>jsp下的jsppage.jsp是测试用的,可以不用新建
      打出HelloWorld页面基础,是一个普通的Servlet类,在HelloWorld.java中打出下面的页面:
    package com.demo;
    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    
    
    public class HelloWorld extends javax.servlet.http.HttpServlet {
        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
            response.setContentType("text/html");
            response.getWriter().print("Servlet project create is success!");
        }
    }
    

      此时可以先配置web.xml文件测试,我先继续下一步,编辑JinJavaHello.java,同样先打出最简单的框架:

    import com.hubspot.jinjava.*;
    
    // guava工具类
    import com.google.common.collect.Maps; // 新建map工具(类似python中的dict)
    import com.google.common.io.Resources; //读取文件工具 
    import com.google.common.base.Charsets; //字符集工具
    import com.google.common.collect.ImmutableMap; //初始化map工具
    import com.google.common.collect.ImmutableList; //初始化list工具
    
    import java.util.*; // java标准库中的map
    
    //servlet类
    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    
    public class JinJavaHello extends javax.servlet.http.HttpServlet {
        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    
        }
    }
    

      然后我们去一个编写模板(命名为person.html,并放到Resources目录下):

    <!DOCTYPE html>
    <html lang="zh">
    <head>
        <meta charset="UTF-8">
        <title>{{ name }}的个人页面</title>
    </head>
    <body>
    <b>你好,{{ name }}</b><br>
    <div>您的倒霉同学们又来看你了
        <ul>
            <li>{{201601}}:{{students.get("201601")}}</li>
            <li>{{201602}}:{{students.get("201602")}}</li>
            <li>{{201603}}:{{students.get("201603")}}</li>
        </ul>
    </div>
    <div>
        您还有太多的任务没做
        <ul>
            {% for task in tasks %}
                {% if task != '炼金术'%}
                    <li>{{ task if task != '丢人' else task+'!'}}</li>
                {% endif %}
            {% endfor %}
            <li>...</li>
        </ul>
        <a href="{{url_for('HelloWorld')}}">转去首页</a>
    </div>
    </body>
    <script>
    </script>
    </html>
    

      这里我们希望像flask的render_template方法一样,通过这个模板,传递三个参数:name是用户的名称;students是一个字典表(dict),结构:key是学号字符串,value是姓名字符串;tasks是任务列表,结构类似python中的列表,如果是在python flask中,我们会这样编写:

    @route('/person')
    def Person():
    #    name = session.get('name')
        name = '黑暗大法师'
        students = {'201601' : 王大锤, '201602' : 李铁华, '201603' : '崔大建'}
        tasks = ['炼金术', '魔造学', '圣光术', '冲锋', '丢人']
        return render_template('person.html', name=name, students=students, tasks=tasks)  
    
    @route('/hello')
    def HelloWorld():
        return 'Servlet project create is success!'
    
    然后在运行后显示出:

      在点击链接后我们可以跳往首页,那么在java中如何编写呢?让我们先构造java中的list与dict(map)类型:

    Map<String, String> students = ImmutableMap.<String, String>builder().
            put("201601", "王大锤").
            put("201602", "李铁华").
            put("201603", "崔大建").build();
    List<String> tasks = ImmutableList.<String>builder()
            .add("炼金术")
            .add("魔造学")
            .add("圣光术")
            .add("冲锋")
            .add("丢人").build();
    

    在python中,多余参数都会变为**kwargs,类型为字典,key和value都是可以获取的,而在java中,这个kwargs就要我们自己去构建了,下面程序中的context就相当于kwargs:

    map<String, Object> context = Maps.newHashMap();
    context.put("name", "黑暗大法师");
    context.put("students", students);
    context.put("tasks", tasks);
    

      构造jinjava对象,读取资源,并呈现页面:

    Jinjava jinjava = new Jinjava();
    String template = Resources.toString(Resources.getResource("person.html"), Charsets.UTF_8);
    String renderedTemplate = jinjava.render(template, context);
    

      可以看出,得到的呈现结果是String类型,实际上,flask也是对jinja2模板做出了封装才能使得返回结果自动为response,因此在java中我们操作response对象就可改变返回结果,不过再此之前还要改变下编码格式:

    response.setContentType("text/html;charset=utf-8");
    response.setHeader("Content-type", "text/html;charset=utf-8");
    response.setCharacterEncoding("utf-8");
    
    response.getWriter().print(renderedTemplate);
    

      是时候测试一下了,更改webapp>WEB-INFO下的web.xml:

    <web-app>
      <display-name>Archetype Created Web Application</display-name>
        <servlet>
            <servlet-name>HelloWorld</servlet-name>
            <servlet-class>com.demo.HelloWorld</servlet-class>
        </servlet>
        <servlet>
            <servlet-name>jsppage</servlet-name>
            <jsp-file>/source/jsp/jsppage.jsp</jsp-file>
        </servlet>
        <servlet>
            <servlet-name>jinjavahello</servlet-name>
            <servlet-class>com.demo.JinJavaHello</servlet-class>
        </servlet>
    
    
        <servlet-mapping>
            <servlet-name>HelloWorld</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>jsppage</servlet-name>
            <url-pattern>/giligili</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>jinjavahello</servlet-name>
            <url-pattern>/hello2</url-pattern>
        </servlet-mapping>
    </web-app>
    

      同样,jsppage的映射是用于测试的,可以不用加
      我们运行下Tomcat服务器,并访问:http://localhost:8080/person,结果却并非我们想要的:

      
    500意思是服务器的程序运行出错,信息为:不能解析方法:url_for
      想想也是,在flask中,我们用url_for的目的是:假如我们有一个需求,想要改变访问的路径,但网页不需要改变,如果路径是写死的,那么之前所有网页的路径都需要改变,但是方法的名字不会改变。这时我们用方法名作为终结点endpoint(蓝图注册时可自定义),将终结点传入url_for,以及一系列参数,自动生成相关路径。但是java中的“终结点”是什么呢?Servlet类名吗?如何通过类名获取路径(pattern)?
      或许Servlet有现成方法,但我暂时只找到一个:
    ServletContext servletContext = getServletContext();
    Map<String, ? extends ServletRegistration> registrations =
            servletContext.getServletRegistrations();
    
    String path = new ArrayList<String>(registrations.get("HelloWorld").getMappings()).get(0);
    

      通过上面的方法,获取注册信息[ServletContextAPI],其中registrations的键(key)是我们在web.xml中注册的servlet-name,值(value)是org.apache.catalina.core.ApplicationServletRegistration对象,通过操控这个对象可以获取注册信息,例如:getMapping就能获取路径,getClassName返回类名的字符串。
      上面的将getMappings的结果传递到列表中,是因为getMapping返回的是HashSet类型,我想要获取到第一个路径,就要先转换为list并得到第一个值,结果就是path;然后我们将这个path传递给context:

    context.put("HelloWorldPath", path);
    

    并将jinja模板相应地点改为:

    <a href="{{HelloWorldPath}}">转去首页</a>
    

      重启Tomcat服务器,并访问http://localhost:8080/person,就会发现页面正常呈现,并且跳转链接也可以做到了。

    三、总结

      此时我们已经完成了链接获取的功能,但还不能完全代替url_for,以及一些地址栏规则,我们可以封装一些函数,用正则替换来实现这个功能。
      或许jinjava也集成了这些功能,只是我还没找到。
      试验了一些东西,发现jinjava对jinja的还原还是不错的,例如extend base页面,单引号字符串,if else三元表达式、过滤器等等。
      更多的功能期待各位自己去挖掘。

    相关文章

      网友评论

        本文标题:【JinJava】一个Java使用的Jinja模板

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