如果你想开发一个应用(1-3)

前一张的结尾,我想你已经发现了现在这个应用就是个骗子,根本就不是一个todos的应用,连个列表都没有,这一章就着重解决这个问题,在解决之前,先要明白几个概念:

Jsp的四种作用域

作用域顾名思义,就是一个变量能够起作用的区域,具体到jsp中,扣除自定义变量之外,共存在四种作用域,分别为:

  1. pageContext 即页面级,只存在本页面中,刷新后就消失。
  2. request 请求级,存在一样请求/响应周期
  3. session 会话级,存在一个用户完整的会话周期,可以暂时简单的理解为从用户访问站点开始,到关闭浏览器为止。
  4. application 应用级,即从应用启动到应用停止,比如tomcat重启,服务器重启都在这个范围。

page和request的区别在于,有些请求是跨页面的,比如通过forward的转发的页面,关于forward转发的内容,后面的章节会详细介绍。

看了四种作用域的介绍,因为列表是随时打开随时看的,那么很容易就可以想到,将所有的todo项存在于application中是最适合的(此时不考虑持久化技术),那么问题来了,jsp的application内置对象该如何使用呢?

这点,其实这四个内置变量的使用方式都是一致的,通过getAttribute(name:String)来获取在职,通过setAttribute(name:String,val:Object)来设置值。

存储todos

继续上一章的程序,暂时还是采取最简单的方法,仅仅是一个字符串的列表,将它通过一个List存入application中,代码如下:

<%
    request.setCharacterEncoding("utf-8");
    List<String> todos=(List<String>) application.getAttribute("todos");
    if(todos==null){
        todos=new ArrayList<>();
        application.setAttribute("todos",todos);
    }
    if(request.getParameter("todo")!=null){
        todos.add(request.getParameter("todo"));
    }
%>

这里首先从application中获取一个List,然后判断他是否存在,如果不存在则创建一个新的ArrayList,并将其放入application中,由于list为引用类型,所以即使没用重新setAttribute,在list没有重新初始化的情况下,对list的操作也会如实的反映到application中.

这里有一个java很重要的基础知识点,即值类型与引用类型,对于这点一定要非常清晰,否则会在编码中产生非常可怕的影响。

显示todos

现在todo项已经存在了一个list中,下面要做的就是输出它(整个代码块):

<%
    request.setCharacterEncoding("utf-8");
    List<String> todos=(List<String>) application.getAttribute("todos");
    if(todos==null){
        todos=new ArrayList<>();
        application.setAttribute("todos",todos);
    }
    if(request.getParameter("todo")!=null){
        todos.add(request.getParameter("todo"));
    }

    out.print("<ul>");
    for (String item :todos) {
        out.print("<li>"+item+"</li>");
    }
    out.print("</ul>");
%>

查看一下效果:

image

不考虑美观的话,功能算是实现了,但是,这样好么?
当然不好,尤其是输出html标签这一点,想一下,如果把ul改成table怎么办?并且标签的开闭,很容易出错,在想想如果在todo的项目中增加一些超链接之类的,那么对于这种在代码中嵌入html的方法,几乎是一场噩梦,所以一个方法是把在代码中嵌入html改为在html中嵌入代码,修改后的代码如下:

<%
    request.setCharacterEncoding("utf-8");
    List<String> todos=(List<String>) application.getAttribute("todos");
    if(todos==null){
        todos=new ArrayList<>();
        application.setAttribute("todos",todos);
    }
    if(request.getParameter("todo")!=null){
        todos.add(request.getParameter("todo"));
    }
%>
    <ul>
    <%
    for (String item :todos) {
        %>
       <li><%=item%></li>
<%
    }
    %>
    </ul>

这个:(貌似也没清晰多少,但是这是一个简单的例子,如果html稍微复杂点就能看出优势来,退一步说,即使只考虑使用html的高亮功能,都值得用这种代码中嵌入html的方法,但这种方法的缺点也是相当明显的,那就是他即破坏了html的结构,又破坏了代码的结构,那么有没有其他一种更加优秀的方法呢,这时候就轮到jstl出厂了.

jstl

jstl实际上是一套封装好的taglib组件,使用jstl首先需要加载jstl的依赖项,我们可以再maven公共库中搜索jstl,将搜索结果假如pom.xml文件的dependencies节点内中:

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

加载后点击Import Changes 带进度条走完即表示加载完成。

然后,我使用默认模板创建的应用为2.3版本,其实现在默认即为3.*版本,即删除<!DOCTYPE声明节点即可,若需要强制声明版本,可修改web-app属性为:

<web-app  xmlns="http://xmlns.jcp.org/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
    http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">

我的web.xml:

<web-app>
  <display-name>jTodos</display-name>
</web-app>

呃 好简单

然后在页面声明taglib前缀:
<%@ taglib prefix=”c” uri=”http://java.sun.com/jsp/jstl/core"%>

然后修改输出代码为:

<%
    request.setCharacterEncoding("utf-8");
    List<String> todos=(List<String>) application.getAttribute("todos");
    if(todos==null){
        todos=new ArrayList<>();
        application.setAttribute("todos",todos);
    }
    if(request.getParameter("todo")!=null){
        todos.add(request.getParameter("todo"));
    }
%>
    <ul>
        <c:forEach var="item" items="${todos}">
         <li>${item}</li>
        </c:forEach>
    </ul>

是不是清爽多了?运行看一下页面,和之前的一样,现在看页面的逻辑基本比较清晰了,代码和html互不干扰,同时,我们还可以为这个list假设一个编号:

 <ul>
    <c:forEach var="item" varStatus="status" items="${todos}">
     <li>${status.index+1}.${item}</li>
    </c:forEach>
</ul>

Ok很完美,这块暂时先告一段落,当然,jstl还有好多用法,以后再慢慢探索。

感谢您的阅读

image