Java中常用的模板技术

在进行web开发时,对数据进行展示是非常重要的一个环节。在基于Java开发的网站中,JSP技术是最为常见的视图展示技术。由于JSP技术是官方推荐的视图展示技术,文档相对比较丰富,所以本文不会对JSP技术进行介绍。

虽然JSP是官方推荐的视图展示技术,但是JSP依然存在一些不太尽如人意的缺点。比如,JSP中可以直接写Java代码,这使得业务逻辑和前端展示没有完全分开,这样在后期维护时将会非常麻烦。而且,如果想要在JSP中完全不使用Java代码,开发人员需要进行自定义标签的编写,这就无形中增加了开发人员的工作压力。

本文主要介绍常用的两种模板技术——即velocityXSLT技术。velocity使用上跟JSP具有很大的相似性,但是velocity中不允许出现任何Java代码,实现了界面和Java代码真正意义上的分离。velocity和JSP,FreeMarker并称为三大视图展示技术。XSLT主要是用来对xml进行展示的一种技术,常见的比如将xml转换为html进行展示,这种技术在大量需要对xml进行展示的项目中使用广泛。

Velocity

在使用velocity模板引擎前,需要首先添加如下Maven依赖:

<dependency>  
      <groupId>org.apache.velocity</groupId>
      <artifactId>velocity</artifactId>
      <version>1.7</version>
</dependency>  

Velocity基础用法

velocity中重要的有三个概念,一个是模板引擎,即VelocityEngine,一个是模板上下文,即VelocityContext,还有一个是模板本身,是以.vm为后缀的文件。VelocityEngine负责加载模板文件,并对其进行翻译;模板上下文中可以存储一些数据,然后在模板文件中获取;模板文件主要是为最终的展示效果提供一个模板。下面的代码是最为基础的velocity的使用。

public class HelloVelocity {

    public static void main(String[] args) {

        VelocityEngine velocityEngine = new VelocityEngine();
        velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
        velocityEngine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
        velocityEngine.init();

        VelocityContext context = new VelocityContext();
        Map<String, String> map = new HashMap<String, String>();
        map.put("name", "InsaneCoder");
        map.put("age", "25");
        map.put("sport", "football");
        context.put("map", map);
        List<String> list = new ArrayList<String>();
        list.add("Cristiano");
        list.add("Bale");
        list.add("Ramos");
        context.put("list", list);
        StringWriter sw = new StringWriter();
        velocityEngine.mergeTemplate("hello.vm", "utf-8", context, sw);
        System.out.println(sw.toString());
    }
}

上述代码首先初始化模板引擎,然后初始化模板上下文,并在模板上下文中放入一些数据,最后利用模板引擎指定模板文件得到享应用的字符串。下面是上述的hello.vm文件,注意一个#符号是引用宏定义,连续两个#符号才是注释

## declare a variable
#set ($greet = "Hello")

## declare a macro
#macro(sayHello $name)
    Hello $name!
#end

$greet $map.name! You are $map.age years old and your favorite sport is $map.sport.
#foreach($i in $list)
    $i
#end
#sayHello("InsaneCoder")

velocity模板文件中通过$引用变量,这个变量可以在文件中定义,如上述的#set($greet="Hello"),也可以从模板上下文中获取,如上述的$name等。velocity中有许多的内置宏定义,比如上述的#foreach#set等,具体的velocity教程可参考这篇文章。当然,我们也可以自定义宏,就像定义一个函数一样,如上述的#macro(sayHello $name)。上述代码的运行结果如下:

Hello InsaneCoder! You are 25 years old and your favorite sport is football.  
    Cristiano
    Bale
    Ramos
    Hello InsaneCoder!

在Web开发中使用Velocity

如果想在Web中使用velocity模板,那必须要继承Apache官方提供的VelocityViewServlet,位于velocity-tools包中,可以通过添加如下maven依赖获得:

<dependency>  
      <groupId>org.apache.velocity</groupId>
      <artifactId>velocity-tools</artifactId>
      <version>2.0</version>
</dependency>  

Java代码如下:

public class IndexVelocityServlet extends VelocityViewServlet {

