您正在查看: 前端开发 分类下的文章

PhosphoPrediction项目总结-前端

Overview

PhosphoPrediction这个项目是我们两个开发的第一个项目,整体进度比较快,但是因为是第一次,很多小细节花了不少的时间,当然,每一次细节的修改都有新知识的补充。
这里我主要记录下前端的一些知识点以备后面项目使用。

1. 长字符串自动换行的问题

在结果页面中,我们不只在页面上显示了预测结果,还将原始序列,Native Disorder等信息显示在了页面,由于这些序列特别长,所以在显示的时候就会出现一些问题,比如会显示为一个不换行的序列,导致页面出现了横向滚动条,非常不美观。
我们希望这个长序列可以在超出容器大小的时候自动换行,而不是把容器撑大。
解决方法:
给盛放长字符串内容的容器(可以是<div>或者<td>等)添加下面的样式:

word-wrap : break-word;
word-break: break-all;

这两个属性就是为了处理长字符串自动换行而存在的,break-word表示一个长句子换行时,在两个单词之间出现换行,而不会把一个单词断开换行,比如有一个句子I am a student from China!,换行的时候会变成

I am a
student from
China!

或者

I am a
student
from
China!

但是,不会出现下面的换行

I am
a stud
ent from
China!

也就是说,word-wrap : break-word;会保持单词的完整性,让每个单词都在同一行,然后产生换行。通常情形下,这样做是好的选择,美观又不影响阅读。但是,如果一个单词特别长呢,比如一个100个字母的单词出现了,而这个容器宽度只能容纳50个字母,那应该怎么断行?依然不会断行,这个长单词就会把容器撑大,让它的宽度变成100个字母的长度。
很不幸,我们处理的序列就是这样的情况,序列长度通常有几百个字母甚至更多,而且中间没有空格。
这时候我们需要word-break: break-all;,从字面意思就可以看出,强制一切换行,超出容器宽度,就自动换行。例子就不举了,看一下服务器的结果页面就可以看到效果。

2.关于margin和padding

这两个属性对于页面布局的细节调整非常的重要,比如说页面的页边距,容器内部内容跟容器的边距等等。
用一幅图就可以直观地表示:

margin-padding.jpg

margin就是一个容器的外边距,是这个容器离包含它的容器的距离;而padding就是容器的内边距,就是容器的内容距这个容器的距离。border就是这个容器的边,图上画的比较大就是为了突出一点显示而已。

3.<div>居中

通常,为了美观,我们不会将页面的内容以100%的宽度铺在浏览器中,而是在左右各留出一段空白,在中间显示内容。
我们可以在<body>标签中,使用一个<div>,为这个div添加下面的样式:

width: 80%;
height: 100%; 
margin-left:auto;
margin-right: auto;

可以看出,我们将这个<div>宽度设置成上层容器(这里就是<body>)的80%,这样<body><div>中间就有20%的空白。 我们将<div>的外边距设置成auto,浏览器会自动设置,让<div><body>中居中,也就是在左右两边各留10%的空白。
注意,这里的margin一定不要使用具体的数值,原因是在一个屏幕上可能留100px的左边空白就可以居中,而在更大的屏幕上100px可能不能让内容在屏幕的正中央。

IE6兼容PNG透明背景记录

PNG相比GIF有更好的显示质量。
但是在IE6下面,透明背景无法被兼容。

下面记录下具体兼容的一种方法:

图片img采用div加背景图片的方式添加,在html文件中添加css代码:

#logo {
width: 300px;
height: 300px;
background:url(img/logo.png) no-repeat;

/*以下为IE6设置PNG透明代码*/
_background:none;
_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src="img/logo.png");
}

这里需要注意,后面两行IE6的hack只有直接写在HTML文件中,才有效果。

关于前端使用SiteMesh的一些介绍

Overview

在网站开发的过程中,通常一个网站会有一个整体的风格,页面都有很多共同的菜单,横栏的底部信息。以前我们会采用include标签在每个jsp页面中来不断的包含各种header, stylesheet, scripts and footer,现在,在sitemesh的帮助下,我们不必再使用这种方式来保持风格统一了。

关于SiteMesh概述

sitemesh的设计思想是装饰者(decorator)设计模式。

关于装饰者(decorator)设计模式

