概述:
JavaEE Web开发是基于B/S(浏览器/服务器)架构实现的,客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。浏览器只需要请求服务器,获取Web页面,并把Web页面展示给用户即可。Web开发通常是指开发服务器端的Web应用程序。
HTTP协议:
在Web应用中,浏览器请求一个URL,服务器就把生成的HTML网页发送给浏览器,而浏览器和服务器之间的传输协议是HTTP,而HTTP协议是一个基于TCP协议之上的应用层协议,基于请求-响应模式。HTTP Server实现流程:
代码实例:
// 基于TCP协议连接 + HTTP协议通信 的服务器端
public class HttpServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
while(true) {
// 客户端基于HTTP协议发起请求
Socket clientSocket = serverSocket.accept();
System.out.println("有新的客户端连接:");
// 读取到客户端的请求头内容(基于http协议格式)
BufferedReader reader =
new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
String line = null;
while((line = reader.readLine()) != null) {
// 读到空行,代表请求头结束
// 由于是GET请求,不存在request body(请求体)
// 所以退出请求头的读取
if(line.isEmpty()) {
break;
}
System.out.println(line);
}
System.out.println("开始响应.....");
try(BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(
clientSocket.getOutputStream()));){
// 响应内容
String response = "您优惠券是:<b>"+UUID.randomUUID().toString()+"</b>";
// 按照HTTP协议将响应内容输出至客户端浏览器
// response header 响应头
out.write("HTTP/1.1 200 OK\r\n");
out.write("Connection: close\r\n");
out.write("Content-Type: text/html\r\n");
out.write("Content-Length: " + response.length() + "\r\n");
out.newLine(); // 换行
out.newLine(); // 换行
// reponse body 响应体
out.write(response);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Servlet:
在JavaEE平台上,处理TCP连接,解析HTTP协议这些底层工作统统扔给现成的Web服务器去做,开发人员只只需要使应用程序跑在Web服务器上。为了实现这一目的,JavaEE提供了Servlet API,我们使用Servlet API编写自己的Servlet来处理HTTP请求,Web服务器实现Servlet API接口,实现底层功能:
┌───────────┐
│My Servlet │应用程序
├───────────┤
│Servlet API│servlet接口
┌───────┐ HTTP ├───────────┤
│Browser│<──────>│Web Server │web服务器
└───────┘ └───────────┘
客户端 请求/响应
代码实例:
// WebServlet注解表示这是一个Servlet,并映射到地址 hello.do
@WebServlet(urlPatterns = "/hello.do")
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest req//请求, HttpServletResponse resp//响应)
throws ServletException, IOException {
// 设置响应类型:
resp.setContentType("text/html");
// 获取输出流:
PrintWriter pw = resp.getWriter();
// 写入响应:
pw.write("<h1>Hello, world!</h1>");
// 最后不要忘记flush强制输出:
pw.flush();
}
}
Servlet生命周期:
通过一个URL路径发起对一个Servlet请求的过程中,其本质是在调用执行Servlet实例的doXXX()方法。该Servlet实例创建和使用的过程,被称为Servlet的生命周期。
整个生命周期包括:实例化、初始化、服务、销毁
代码实例:
@WebServlet("/demo")
public class demoServlet extends HttpServlet {
//实例化:根据Servlet请求的路径(PS:demo),查找该Servlet的实例
//若实例不存在,调用构造方法,完成Servlet实例的创建。
public demoServlet(){
System.out.println("实例化:demo实例被创建");
}
//初始化:通过该Servlet的实例,调用init()方法,执行初始化的逻辑
@Override
public void init() throws ServletException {
System.out.println("初始化:demo实例初始化");
}
//服务:通过该Servlet的实例,调用service()方法,根据请求方式,调用方法
//如果子类没有重写该方法,则调用HttpServlet父类的service()方法,在父类的该方法中进行请求方式的判断
//如果子类重写doXXX()方法,则调用子类重写后的doXXX()方法;
//如果子类没有重写doXXX()方法,则调用父类的doXXX()方法,在父类的方法实现中,返回一个405状态码:请求的方式服务器不提供支持
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("请求首页数据");
}
//销毁:服务器关闭或重启时,会销毁所有的Servlet实例,会调用Servlet实例的destroy()方法
@Override
public void destroy() {
System.out.println("销毁:demo实例被销毁");
}
}
Session与Cookie:
因为HTTP协议是一个无状态协议,因此当一个用户访问多个页面时,服务器无法识别两个请求是否为同一用户发出,为了追踪用户状态,Session与Cookie两种会话方式应运而生。
Session:
Session是一种基于唯一ID识别用户身份的机制,第一次访问服务器后,会自动获得一个Session ID。如果用户在一段时间内没有访问服务器,那么Session会自动失效,下次即使带着上次分配的Session ID访问,服务器也认为这是一个新用户,会分配新的Session ID。
JavaEE的Servlet机制内建了对Session的支持。当我们需要获取Session时,可以通过request请求对象的getSession()方法。例如;
HttpSession session = request.getSession();
服务端对session以map形式管理,通过getSession(true)方法创建一个32位随机字符JSESSIONID作为key放在map列表中,value是HttpSession对象;JSESSIONID作为key放在cookie里面,JSESSIONID 的值HttpSession作为value发送给客户端,客户端保存session信息,下次请求服务器时会携带JSESSIONID值,服务端拿到JESSIONID的值会去session列表查找对应的value
key | value |
JSESSIONID1 | HttpSession1 |
JSESSIONID2 | HttpSession2 |
Cookie:
Cookie是由服务器产生,存放在客户端的一系列键值对组成的文本信息,大小最多只有4K。Session中的JSESSIONID便是一个特殊的,由服务器生成的cookie,不同浏览器之间不能共享cookie。我们可以在客户端自己指定生成cookie。
读取Cookie:
private String parseLanguageFromCookie(HttpServletRequest req) {
// 获取请求附带的所有Cookie:
Cookie[] cookies = req.getCookies();
// 如果获取到Cookie:
if (cookies != null) {
// 循环每个Cookie:
for (Cookie cookie : cookies) {
// 如果Cookie名称为lang:
if (cookie.getName().equals("lang")) {
// 返回Cookie的值:
return cookie.getValue();
}
}
}
// 返回默认值:
return "en";
}
创建Cookie:
@WebServlet("/setCookie")
public class CookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("user_name","GGBond");//指定cookie的key
Cookie cookie1 = new Cookie("pass_word","123456");//指定cookie的value
//调用addCookie方法添加
resp.addCookie(cookie);
resp.addCookie(cookie1);
//重定向至web.html页面
resp.sendRedirect("web.html");
}
}
Session与Cookie对比
属性 | Session | Cookie |
产生时间 | 首次请求时产生 | 首次响应时由服务器产生发送回客户端 |
保存地址 | 保存在服务端 | 保存在客户端 |
过期时间 | 由服务端决定,客户端/服务端关闭即消逝 | 在cookie生成的时候设置,可永久保存 |
安全性 | Session > Cookie | |
作用 | 跟踪客户端浏会话,Cookie可保存用户账户密码及用户特征 |
综合案例:
//匿名聊天室
@WebServlet("/ChatServlet")
public class ChatServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理报文乱码
req.setCharacterEncoding("UTF-8");
//获得聊天记录
String IP = req.getRemoteAddr();//获得IP
String user = req.getParameter("user");
String content = req.getParameter("content");//获取发言内容,与客户端input name需要一致
//统计发言次数
HttpSession session = req.getSession();//创建session对象
Integer chatcount = (Integer)session.getAttribute("chat_count");//通过key获得发言次数
if (chatcount == null){
chatcount = 1;
session.setAttribute("chat-count",chatcount);//将发言次数chatcount保存在session中,key为chat-count
}
//通过判断发言次数赋予不同的头衔
String face = "";
if (chatcount >= 5){
face = "<img src='pic/face1.png'/>";
}else if (chatcount >= 3){
face = "<img src='pic/face2.png'/>";
}else if (chatcount >=1){
face = "<img src='pic/face3.png'/>";
}
String chatMsg = String.format("%s%s说:%s[%s]",face,user,content, LocalTime.now());//输出
//使用Cookie统计个人发言次数
Cookie[] cookies = req.getCookies();//获取Cookie对象
int total = 1;
if (cookies != null){
//查找指定Cookis
Cookie cookie = null;
//遍历并匹配
for (Cookie ck: cookies){
if (ck.getName().equals("total")){
cookie = ck;
total = Integer.parseInt(ck.getValue());
}
}
if (cookie == null){
cookie = new Cookie("total",String.valueOf(total));
}else {
cookie = new Cookie("total",String.valueOf(total+1));
}
}
//存储聊天记录
ServletContext application = req.getServletContext();
//获得application范围内的聊天记录
List<String> messageList = (List<String>)application.getAttribute("chat_message_list");//通过key获取聊天记录
//无记录时创建存储集合
if (messageList == null){
messageList = new ArrayList<String>();
application.setAttribute("chat_message_list",messageList);//将聊天记录messageList保存在session中,key为chat_message_list
}
//向集合添加聊天记录
messageList.add(chatMsg);
//显示聊天记录
resp.setContentType("text/html;charset=utf-8");//设置文本参数
PrintWriter out = resp.getWriter();//getWriter获取输入流,返回一个PrintWriter object
for (String messige: messageList) {
out.write(messige+"<br/>");//PrintWriter可以直接调用write()或print()方法,把字符串作为参数写入
}
out.flush();//PrintWriter可以直接调用write()或print()方法,把字符串作为参数写入
}
}
浏览器端页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>焦灼の聊天室</title>
</head>
<body>
<b>杰哥の聊天室</b>
<form action="ChatServlet" method="post">
<p>
用户:<input name = "user">
</p>
<p>
发言:<input name = "content" >
</p>
<button>发送</button>
</form>
</body>
</html>
向上攀爬的痛苦,终会在登顶时烟消云散
——ZQY