Head First Servlets and JSP(一)

第一章 前言

http协议

GET和POST

GETPOST
参数数据只能放在请求行中放在体(body)中
建立书签可以(表单参数可以进入url,书签将包含第二次重建页面所需信息)不可以(由于书签只包含url,所有表单参数将丢失)
幂等是(不会对服务器造成改变)不是(可能对服务器造成
默认不是

TCP端口
端口是16位数据,标识服务器硬件上特定的软件程序;
从0~1023的端口号已经被保留,定制的服务器程序不要使用这些端口;
URL(统一资源定位符)
url
以及可选查询串:如果这是一个GET请求额外的参数会追加到URL最后以"?“开头,各个参数(名/值对)以”&"分隔;

JSP
JSP页面就像是一个HTML页面,可以把java和java有关的东西插入到该页面中;

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

Tomcat
Servlet没有main()方法,它们受控于另一个java应用——容器;
容器提供:
通信支持——让servlet与服务器对话,无须自己创建ServerSocket,监听端口,创建流等;
生命周期管理——控制servlet加载类,初始化,垃圾回收等资源管理;
多线程——自动为servlet请求创建一个新的Java线程;
声明方式实现安全——使用XML配置安全性而不必硬编码到servlet类中;
JSP支持——将JSP代码翻译成Java;

servlet可以有多个名字——通过XML部署;
servlet名字

部署描述文件DD
将Servlet部署到Web容器时,会创建一个简单的XML文档,称为部署描述文件(DD)
![用于URL映射的两个部署描述文件](https://img-blog.csdnimg.cn/20210617160401100.png
模型-视图-控制器(MVC(model-view-controller)
MVC把业务逻辑从Servlet中抽出来,放在一个可重用的java类(模型)中,模型是业务数据和处理该类数据的方法的组合;
MVC例子

J2EE
Tomcat只是一个Web容器,而不是完整的J2EE应用服务器(因为没有EJB容器);
J2EE应用服务器

第三章 MVC实战

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路径下的包,更改之后,响应结果正确;
响应结果
(调用模型实例过程略)

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

Servlet受容器的控制
Servlet基本作用
与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)类型,如pdf, jpg等等;
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中硬编码参数造成的修改不方便的问题,容器建立servlet只初始化一次,读取DD,为ServletConfig创建名/值对;
servlet初始化:
初始化参数

上下文初始化参数
在<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

上下文初始化参数和servlet初始化参数对比

常见

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

ServletContextListener
可以监听ServletContext的生命周期(例如在web缓存中的应用)
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){
		ServletContext 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

除了上下文事件,还可以监听上下文属性,servlet请求和属性等,8个监听者:
8个监听者
属性不同于参数:
属性与参数区别
属性的三个作用域:上下文(context)、请求、会话;
上下文作用域不是线程安全的;同步服务方法会防止同一个servlet中的其他线程访问上下文属性,但是不能阻止另外一个不同的servlet的访问;
解决办法:对上下文对象对象本身同步:

public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException,ServletException{
	response.setContentType("text/html");
	//etc.
	synchronized(getServletContext()){
	//对属性的操作,对ServletContext同步的其他代码无法同时访问上下文属性
	}
}

对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
RquestDispatcher
RequestDispatcher只有forward()和include()两个方法取请求和响应对象为参数;
requestDispatcher

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

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

HttpSession对象可以保存跨同一个客户多个请求的会话状态;
HTTP协议使用的是无状态连接,对容器而言,每个请求都来自于新的客户——客户需要一个唯一的会话ID——通过cookie交换这个会话ID的信息;
在响应中发送一个会话cookie:

HttpSession session=request.getSession();//cookie所有工作都在后台进行

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访问;
重要里程碑
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();
		//利用HttpSessionBindingEvent,可以得到触发此事件的属性的名和值
		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中配置监听者;
与会话相关的监听者:
与会话相关的监听者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值