尝试着简短的几句话介绍清楚,发现很难..列一个官方的定义:

    The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. (装饰者模式可以动态地给一个对象增加其他职责。就扩展对象功能来说,装饰者模式比生成子类更为灵活。)

详细的关于装饰者设计模式的资料,推荐下面两篇文章:
1.http://blog.chinaunix.net/uid-20761674-id-304542.html
2.http://www.cnblogs.com/rush/archive/2011/05/08/Decorator_DesignPattern_NETFramework.html

SiteMesh

首先看一下SiteMesh的工作过程图:
sitemesh2.png

从宏观上来看上图,HeaderFooter都是在每个界面都是固定或者有细微变化的(但不会有结构上的变化),改变的只是中间的元素。这时候我们可以编写一个decorator页面(上图中的browser-theme.jsp),这样在编写实际的展示页面(左上角的Menu.jsp)时,只需要去展示实际上不同的界面,并指定它需要哪个decorator修饰,这样在返回结果时,实际上是decorator和实际的展示页面组合成的新页面,如上图中右下角的Menu.jsp界面。
看得出来,SiteMesh使得我们可以不必在每个界面都重复编写相同的模板性元素,而只需要专注于实际需要展示的内容。
而且,使用SiteMesh可以实现更大的灵活性,给界面指定不同的decorator,比如下图中,使用手机浏览器和网页浏览器访问Menu.jsp时,该页面是被不同的decorator修饰,最终返回不同的组合界面。
sitemesh1.png

如何使用SiteMesh

1.在WEB-INF/web.xml中添加以下filter的定义:

    <!-- SiteMesh -->
    <filter>
        <filter-name>sitemeshFilter</filter-name>
        <filter-class>
            com.opensymphony.sitemesh.webapp.SiteMeshFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>sitemeshFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

sitemesh老版本的filter是PageFilter。 目前这个Filter已经过时,使用新的SiteMeshFilter。

2.将所需要的sitemesh.jar copy至WEB-INF/lib,如果使用Maven。不需要手动管理jar包,直接在pom.xml中添加以下依赖:

    <dependency>
        <groupId>opensymphony</groupId>
        <artifactId>sitemesh</artifactId>
        <version>${sitemesh.version}</version>
        <scope>runtime</scope>
    </dependency>

其中,${sitemesh.version}是一个pom中自定义的版本号,我们使用的为2.4.2。

3.建立WEB-INF/decorators.xml描述各装饰器页面,可仿照sitemesh例子。 如下所示:

    <decorators defaultdir="/WEB-INF/views>
        <decorator name="default" page="layouts/default.jsp">
            <pattern>*</pattern>
        </decorator>
    </decorators>

这其中,我们使用了<decorator>标签来声明了一个叫default的装饰器,<pattern>子标签指定了该标签可以用来装饰所有的请求页面,这样,当客户端请求任何一个页面时,实际上都是由default装饰器修饰之后,返回一个组合界面。

既然需要default装饰器修饰,那么需要可以找到实际上这个装饰器页面default.jsp在哪里。这里注意decoratorsdefaultdir属性,该属性指定了所有装饰器的具体存放路径前缀,根据<decorators>defaultdir属性和<decorator>page属性,就可以准确定位到每一个decorator的装饰页地址。比如,上述default装饰器页面实际放在/WEB-INF/views/layouts/default.jsp中。

4.建立default.jsp界面:

<%@ page contentType="text/html; charset=GBK"%>
<%@ taglib uri="sitemesh-decorator" prefix="decorator" %>

<html>
  <head>
    <title>
        <decorator:title default="装饰器页面..." />     
    </title>
    <decorator:head />
  </head>
  <body>
    sitemesh的例子<hr>
    <decorator:body />
    <hr>chen56@msn.com
  </body>
</html>
  1. 注意<%@ taglib uri="sitemesh-decorator" prefix="decorator" %>这句代码,为了在后面的jsp界面中使用decorator前缀,如<decorator:head />,需要声明该prefix并指定uri以让编译器知道应该去哪里寻找decorator的定义文件,上述的代码指定了一个本地定义文件,sitemesh-decorator,因此需要手动将sitemesh包种的sitemesh-decorator.tldsitemesh-page.tld文件放到/WEB-INF/文件夹下。
    实际上,新版本的sitemesh中,可以不必这么做,下面结合我们自己的项目讲解的时候会再说明。

  2. 装饰器jsp里使用的前缀还是decorator,新版本的已经变成了sitemesh,并且不再需要手动添加sitemesh-decorator.tldsitemesh-page.tld文件到本地。后面一并解释。

