Servlet和JSP

Servlet

Servlet/JSP应用架构

servlet无法独立运行,必须运行在Servlet容器中。Servlet容器将用户的请求传递给Servlet应用,并将结果返回给用户。Tomcat和Jetty就是当前最流行的Servlet/JSP容器。


Servlet/JSP应用架构.png
Servlet是一个Java程序
@WebServlet(name = "WxsFirstServlet", urlPatterns = {"/myServlet"})
public class MyServlet implements Servlet {
    private transient ServletConfig servletConfig;

    //保存配置信息,如@WebServlet注解中的属性,仅调用一次
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        this.servletConfig = servletConfig;
    }

    //获得上述配置信息对象
    @Override
    public ServletConfig getServletConfig() {
        return servletConfig;
    }

    //容器会自动调用该方法处理请求,返回响应。
    //容器接到请求后,会自动创建ServletRequest和ServletResponse对象并传给service方法
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        String servletName = servletConfig.getServletName();
        servletResponse.setContentType("text/html");

        System.out.println(servletRequest.getParameter("id"));

        PrintWriter writer = servletResponse.getWriter();
        writer.print("<html><head></head><body>Hello from" + servletName + "</body></html>");
    }

    @Override
    public String getServletInfo() {
        return "MyServlet";
    }

    @Override
    public void destroy() {

    }
}

运行该程序,只需启动Tomcat并部署后,访问http://localhost:8080/项目名/MyServlet即可,此时,浏览器上将会显示我们返回的HTML页面

GenericServlet

上述实现Servlet的缺点是,有些方法没必要修改,但我们仍要重载。
GenericServlet实现了Servlet和ServletConfig接口,通过继承GenericServlet,只需实现Service方法即可实现Servlet

HttpServlet

主角!最常用!
HttpServlet有两个特性是GenericServlet所不具备的:

  • 不用覆盖Service方法,而是覆盖doGet、doPost、doHead等方法。HttpServlet中的Service方法会根据HTTP请求的Method调用响应的doXXX方法
  • 使用HttpServletRequest和HttpServletResponse,而不是ServletRequest和ServletResponse
处理HTML表单

当用户提交(submit)表单时,在表单元素中输入的值就会被当作请求参数发送到服务器。

HttpServlet&HTML表单示例

一开始HTML页面竟然是这么返回的。。。>_<
该Servlet有两个方法

  • 方法1,返回HTML,包含Form表单,用于填写信息。提交表单后,会调用第二个方法
  • 方法2,返回HTML,显示上面表单所填写的信息
@WebServlet(name="FormServlet", urlPatterns={"/form"})
public class FormServlet extends HttpServlet
{
    //这里是要返回给客户端一个包含Form表格的HTML,用于填写信息
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOExeption
    {
        response.setContentType("text/html");
        Printwriter writer = response.getWriter();
        writer.print("<html>");
        ...
        //<form>的action属性为默认,表示该表单会被提交给请求它时用的相同的URL,以便提交表格后调用下一个方法
        writer.println("<form method='post'>")
        //<table><tr><td><input>等组件
        writer.print("</html>");
    }

    //这里是要返回一个HTML,展示用户在上一个方法返回的HTML页面中填写的表格信息
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("<html>");
        //<table><tr><td><input>等组件
        ...
        writer.println("</html>");
    }
}
部署描述符

前面的例子都是利用@WebServlet配置Servlet应用程序,例如用一个路径映射到一个Servlet。本节介绍另一种方法,即部署描述符。部署描述符总是放在WEB-INF目录下,并且总是命名为web.xml

可将第一个例子中的@WebServlet去掉,编辑web.xml如下,可达到同样效果

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
           version="3.1">
    <servlet>
        <servlet-name>WxsFirstServlet</servlet-name>
        <servlet-class>MyServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>WxsFirstServlet</servlet-name>
        <url-pattern>/myServle3t</url-pattern>
    </servlet-mapping>

</web-app>

会话管理

HTTP是无状态的,默认下,Web服务器无法区分一个HTTP请求是否为第一次访问。
例如,一个Web邮件应用要求用户登陆才能查看邮件,因此,当用户输入用户名、密码登陆后,应用应能记住这些用户已经登陆,不应再次提示登陆。
本节将阐述4种不同的状态保持技术:URL重写、隐藏域、cookies和HTTPSession

URL重写

将必要的信息都记录在url中
仅适合与信息仅在少量页面间传递,形式如下
url?key-1=value-1&key-2=value-2&...&key-n=value-n

隐藏域

将必要的信息都记录在表单的隐藏域,表单提交时,隐藏域的值也同时提交到服务器端。
隐藏域技术仅当网页有表单时有效。相对于URL重写的优势在于没有字符数限制,但也不适合跨越多个界面形式如下
<form ...>
<input type='hidden' name='id' value='1' 其它我们要保存的值>
</form>

Cookies
  • 构造cookie:Cookie cookie = new Cookie(name, value);
  • 将cookie发送到浏览器:httpServletResponse.addCookie(cookie);
  • 服务端获取浏览器提交的cookie:HttpServletRequest接口的getCookies()返回Cookie数组。若要查询某个特定名称cookie,需要遍历cookie数组。

浏览器在访问同一Web服务器时,会将之前收到的cookie一并发送。我试过,浏览器中的cookie默认会保存很久,即使关闭浏览器也不会消除cookie。可控制cookies的有效时间,例如将maxAge设置为0实现消除cookie。
cookies的问题在于用户可以通过改变其浏览器设置来拒绝接受cookies

HttpSession

