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的值 -->
网友评论