5.建立一个的被装饰页面 /index.jsp(内容页面)

    <%@ page contentType="text/html; charset=GBK"%>
    <html>
      <head>
        <title>Agent Test</title>
      </head>
      <body>
        <p>本页只有一句,就是本句.</p>
      </body>
    </html>

6.当客户端请求/index.jsp界面时,首先会被web.xml中的sitemesh filter拦截,然后去查看有没有decorator可以匹配/index.jsp,结果发现default.jsp声明可以匹配一切界面。当返回/index.jsp的结果时,实际上会将这两个界面拼起来,最后返回给客户端。 如下所示:
sitemesh3.png

实际项目中使用到的SiteMesh

第1,2步与上文没有区别,因此下面直接从3开始讲解。
3.建立WEB-INF/decorators.xml描述各装饰器页面:

    decorators.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xml>
    <decorators defaultdir="/WEB-INF/views">

        <!-- 默认装饰页面, 在需要装饰的页面增加<meta name="decorator" content="default"/> -->
        <decorator name="default" page="layouts/default.jsp" />
        <decorator name="default_mb" page="mobile/layouts/default.jsp" />

        <!-- CMS基础主题装饰页面 -->
        <decorator name="cms_default_basic" page="modules/cms/front/themes/basic/layouts/default.jsp" />
        <decorator name="cms_default_zgxj_blue" page="modules/cms/front/themes/zgxj_blue/layouts/default.jsp" />

    </decorators>

看得出来,跟上面例子唯一的区别在于decorator不再显式声明它要修饰的页面,而是改由要被修饰的页面主动制定要被谁修饰。在第5步中介绍。

4.建立default.jsp界面:

    default.jsp:

    <%@ page contentType="text/html;charset=UTF-8"%>
    <%@ include file="/WEB-INF/views/include/taglib.jsp"%>
    <%@ taglib prefix="sitemesh" uri="http://www.opensymphony.com/sitemesh/decorator" %>
    <!DOCTYPE html>
    <html style="overflow-x:hidden;overflow-y:auto;">
        <head>
            <title><sitemesh:title/></title>
            <%@include file="/WEB-INF/views/include/head.jsp" %>
            <sitemesh:head/>
        </head>
        <body>
            <sitemesh:body/>
        </body>
    </html>

可以看到,跟上文的区别主要有两点,主要在<%@ taglib prefix="sitemesh" uri="http://www.opensymphony.com/sitemesh/decorator" %>这句代码的区别。

  1. 首先前缀不再使用decorator,而是使用sitemesh,uri也不再使用本地的定义文件,而是使用远程站点的uri。
  2. 顺理成章,下面都使用了sitemesh前缀,如<sitemesh:body/>

5.建立一个的被装饰页面 /sysIndex.jsp,地址为(src/main/webapp/WEB-INF/views/modules/sys/sysIndex.jsp),代码如下:

    sysIndex 片段:

    <%@ page contentType="text/html;charset=UTF-8" %>
    <%@ include file="/WEB-INF/views/include/taglib.jsp"%>
    <html>
        <head>
        <title>利安管家卡后台</title>
        ...//省略代码
            
        <meta name="decorator" content="default"/>
             
            ...//省略代码

        </head>
            
            ...//省略代码

这里面需要注意一句代码:
<meta name="decorator" content="default"/>显式指定了该页面需要被哪个decorator修饰。content 中指定的就是decorator的名字。
因此当请求该页面的时候,会在返回结果时自动找到default.jsp进行组装,最后返回完整的融合信息给客户端。

本文参考了:

  1. http://www.cnblogs.com/fangjian0423/p/3559270.html
  2. http://docs.huihoo.com/java/sitemesh/index.html

想要参考更多的话:

  1. 官方的文档(其实不是很全,缺失很多部分):
    http://wiki.sitemesh.org/wiki/display/sitemesh/Learn+-+Getting+Started+with+SiteMesh
  2. 一个使用springMVCSiteMesh结合的例子:
    https://fahadshaon.wordpress.com/2012/07/24/sitemesh-2-with-spring-mvc-3-multiple-layout-for-multiple-controller/

