一、前言
从我过去的文章可以看出,我更喜欢使用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三元表达式、过滤器等等。
更多的功能期待各位自己去挖掘。
网友评论