note about Servlet and JSP(一)

Head First Servlets and JSP笔记(一)

第一章 前言

GET和POST
GET是一个简单请求,总字符数是有限的,发送的数据会追加在url后面;GET是幂等的,不会修改服务器上的任何内容;
POST可以发送用户数据;POST不是幂等的,POST体中提交的数据可能用于不可逆转的事务;
如果表单没有指明"method=“POST”,会默认为HTTPGET请求;
TCP端口
端口是16位数据,标识服务器硬件上特定的软件程序;
从0~1023的端口号已经被保留,定制的服务器程序不要使用这些端口;
URL
统一资源定位符:如果URL对应一个GET请求,那么它可能包含一个可选的查询串;
JSP
JSP页面就像是一个HTML页面,可以把java和java有关的东西插入到该页面中;

第二章 高层概述:Web应用体系

Tomcat
Servlet没有main()方法,它们受控于另一个java应用——容器;
容器提供:通信支持、生命周期管理、多线程、声明方式实现安全、JSP支持;

部署描述文件DD
将Servlet部署到Web容器时,会创建一个简单的XML文档,称为部署描述文件(DD)
note about Servlet and JSP(一)
模型-视图-控制器(MVC)
MVC把业务逻辑从Servlet中抽出来,放在一个“模型”——一个可重用的java类中,模型是业务数据和出力该类数据的方法的组合;
J2EE
Tomcat只是一个Web容器,而不是完整的J2EE应用服务器(因为没有EJB容器);
note about Servlet and JSP(一)

第三章 MVC实战

BUGreport(1):
在编写第二版servlet时,通过javac编译BeerSelect.java出错,未找到对应目录下的文件;
Solution:
直接编译,手动部署目录;
BUGreport(2)
在重启tomcat之后,访问http://localhost:8080/Beer-v1/form.html网页下submit之后资源响应报错;
Solution
反复排查之后发现使用javac调用servlet-api.jar的路径应该是tomcat/lib路径下的包,而不是JDK/lib路径下的包,更改之后,响应结果正确;
note about Servlet and JSP(一)
(调用模型实例过程略)

第四章 作为Servlet:请求和响应

Servlet受容器的控制
note about Servlet and JSP(一)
与http有关的都在javax.servlet.http包中,其余的通用类接口都在javax.servlet包中;
容器运行多个线程来处理对一个servlet的多个请求;
init()在第一个service调用之前完成;
ServletConfig:用于向servlet传递部署时信息;用于访问ServletContext;参数在部署描述文件中描述;
ServletContext:用于访问web应用参数,相当于公告栏,应用的其他部分可以访问这些消息;

常见ServletRequest和HttpServletRequest的API:

// 客户的平台和浏览器信息
String client=request.GetHeader("User-Agent");
//与请求相关的cookie
Cookie cookies[]=request.getCookies();
//与客户相关的会话
HttpSession session=request.getSession();
//请求的HTTP方法
String themethod=request.getMethod();
//请求的输入流——只包含请求体而不包含首部
InputStream input=request.getInputStream();

大多数情况,使用响应只是为了向客户发回数据,会对响应调用两个方法:
setContentType()getWriter() 此后只需要I/O将HTML写至流;
内容类型是HTTP响应中必须有的一个HTTP响应首部,常指MIME(Multipurpose Internet Mail Extensions)类型;
ServletOutputStream 用于输出字节,PrintWriter 用于输出字符数据;

//样例
PrintWriter writer=response.getWriter();
writer.println("some text and html");
ServletOutputStream out=response.getOutputStream();
out.wirte(aByteArray);

PrintWriter有ServletOutputStream的引用,会把调用委托给ServletOutputStream;
setHeader()会覆盖现有的值;addHeader()会增加另一个值;
Servlet重定向让浏览器完成工作;

if(worksforme)
{//handle the request
}
else{
response.sendRedirect("new http location");
}

sendRedirect() 使用相对URLs的两种用法(取的是一个String,而不是一个url对象)——斜线开头与非斜线开头;

第五章 作为Web应用:属性和监听者

servlet初始化参数
在每个特定的servlet的<servlet>元素中,只对配置了<init-param>的相应servlet可用;
在DD文件中(web.xml)中:

<init-param>
	<param-name>adminEmail</param-name>
	<param-value>whatever@bullshit.com</param-value>
</init-param>

在servlet代码中:

out.println(getServletConfig().getInitParameter("adminEmail"));

容器建立servlet只初始化一次,读取DD,为ServletConfig创建名/值对;
上下文初始化参数
在<web-app>元素中,而不在具体的<servlet>元素内;针对整个web应用,而不是一个servlet;
在DD文件(web.xml)中

<context-param>
	<param-name>adminEmail</param-name>
	<param-value>whatever@bullshit.com</param-value>
<context-param>

在servlet代码中

out.println(getServletContext().getInitParameter("admminEmail"));

web应用初始化

  • 容器读DD,对应每个<context-param>创建名\值String对;
  • 容器创建ServletContext的一个新实例;
  • 容器为ServletContext提供上下文初始化参数各个名\值对的引用;
  • 在一个Web应用中部署的各个servlet和JSP都能访问同一个的ServletContext

常见

ServletContext方法
getInitParameter(String)
getAttribute(String)
setAttribute(String,Object)
removeAttribute(String)

ServletContextListener
Dog例子:

  • 监听者对象向ServletContextEvent对象请求应用ServletContext对象的一个引用
  • 监听者使用这个ServletContext引用得到"breed"的上下文初始化参数,这是一个String
  • 监听者使用这个String构造一个一个Dog对象
  • 监听者使用ServletContext引用在ServletContext中设置Dog属性
  • Web应用的测试servlet从ServletContext中得到Dog对象,并调用这个Dog的getBreed()方法

编写监听者类:

package com.example;
import javax.servlet.*;
public class MyServletContextListener implements SerletContextListener{
	public void contextInitialized(ServletContextEvent event){
		ServeltContext sc=event.getServletContext();
		String dogBreed=sc.getInitParameter("breed");
		Dog d=new Dog(dogBreed);
		sc.setAttribute("dog",d);
		}
		public void contextDestroyed(ServletContextEvent event){
		//nothing to do here

8个监听者:
note about Servlet and JSP(一)
属性不同于参数:
note about Servlet and JSP(一)
属性的三个作用域:上下文、请求、会话;
上下文作用域不是线程安全的;同步服务方法会防止同一个servlet中的其他线程访问上下文属性,但是不能阻止另外一个不同的servlet的访问;** 解决办法** 对上下文对象对象本身同步:

public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException,ServletException{
	response.setContentType("text/html");
	//etc.
	synchronized(getServletContext()){
	//对属性的操作
	}
}

对HttpSession同步来保护会话属性;
SingleThreadModel设计用来保护实例变量,确保servlet一次只处理一个请求;
只有请求属性和局部变量是线程安全的;

//code in doGet()
BeerExpert be=new BeerExpert();
ArrayList result=be.getBrands(c);
request.setAttribute("styles",result);
RequestDispatcher view=request.getRequestDispatcher("result.jsp");
view.forward(request,response);

让组件的其他部分接管请求使用RequestDispatcher;
RequestDispatcher只有forward()和include()两个方法取请求和响应对象为参数;
note about Servlet and JSP(一)
如果已经使用的响应(os.flush())则不能再使用请求,容器会抛出IlleagalStateException;

第六章 会话状态:会话管理

  • 编写servlet代码,将对象保存到一个会话对象中,以及从会话对象获取对象
  • 给定一个场景,描述访问会话对象使用的API,解释创建和撤销会话对象的机制
  • 使用会话的监听者,编写代码对会话的有关事件作出响应,包括向会话增加一个对象,以及会话对象从一个VM迁移到另一个VM
  • 给定一个场景,说明Web容器可以采用哪些会话管理机制,如何使用cookie来管理会话,如何使用URL重写管理会话

HttpSession对象可以保存跨同一个客户多个请求的会话状态;
HTTP协议使用的是无状态连接,对容器而言,每个请求都来自于新的客户——客户需要一个唯一的会话ID——通过cookie交换这个会话ID的信息;
session.isNew()方法判断会话是否已经存在;
禁用cookie的客户会忽略"Set-Cookie"首部;如果客户不接受cookie,可以通过URL重写取得置于cookie中的会话ID;

public void doGet(HttpServletRequest request,HttpServletResponse response)throws IOException{
	response.setContentType("text/html");
	PrintWriter out=response.getWriter();
	//得到一个会话
	HttpSession session=request.getSession();
	out.println("<html><body>");
	//向这个URL增加额外的会话ID信息;对一个URL编码,需要调用response;
	out.println("<a href=\""+response.encodeURL("/BeerTest.do")+"\">click me</a>");
	out.println("</body></html>");
关键的HttpSession方法 作用
getCreationTime() 返回第一次创建会话的时间
getLastAccessedTime() 返回容器最后一次得到包含这个会话ID的请求后过去了多长时间(ms)
setMaxInactiveInterval() 指定对于这个会话客户请求的最大间隔时间(s)
getMaxInactiveInterval() 返回对于这个会话客户请求的最大间隔时间(s)
invalidate() 结束会话,这个会话中的所有会话属性也会解除绑定

会话有三种死法:超时;你在会话对象上调用invalidate();应用结束;
可以使用cookie在服务器和客户之间交换名/值String对;服务器把cookie发送给客户,客户再在以后的每个请求中发回这个cookie;客户的浏览器退出时,会话cookie就会消失,但是可以使cookie存活时间更长一些;
简单的定制cookie示例:

//创建和设置cookie的Servlet
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class CookieTest extends HttppServlet{
	public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException{
	response.setContentType("text/html");
	//得到表单中提交的用户名
	String name=request.getParameter("username");
	Cookie cookie=new Cookie("username",name);
	cookie.setMaxAge(30*60);//在客户端上存活30分钟
	response.addCookie(cookie);//将此cookie增加为"Set-Cookie"响应首部
	//让jsp建立响应页面
	RequestDispatcher view=request.getRequestDispatcher("cookieresult.jsp");
	view.forward(request,response);

下面这个JSP呈现以上servlet生成的视图:

<html><body>
	<a herf="checkcookie.do">click here</a>
</body></html>

构建CheckCookie类:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class CheckCookie extends HttpServlet{
	public void doGet*HttpServletRequest request,HttpServletResponse response) throws
	IOException,ServletException{
	response.setConetentType("text/html");
	PrintWriter out=response.getWriter();
	Cookie[] cookies=request.getCookies();
	
	if(cookies!=null){
		for(int i=0;i<cookies.length;i++){
			Cookie cookies[i];
			if(Cookie.getName().equals("username")){
				String userName=cookie.getValue();
				out.println("Hello"+userName);
				break;
				}
			}
		}
	}
}

HttpSession的重要里程碑;
绑定到会话的属性可以由属于同一个ServletContext而且处理同一会话中某个请求的其他servlet访问;
note about Servlet and JSP(一)
HttpSessionListener,HttpSessionAttributeListener和HttpSessionActivationListener必须在DD中注册,它们与会话本身相关,而不是与会话中放置的单个属性相关;
只有HttpSession对象会从一个VM移到另一个VM;
Listener例子:(监听者同时也是一个属性类)

package com.example;
import javax.servlet.http.*;
public class BeerSessionCounter implements HttpSessionListener{
//监听者允许跟踪Web应用中活动会话的个数
	static private int activeSessions;
	public static int getActiveSessions(){
		return activeSessions;
		}
	public void sessionCreated(HttpSessionEvent event){
		activeSessions++;
	}
	public void sessionDestroyed(HttpSessionEvent event){
		activeSessions--;
	}
}

在DD中配置监听者

<web-app...>
	<listener>
		<listener-class>
			com.example.BeerSeeeionCounter
		</listener-class>
	</listener>
</web-app>

属性监听者

package com.example;
import javax.servlet.http.*;

public class BeerAttributeListener implements HttpSessionAttributeListener{
	public void attributedAdded(HttpSessionBindingEvent event){
		String name=event.getName();
		Object value=event.getValue();
		System.out.println("Attribute added:"+name+":"+value);
	}
	public void attributeRemoved(HttpSessionBindingEvent event){
	String name=event.getName();
	Object value=event.getValue();
	System.out.println("Attribute removed:"+name+":"+value);
	}

	public void attributeReplaced(HttpSessionBindingEvent event){
	//and so on

类似地,在DD中配置监听者;

上一篇:note_doit_day03


下一篇:软件工程note