前台兼容性测试及fix

浏览器种类

目前,前端需要专门测试的浏览器包括:

  • IE6
  • IE7
  • IE8
  • IE9+
  • chrome
  • firefox
  • opera
  • 360浏览器(on xp/windows7)

其中,IE6/IE7/360+xp作为低端浏览器,专门做兼容fix。
其他浏览器可以看作modern浏览器,兼容性问题相对较少。

开发过程

开发,从modern浏览器中的chrome作为标准,第一波测试在基于chrome/firefox/opera的开发中完成。
第二波测试IE9+,第三波测试IE8-。

在第一波过程中,修改完善css,html标签,初始开发保证标签css没有问题。
在后续测试中,通过hack的方法,加入补丁,将其他浏览器上的效果调整到modern浏览器的效果。

css hack及其他fix方法

这里整理下http://www.douban.com/note/163291324/ 的内容

IE8兼容问题

直接通过添加
<metahttp-equivmetahttp-equiv="x-ua-compatible"content="IE=7"/>
将IE8的显示模式调解成IE7的,这样将IE8的兼容问题转化为IE7的兼容问题

IE6兼容问题

float浮动在IE6下双倍margin问题:
'_'只有IE6能够识别,比如:

_maring: 0;

Firefox问题

清除浮动
添加一个div,style为 clear:both;

其他辨识标签