一个用户有且仅有一个HttpSesison对象
HttpSession对象在用户第一次访问网站的时候自动被创建

常用接口
  • getSession:获得当前Session对象
  • setAttribute:将键值(可以是Serializable的接口)放入HttpSession。
  • getAttribute:取值
  • getAttributeNames:返回所有保存在Session中的值
  • 还有一些设置Session过期的接口,若果Session没过期且容器和应用没有关闭,则一直存放在服务端内存中

注意,所有保存在Session中的值都存储在内存中,并且不会被发送到客户端。Servlet容器为每个HttpSession生成唯一标识发送给浏览器(开发人员无需介入),在后续的请求中,浏览器会将标识提交给服务端,这样服务器可以识别该请求是由哪个用户发起的

JavaServer Pages(JSP)

Servlet的缺点
  • 生成响应报文的方式十分烦琐,writer.println("<tr> <td cospan='4' style......")
  • 文本和HTML标记硬编码,即使表现层需要改变背景颜色,也需要重新编译

Servlet解决上述的问题,现代JavaWeb应用会同时使用JSP和Servlet

JSP概述

JSP页面本质上是一个Servlet,但更便于开发。JSP是Servlet的补充,并不是取代Servlet技术,倾向于表现层的Servlet可用Servlet来代替
JSP页面在JSP容器中运行,一个Servlet容器通常也是JSP容器,如Tomcat就是一个Servlet/JSP容器

当一个JSP页面第一次被请求时,Servlet/JSP容器主要做以下两件事情:

  • 将JSP页面转化为对应的实现了javax.servlet.Servlet子接口实现类,使得每一个页面都是一个Servlet(在Tomcat目录下找到转换后的Servlet后,会发现响应HTML仍然是通过out.write(....)的方式写的)
  • 如果转换成功,容器编译、装载、实例化该Servlet类,像正常Servlet一样执行生命周期操作

JSP页面不同于Servlet的一个方面是,前者不需要添加注解或在部署描述符中配置映射URL,在应用程序目录中的每一个JSP页面可以直接在浏览器中输入路径页面访问。如:http://localhost:8080/ProjectName/welcome.jsp。添加新的JSP界面后,无须重启Tomcat

JSP页面可以包含模版数据和语法元素

  • 语法元素:具有特殊意义的JSP转换符,如<%,%>分别代表Java代码的起止符号,<%%>块又被称作scriplet。
  • 模版数据:除去语法元素外的一切是模板数据。模板数据会原样发送给浏览器,如HTML标记和文字
隐式对象

Servlet中,通过service方法能够拿到:HttpServletRequest、HttpServletResponse;通过init能够访问ServletConfig;通过HttpServletRequest的getSession访问HttpSession

在JSP中,可以通过使用如下隐式对象来访问上述对象:
request、response、config、session
另外还有

  • application,类型为ServletContext
  • out,类型为JspWriter
  • page,类型为HttpjspPage
  • pageContext,类型为HttpJspPage
  • exception,类型为Throwable

示例,在JSP中实现从HttpServletRequest对象中获取username的值

<%String userName = request.getparameter("username"); %>
指令

指令是JSP语法元素的第一种类型,用于指示JSP转换器如何翻译JSP页面为Servlet。只有page和include最重要

  • page:可以导入包、设置out缓冲区大小、是否使用session等
  • include:将另一个JSP或静态HTML文件嵌入到当前JSP中
表达式

<%= xxx%>等价于<% out.print(xxx)%>

声明

<%! xxx%>可用与声明或重写函数,声明和重写的函数可以在JSP页面中使用

禁用脚本元素

推荐的实践是:在JSP页面中用EL访问服务器端对象且不写Java代码。因此,需要通过在部署描述符中的<jsp-property-group>定义scripting-invalid元素,来禁用脚本元素

动作

useBean

<jsp:useBean id="today" class="java.util.Date">
<%=today%>

该代码创建一个Java.util.Date实例,并赋值给名为today的脚本变量,然后在表达式中使用

setProperty和getProperty

<jsp:useBean id="today" class="java.util.Date">
<jsp:setProperty name="today" property="time" value="12:00">
<jsp:getProperty name="today" property="time">

forward
将当前页面转向其它资源

<jsp:forward page="jspf/login.jsp">
    <jsp:param name="text" value="please login">
</jsp:forward>
错误处理

当应用遇到未捕获的异常时,用户将看到一个精心设计的网页解释放生了什么。
errorHandler.jsp使用Page指令的isErrorPage属性,表示自己是个错误页面

<%page isErrorPage="true"%>

bug.jsp故意抛出异常, 并且使用errorPage指向错误处理的页面

<%@page errorPage="erroHandler.jsp"%>

表达式语言

EL的目的是设计成可以轻松地编写免脚本的JSP页面。也就是说,页面不实用任何JSP声明、表达式或者scriptlet

EL表达式
${expression},返回结果为String
${a+b}${c+d},如果a+b=8,c+d=10,则结果为810
${object["property"]}或${object.property}可访问object的属性

在JSP页面中,可以利用JSP脚本来访问JSP隐式对象。但是,在免脚本的JSP页面中,则不可能访问这些隐式对象。EL允许通过提供一组它自己的隐式对象来访问不同的对象。不详细说了

JSTL

JSTL标准标签库是一个定制标签库的集合,用来解决像遍历Map或集合、条件测试、XML处理,甚至数据库访问和数据操作等常见的问题
在JSP页面使用JSTL库,必须通过类似以下格式使用taglib指令:

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

<c:out value="${x}"/>          <!-- 输出X的值 -->
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容