    @Override
    protected Template handleRequest(HttpServletRequest request, HttpServletResponse response, Context ctx) {

        String root = getServletContext().getRealPath("/");
        JAXBContext context = null;
        Person person = null;
        try {
            context = JAXBContext.newInstance(Person.class);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            person = (Person) unmarshaller.unmarshal(new FileReader(root + "WEB-INF/classes/test.xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        ctx.put("person", person);
        request.setAttribute("title", "FirstVelocityPage");

        return getTemplate("/WEB-INF/index.vm");
    }
}

从代码中不难发现,数据不仅仅存入了模板上下文中(ctx.put("person", person)),也存入了request的属性中(request.setAttribute("title", "FirstVelocityPage"))。在velocity模板文件中通过$符号获取变量时,跟JSP中EL表达式获取变量的方式类似,会按照context->request->session->application这个顺序进行变量获取。此外,这个例子的数据是从xml中获取的,xml文件在讲解XSLT小节中会给出。关于xml和javabean之间的转换后面我可能会单独写篇文章。下面是模板文件:

#set($greet = "Hello")
<!doctype html>  
<html>  
<head>  
    <title>$title</title>
</head>  
<body>  
    <h3>$greet $person.name!</h3>
    <div>You are $person.age years old and your favorite sport is $person.hobby.sport.</div>
    <div>Your favorite football stars are as follows:</div>
    <ul>
    #foreach($item in $person.favoriteStars)
        <li>$item.name $item.age $item.nationality</li>
    #end
    </ul>
</body>  
</html>  

最后启动服务,从浏览器中访问即可得到预期结果。

XSLT

细心地同学可能已经发现,上述在Web端采用velocity进行数据展示时,实际上经历了xml->javabean->velocity的过程,那么有没有一种方法直接对xml文件进行展示呢?这就要用到XSLT技术了。XSLT出现的目的就是对xml数据进行更加方便的展示,其全称为Extensible Stylesheet Language Transformations。利用XSLT对xml进行展示涉及到XPath。下面首先给出xml文件如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
<person>  
    <name>InsaneCoder</name>
    <age>25</age>
    <hobby>
        <art>music</art>
        <sport>football</sport>
    </hobby>
    <favorite-stars>
        <star>
            <age>31</age>
            <name>Cristiano</name>
            <nationality>Portugal</nationality>
        </star>
        <star>
            <age>28</age>
            <name>Bale</name>
            <nationality>Wales</nationality>
        </star>
        <star>
            <age>29</age>
            <name>Ramos</name>
            <nationality>Spain</nationality>
        </star>
    </favorite-stars>
</person>  

然后给出相应的xslt文件如下:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/person">
        <html>
            <head>
                <title>Test XSLT</title>
            </head>
            <body>
                <h3>Hello <xsl:value-of select="name"/>!</h3>
                <div>You are <xsl:value-of select="age"/> years old and your favorite sport is <xsl:value-of select="hobby/sport"/></div>
                <div>Your favorite football stars are as follows:</div>
                <ul>
                    <xsl:for-each select="favorite-stars/star">
                        <li><xsl:value-of select="name"/>-<xsl:value-of select="age"/>-<xsl:value-of select="nationality"/></li>
                    </xsl:for-each>
                </ul>
            </body>
        </html>
    </xsl:template>

</xsl:stylesheet>  

有了这两个文件就可以编写相应的java文件对xml数据进行展示了,Java文件如下:

public class XSLTServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String root = getServletContext().getRealPath("/");
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        StreamSource xslStreamSource = new StreamSource(new FileReader(root + "WEB-INF/classes/test.xsl"));
        StreamSource xmlStreamSource = new StreamSource(new FileReader(root + "WEB-INF/classes/test.xml"));
        try {
            OutputStream output = response.getOutputStream();
            Transformer transformer = transformerFactory.newTransformer(xslStreamSource);
            transformer.transform(xmlStreamSource, new StreamResult(output));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这个转换过程其实非常简单,就是读取xsl文件,然后对xml文件进行转换,然后转换的结果写入StreamResult中。上述步骤完成后,启动服务,从浏览器访问便可以发现这种展示方式跟上面利用velocity进行展示的结果如出一辙。

上述给出了Velocity和XSLT两种模板技术的基本使用方法,更加高级的用法可以参考相应文档。最后附上源码下载地址

参考

Velocity教程

XSLT Introduction

Shaohang Zhao

Read more posts by this author.