更进一步,可以通过HTML判断浏览器,来编写针对特定浏览器的代码(from http://zxlyecf2.iteye.com/blog/2116974):

  1. <!--[if !IE]><!-->除IE外都可识别<!--<![endif]-->
  2. <!--[if IE]> 所有的IE可识别<![endif]-->
  3. <!--[if IE 6]> 仅IE6可识别<![endif]-->
  4. <!--[if lt IE 6]> IE6以及IE6以下版本可识别<![endif]-->
  5. <!--[if gte IE 6]> IE6以及IE6以上版本可识别<![endif]-->
  6. <!--[if IE 7]> 仅IE7可识别<![endif]-->
  7. <!--[if lt IE 7]> IE7以及IE7以下版本可识别<![endif]-->
  8. <!--[if gte IE 7]> IE7以及IE7以上版本可识别<![endif]-->

测试工具

这里主要介绍 modern.IE ,这是微软出的前台测试的平台。
该平台可分为两个部分:一个是用于检测常见代码问题的Web扫描工具,另一个是与BrowserStack合作的虚拟测试服务。

其中,BrowerStack是一个云端的测试服务,可以提供所有系统和浏览器的组合。

框架前端页面涉及到的标签以及数据传输

Overview

本文针对以JSP为主的前端界面,如何展示数据以及接收后台传输的数据做出了一点说明。
目前框架前端页面所使用到的标签,主要为三种:普通的JSP标签,JSP标准标签库(JSTL),标签库描述符。下面分别介绍这三种不同的形式,并以框架中的src/main/webapp/WEB-INF/views/modules/sys文件夹中的后台管理主页面sysIndex.jsp为例进行分析。

JSP标签

普通的JSP标签几乎是在HTML标签之外,添加了一些传输数据和控制显示的功能,在此不再做过多的解释。请参考以下网址详细了解:http://www.w3cschool.cc/jsp

JSP标准标签库(JSTL)

JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。
JSTL支持通用的、结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签。 除了这些,它还提供了一个框架来使用集成JSTL的自定义标签。
JSTL标签按照功能可以分为核心标签,格式化标签,SQL 标签,XML标签,JSTL函数。
根据我们的需求,这里只介绍一下核心标签,并省略了JSTL库的安装。

核心标签是最常用的JSTL标签。引用核心标签库的语法如下:

    <%@ taglib prefix="c" 
       uri="http://java.sun.com/jsp/jstl/core" %>

    
  • <c:out> 用于在JSP中显示数据,就像<%= ... >
  • <c:set> 用于保存数据
  • <c:remove> 用于删除数据
  • <c:catch> 用来处理产生错误的异常状况,并且将错误信息储存起来
  • <c:if> 与我们在一般程序中用的if一样
  • <c:choose> 本身只当做<c:when>和<c:otherwise>的父标签
  • <c:when> <c:choose>的子标签,用来判断条件是否成立
  • <c:otherwise> <c:choose>的子标签,接在<c:when>标签后,当<c:when>标签判断为false时被执行
  • <c:import> 检索一个绝对或相对 URL,然后将其内容暴露给页面
  • <c:forEach> 基础迭代标签,接受多种集合类型
  • <c:forTokens> 根据指定的分隔符来分隔内容并迭代输出
  • <c:param> 用来给包含或重定向的页面传递参数
  • <c:redirect> 重定向至一个新的URL.
  • <c:url> 使用可选的查询参数来创造一个URL

以<c:forEach>为例,对该标签详细说明:
<c:forEach>标签封装了Java中的for,while,do-while循环。它迭代一个集合中的对象。

属性

<c:forEach>标签有如下属性:

属性 | 描述

  • items 要被循环的信息
  • begin 开始的元素(0=第一个元素,1=第二个元素)
  • end 最后一个元素(0=第一个元素,1=第二个元素)
  • step 每一次迭代的步长
  • var 代表当前条目的变量名称
  • varStatus 代表循环状态的变量名称

该标签主要用来在JSP页面中循环显示一个列表型栏目,比如从后台取到一个用户list数据,使用此标签可以循环将用户list中的每一个用户信息显示在JSP页面。

<c:forEach>实例演示
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core"                 prefix="c" %>
    <html>
        <head>
            <title>c:forEach 标签实例</title>
        </head>
        <body>
            <c:forEach var="i" begin="1" end="5">
               Item <c:out value="${i}"/><p>
            </c:forEach>
        </body>
    </html>

结果显示如下:

    Item 1
    Item 2
    Item 3
    Item 4
    Item 5

关于所有这些标签的更详细信息,请参见:http://www.w3cschool.cc/jsp/jsp-jstl.html

标签库描述符

tld是taglib description 的缩写。

标签库描述文件,如要在JSP页面中实现JSP标签,必须首先定义实现标签的类,然后在标签库描述文件(TLD)中将写好的类映射成jsp标签,最后在JSP文件中使用定义好的标签,就可以生成动态的JSP内容。

下面结合sysIndex.jsp完整说明整个流程。

sysIndex.jsp代码片段:

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
<html>
<head>
...

<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
此代码将taglib.jsp中的内容完整引入到sysIndex.jsp中,先看一下taglib.jsp中的代码:

    <%@ taglib prefix="shiro" uri="/WEB-INF/tlds/shiros.tld" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    <%@ taglib prefix="fns" uri="/WEB-INF/tlds/fns.tld" %>
    <%@ taglib prefix="fnc" uri="/WEB-INF/tlds/fnc.tld" %>
    <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
    <c:set var="ctx" value="${pageContext.request.contextPath}${fns:getAdminPath()}"/>
    <c:set var="ctxStatic" value="${pageContext.request.contextPath}/static"/>

可见taglib.jsp主要是为了引入要再JSP中使用的名字前缀,这类名字前缀主要是分两类,一类是标准标签,如c,form,fmt,fn标签,他们的uri属性是一个网址,实质上这个网址上指向的是一个标准定义的tld文件,一类是自定义标签,包含第三方库的自定义标签shiro,自己定义的自定义标签fns,fnc,他们的uri指向的是本地自定义的tld文件。prefix属性是使用此标签时的标签名。

<%@ taglib prefix="fns" uri="/WEB-INF/tlds/fns.tld" %>为例,我们看一下fns.tld文件:

fns.tld 代码片段:

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee     http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
  version="2.0">

  <description>JSTL 1.1 functions library</description>
  <display-name>JSTL functions sys</display-name>
  <tlib-version>1.1</tlib-version>
  <short-name>fns</short-name>
  <uri>http://java.sun.com/jsp/jstl/functionss</uri>

  <!-- 通用设置 --> 
  <function>
    <description>获取管理路径</description>
    <name>getAdminPath</name>
    <function-class>
        com.thinkgem.jeesite.common.config.Global
    </function-class>
    <function-signature>j
        java.lang.String getAdminPath()        
    </function-signature>
    <example>${fns:getAdminPath()}</example>
  </function>
  ...//省略代码

该文件实际上是一个前后台连接的枢纽,<short-name>中的标签就是JSP中可以使用的标签前缀。<function>中定义的是所需要映射到后台实现类的具体方法,这部分下面会详细解释。

回到sysIndex.jsp看一段代码:

    sysIndex.jsp代码片段:
    
    ...//省略代码
    
     <div class="nav-collapse">
           <ul id="menu" class="nav">
             <c:set var="firstMenu" value="true"/>
             <c:forEach items="${fns:getMenuList()}" var="menu" varStatus="idxStatus">
                <c:if test="${menu.parent.id eq '1' && menu.isShow eq '1'}">
                    <li class="menu ${firstMenu ? ' active' : ''}"><a class="menu" href="${ctx}/sys/menu/tree?parentId=${menu.id}" target="menuFrame" >${menu.name}</a></li>
                    <c:if test="${firstMenu}">
                        <c:set var="firstMenuId" value="${menu.id}"/>
                    </c:if>
                    <c:set var="firstMenu" value="false"/>
                </c:if>
             </c:forEach>
             <shiro:hasPermission name="cms:site:select">
                <li class="dropdown">
                    <a class="dropdown-toggle" data-toggle="dropdown" href="#">${fnc:getSite(fnc:getCurrentSiteId()).name}<b class="caret"></b></a>
                    <ul class="dropdown-menu">
                    <c:forEach items="${fnc:getSiteList()}" var="site"><li><a href="${ctx}/cms/site/select?id=${site.id}&flag=1">${site.name}</a></li></c:forEach>
                    </ul>
                </li>
             </shiro:hasPermission>
           </ul>
           
             

            ...//省略代码
 
         </div><!--/.nav-collapse -->

<c:set var="firstMenu" value="true"/>
此标签使用了jstl,声明了一个变量firstMenu,值为value,其中c即由taglib.jsp<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>中声明。

<c:forEach items="${fns:getMenuList()}" var="menu" varStatus="idxStatus">
此标签定义了循环,用来显示从后台获取的菜单,其中,items存储了后台读取来的menu list,var中存储了每次遍历时,遍历到的菜单对象,用于显示在页面上。
其中fnstaglib.jsp<%@ taglib prefix="fns" uri="/WEB-INF/tlds/fns.tld" %>声明,并在fns.tld中定义,我们来看一下其中关于getMenuList()中的定义:

    fns.tld代码片段:

    ...//省略代码

    <function>
        <description>获取当前用户的菜单对象列表</description>
        <name>getMenuList</name>
        <function-class>
          com.thinkgem.jeesite.modules.sys.utils.UserUtils
        </function-class>
        <function-signature>
            java.util.List getMenuList()        
        </function-signature>
        <example>${fns:getMenuList()}</example>  
    </function>

    ...//省略代码

<function>: 对应于一个具体的函数,声明了JSP中可以使用的函数名,以及跟后台具体实现类的映射。具体如下:

  • <description>: 可以理解为函数的注释,用来说明函数的用途。
  • <name>: 前端JSP中可以使用的函数名,${fns:getMenuList()}中的getMenuList()就是这个名字。
  • <function-class>: 指定了一个具体的后台实现类,来真正实现该方法。
  • <function-signature>: 通常一个tld文件对应一个实现类,里面会有很多方法,函数签名唯一的映射到了后台类的具体某一个函数上。
  • <example>: 说明了JSP中如何使用该函数,给出了一个示范用法。

接下来我们看一下com/thinkgem/jeesite/modules/sys/utils/中的UserUtils.java文件:

    UserUtils.java代码片段:

    public class UserUtils extends BaseService {
    
    ... //代码省略

    private static MenuDao menuDao = SpringContextHolder.getBean(MenuDao.class);

    ... //代码省略

    public static List<Menu> getMenuList(){
        @SuppressWarnings("unchecked")
        List<Menu> menuList = (List<Menu>)getCache(CACHE_MENU_LIST);
        if (menuList == null){
            User user = getUser();
            if (user.isAdmin()){
                menuList = menuDao.findAllList();
            }else{
                menuList = menuDao.findByUserId(user.getId());
            }
            putCache(CACHE_MENU_LIST, menuList);
         }
        return menuList;
    }        

    ... //代码省略

    }   

可以看到,UserUtils.java中的 getMenuList()方法首先会检查是否缓存了该menu list,如果没有,调用了menuDAO来进行后台数据获取,然后存一份到缓存,并返回一个menu list给前台。具体的DAO层不再深入说明。

至此,整个前端取后台数据并展示到前台的流程就到这里了。