后端学习第四周
Servlet
Servlet 前置知识
http
客户端和服务器之间通过请求和响应进行交互。http,超文本传输协议,是请求和响应的一种标准协议。
客户端发给服务器的格式叫“请求协议”;服务器发给客户端的格式叫“响应协议”。
书写格式:
特点:
-
支持客户端/服务器模式。
-
简单快速。
-
灵活。
-
无连接
-
无状态
URL:
URL是一种特殊的URI,包含了用于查找某个资源的足够的信息。
URL格式:
http://host[:port]/[abs_path]
http://IP(主机名/域名):端口/访问的资源路径
http表示要通过HTTP协议来定位网络资源;
host表示合法的Internet主机域名或者IP地址;
port 指定一个端口号,为空则使用缺省端口80;
abs_path 指定请求资源的URI;如果URL中没有给出abs_path,那么当它作为请求URI时,必须以“/”的形式给出,通常这个工作浏览器自动帮我们完成。
HTTP请求
三部分:请求行、请求头、请求正文
GET请求(没有请求体)
GET/.../...?unme=... HTTP/1.1 (请求行)
xxxx:xxxx
xxxxx:xxxxxx (请求头)
....:....
POST请求:
POST /.../... HTTP1.1 (请求行)
xxxx:xxxx
xxxxx:xxxxxx (请求头)
....:...
Form Data
uname:zhangsan (请求体)
HTTP响应
状态行(响应行)
消息报头(响应头)
响应体
消息头:
请求头
Referer:指明请求从哪来。例如从百度来的才算入价格。通常用来做统计工作,防盗链。
响应头
Locaton:Location响应报头域重定向新的位置。常用在更换域名的时候。
Refresh:自动跳转,可以在html实现,也可以在后台实现。
服务器
服务器等待和接受客户端的请求,以分配资源。
Tomcat服务器的安装和启动:
解压后配置JAVA_HOME路径,打开bin目录下的startup.bat,若能成功运行且打开127.0.0.1.8080时打开的是Tomcat的页面,则成功。
Tomcat目录结构
bin:启动和关闭tomcat的bat 文件
conf:配置文件server.xml该文件用于配置server相关的信息,比如tomcat启动的端口号,配置主机(Host) ; web.xml文件配置与web应用(web应用相当于一个web站点); tomcat-user.xml配置用户名密码和相关权限
lib:该目录放置运行tomcat运行需要的jar包
logs:存放日志,当我们需要查看日志的时候,巧以查询信息
webapps:放置我们的web应用
work工作目录:该目录用于存放jsp被访问后生成对应的server文件和.class文件
IDEA 配置 Tomcat
在Setting中找到"Appliction Servers",点击右侧的"+"号,选择"Tomcat Server",选定Tomcat Home路径,点击应用即可。
Servlet
Servlet是Server 与Applet的缩写,是服务端小程序的意思。要实现web开发,需要实现Servlet标准。
Servlet本质上也是Java类,但要遵循Servlet规范进行编写,没有main()方法,它的创建、使用、销毁都由Servlet容器进行管理(如Tomcat)。(言外之意:写自己的类,不用写main方法,别人自动调用)
Servlet是和HTTP协议是紧密联系的,其可以处理HTTP协议相关的所有内容。这也是Servlet应用广泛的原因之一。
提供了Servlet功能的服务器,叫做Servlet容器,其常见容器有很多,如Tomcat, Jjetty, WebLogic Server,WebSphere,JBoss 等等。
Servlet基本使用
在IDEA中创建web项目
New Project中,选择Java Enterprise,勾选Web Application(4.0),点击Next,设定Project name和Project location,点击Finish即可。
重写service方法
-
在类名后加上extends HttpServlet。
-
Crtl + O, 选择HttpServletRequest的service方法。
设置注释,指定访问的路径
@webServlet("/ser01")
或 @webServlet(name = "Servlet01", value = "/ser01", "ser001") 以设置多个路径。
其他服务器设置
右上角下拉条选择Edit Configurations,可更改URL为更简单的地址,可取消勾选After launch或设定其他打开方式。
访问路径说明
localhost:8080/s01/ser01
localhost:本台主机
8080:端口号为8080的程序(即tomcat)
s01:服务器中的项目
ser01:项目里的资源
Servlet的实现代码
package com.join.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 实现Servlet
*
*/
//设置注解,指定访问的路径
@WebServlet("/ser01")
//也可以用以下写法,但不常用
//value 和 urlPatterns 可以是数组,即多个路径可访问
//@WebServlet(name = "Servlet01", value = "/ser01")
//@WebServlet(name = "Servlet01", value = {"/ser01", "ser001"})
//@WebServlet(urlPatterns = "/ser01")
//@WebServlet(urlPatterns = {"/ser01", "/ser001"})
//@
public class Servlet01 extends HttpServlet {
//继承HttpServlet类
//重写Service方法,选择 HttpServletRequest
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//打印内容在控制台
System.out.println("Hello Servlet!");
//通过流输出数据到浏览器
resp.getWriter().write("Hello Servlet!");
}
}
其他 Servlet 实现方式
(不常用)
-
继承Http的父类:GenericServlet,重写Service 方法
public class Servlet02 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//Service 方法
}
}
-
继承 GenericServlet 的总接口,实现其中的方法:
public class Servlet03 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//Service 方法
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
-
仍继承 httpServlet,但重写 doGet 和 doPost 方法
public class Servlet04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//doGet方法
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//调用doGet方法
doGet(req, resp);
}
}
不重写 Service 方法,但较为麻烦,不常用。
Service 方法底层也调用了 doGet 和 doPost 方法。
Servlet 生命周期
简单概括为四步:Servlet 类加载-->实例化(Servlet容器检查是否存在Servlet对象,否则执行,是则跳过,生命周期中只执行一次)-->服务-->销毁。
init 方法,在 Servlet 实例创建后执行。
service 方法,每次有请求到达某个 Servlet 方法时执行,用来处理请求(证明该 Servlet 进行服务了)。
destroy 方法,Servlet 实例销毁时进行(证明该 Servlet 实例被销毁了)。
HttpServletRequest 对象
主要作用是用来接收客户端发送过来的请求信息。
常用方法:
getRequestURL() 获取客户端发出请求时的完整URL
getRequestURI() 获取请求行中的资源名称部分(项目名称开始)
getQueryString() 获取请求行中的参数部分
getMethod() 获取客户端请求方式
getProtocol() 获取HTTP版本号
getContextPath() 获取webapp名字
/*常用方法*/
//获取请求时的完整路径(从http开始,到"?"前面结束)
String url = req.getRequestURL() + "";
System.out.println("获取请求时的完整路径:" + url);
//获取请求时的部分路径(从项目的站点名开始,到"?"前面结束)
String uri = req.getRequestURI();
System.out.println("获取请求时的部分路径:" + uri);
//获取请求时的参数字符串(从"?"后面开始,到最后的字符串)
String queryString = req.getQueryString();
System.out.println("获取请求时的参数字符串:" + queryString);
//获取请求方式(GET和POST)
String method = req.getMethod();
System.out.println("获取请求方式:" + method);
//获取当前协议版本(HTTP/1.1)
String prototol = req.getProtocol();
System.out.println("获取当前协议版本:" + prototol);
//获取项目的站点名(项目对外访问路径)
//上下文路径
String webapp = req.getContextPath();
System.out.println("获取项目的站点名:" + webapp);
控制台输出:
获取请求时的完整路径:http://localhost:8080/s01/ser05
获取请求时的部分路径:/s01/ser05
获取请求时的参数字符串:null
获取请求方式:GET
获取当前协议版本:HTTP/1.1
获取项目的站点名:/s01
获取请求的参数方法:
String getParameter(String)
String[] getParameterValues(String)
/*获取请求的参数*/
//获取指定名称的参数值,返回字符串(重点)
String uname = req.getParameter("uname");
String upwd = req.getParameter("upwd");
System.out.println("uname:" + uname + ",upwd: " + upwd);
//获取指定名称的参数的所有参数值,返回字符串数组(用于复选框传值)
String[] hobbys = req.getParameterValues("hobbys");
//判断数组是否为空
if (hobbys != null && hobbys.length > 0) {
for (String hobby : hobbys) {
System.out.println("爱好:" + hobby);
}
}
请求乱码问题
Tomcat8 及以上版本 | Tomcat7 及以下版本 | |
---|---|---|
GET请求 | 不会乱码,不需处理 | 会乱码,String name = new String(req.getParameter("uname").getBytes("ISO-8859-1"), "UTF-8");(每次只能处理一个(可处理任何数据,但必须只能是本身会乱码的),不好用,且Tomcat7及以下版本不常用) |
POST请求 | 会乱码,设置服务器解析编码的格式 | 会乱码,设置服务器解析编码的格式 |
解决 | req.setCharacterEncoding("utf-8");(只针对POST请求) | req.setCharacterEncoding("utf-8"); |
请求转发
req.getRequestDispatcher(url).forward(req, resp);
//url 可以是
//1. 特定Servlet:"ser01"
//2. jsp文件:"login.jsp"
//3. html文件:"login.html"
特点:
-
服务端行为
-
地址栏不发生变化
-
从始至终都是同一次请求
-
request数据可以共享
request作用域
从request中获取数据之后,将数据存入作用域中,然后请求转发到新的Servlet/jsp页面之后,就可以从作用域中取出数据并使用了。
方法:
//设置域对象内容
req.setAttribute(String name, String value);
//获取域对象内容
req.getAttribute(String name);
//删域对象的内容
req.removeAttribute(String name);
代码:
-
Servlet
package com.join.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 测试域对象与请求转发跳转
*/
@WebServlet("/ser06")
public class Servlet06 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置域对象内容
//数据可以是Object类型,即任何类型都行
req.setAttribute("name", "admin");
req.setAttribute("age", 10);
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
req.setAttribute("list", list);
//请求转发跳转到jsp,并通过与对象传递数据
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
}
-
jsp:
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%-- 如果要在jsp中些java代码,需要写在脚本段中 --%>
<%
//获取域对象,并输出
String name = (String) request.getAttribute("name");
System.out.println("name:" + name);
Integer age = (Integer) request.getAttribute("age");
System.out.println("age:" + age);
List<String> list = (List<String>) request.getAttribute("list");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
%>
</body>
</html>
控制台输出:
name:admin
age:10
aaa
bbb
HttpServletResponse 对象
HttpServletResponse的主要功能用于服务器对客户端的请求进行响应,将Web服务器处理后的结果返回给客户端。
两种形式:
getWriter()获取字符流(只能相应回字符)
getOutputStream() 获取字节流(能响应一切数据)
二者不能同时使用。
代码:
package com.join.servlet;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class Servlet07 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//字符输出流
//获取字符输出流
PrintWriter writer = resp.getWriter();
//输出数据
writer.write("Hello.");
//字节输出流
//获取字节输出流
ServletOutputStream out = resp.getOutputStream();
//输出数据,要转为字节数组
out.write("Hi.".getBytes());
}
}
当然,此时浏览器页面只会输出“Hello.”,并报错。因为二者不能同时使用,他们都调用了response对象。response对象只能被使用一次。
相应乱码问题
服务器端和客户端编码格式不同导致。
要设置客户端和服务端的编码格式支持中文,且保持一致。
-
对于字符流响应数据(getWriter()):
原因:相应中文必定出现乱码,因为服务器端在进行编码时会默认使用ISO-8859-1格式的编码。
解决:1. 设置服务端编码格式为"UTF-8"(在流之前进行设置):resp.setCharacterEncoding("UTF-8");
2 .设置客户端的编码格式:resp.setHeader("content-tpye", "text/html;charset=UTF-8");
(其中:
setHeader:响应头。
content-tpye:相应类型
text/html:响应的MIME类型,让其中字符有html效果
charset=UTF-8:编码格式。
或:同时设置客户端和服务端的编码格式:
resp.setContentType("text/html;charset=UTF-8");
2. 对于字节流响应数据(getOutputStream()):
不会出现服务端编码的问题,但是会出现两个端编码格式不一致的问题。
所以也应该同时设置客户端和服务端的编码格式。
重定向
resp.sendRedirect(url);
特点:
-
服务端指导,客户端行为。
-
地址栏改变
-
Location改变,
-
两次请求
-
request对象不共享
-
故数据也无法传递
请求转发与重定向其他区别:
-
请求转发的地址只能是当前站点名下的资源;重定向可以任意地址,可跨域(可以跳转到百度)
Cookie 对象
Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行处理的数据,放在本地的计算机上,不需要通过网络传输,因而提高网页处理的效率,并且能够减少服务器的负载,但是由于Cookie是服务器端保存在客户端的信息,所以其安全性也是很差的。例如常见的记住密码则可以通过Cookie来实现。
Cookie的创建、发送、获取
-
创建:直接new Cookie对象即可
-
发送:resp.addCookie(cookie);
-
获取(返回值为全部Cookie组成的Cookie数组):req.getCookies();
package com.join.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 1. Cookie的创建、发送
*
* 2. Cookie的获取
* 返回的是Cookie数组
*/
@WebServlet("/ser08")
public class Servlet08 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//Cookie的创建
Cookie cookie = new Cookie("name", "admin");
//发送(响应)Cookie对象
resp.addCookie(cookie);
//获取Cookie数组
Cookie[] cookies = req.getCookies();
//判断cookies是否为空
if(cookies != null && cookies.length > 0){
//遍历Cookie数组
for (Cookie c : cookies) {
//获取Cookie的名称和值
String name = cookie.getName();
String value = cookie.getValue();
System.out.println("名称:" + name + ",值:" + value);
}
}
}
}
控制台输出:
名称:name,值:admin
Cookie设置到期时间
Cookie类的一个字段:maxAge控制Cookie的到期时间。可以通过setMaxAge(int time);方法设定Cookie的最大有效时间,以秒为单位。
到期时间的取值:
-
负整数:
表示不储存该Cookie。
该Cookie只在浏览器此次打开期间存活(即存于浏览器内存中),一旦关闭浏览器窗口,Cookie消失。Cookie对象的maxAge默认值为-1。
-
正整数:
表示Cookie可存活的秒数。
该Cookie在浏览器(即硬盘)中储存相应的时间,时间到后消失。
-
零
表示删除该Cookie。
该Cookie作废。如果原本浏览器已经保存这个Cookie(在浏览器内存中或在硬盘中),就删除该Cookie。
Cookie路径
假设现在发送的请求路径是“/servlet13/cookie/generate”生成的cookie,如果cookie没有设置path
默认的path是:/servlet13/cookie 以及它的子路径。
也就是说,以后只要浏览器的请求路径是/servlet13/cookie 这个路径以及这个路径下的子路径,cookie都会被发送到服务器。
Cookie几个注意点
-
Cookie存在浏览器中。换浏览器后不会存在。
-
Cookie不建议存中文。如果有中文,则通过URLEncoder.encode()来进行编码,获取时通过URLDecoder.decode()进行解码。
-
如果服务器发送了重复的Cookie,会覆盖原来的。
-
不同浏览器存放Cookie的上限不同(有限),且Cookie大小有限(4kb左右)。
HttpSession 对象
对于服务器而言,每一个连接到它的客户端都是一个session。servlet容器使用此接口创建HTTP客户端和HTTP服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。
Session的创建和常用方法:
package com.join.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Session对象的获取
* 和常用方法
*/
@WebServlet("/ser09")
public class Servlet09 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Session对象
//如果Session存在则获取,如果不存在则创建
HttpSession session = req.getSession();
//获取Session的会话标识符
String id = session.getId();
System.out.println(id);
//获取Session的创建时间
//返回时间戳
System.out.println(session.getCreationTime());
//获取最后一次访问时间
//返回时间戳
System.out.println(session.getLastAccessedTime());
//判断是否是新的Session对象
System.out.println(session.isNew());
}
}
Session标识符JSESSIONID
每当一次请求到达服务器,如果开启了会话(访问了Session),服务器第一步会查看是否从客户端回传一个名为JSESSIONID的cookie,如果没有则认为这是一次新的会话,会创建一个新的Session对象,并用唯一的sessionld 为此次会话做一个标志。如果有JESSIONID这个cookie回传,服务器则会根据JSESSIONID这个值去查看是否含有id为JSESSION值的session对象,如果没有则认为是一个新的会话,重新创建一个新的session对象,并标志此次会话;如果找到了相应的session对象,则认为是之前标志过的一次会话,返回该session对象,数据达到共享。
这里提到一个叫做JSESSIONID的cookie,这是一个比较特殊的cookie,当用户请求服务器时,如果访问了Session,则服务器会创建一个名为JSESSIONID,值为获取到的Session(无论是获取到的还是新创建的)的sessionld的cookie对象,并添加到response对象中,响应给客户端,有效时间为关闭浏览器。
所以 Session的底层依赖Cookie来实现。
Session域对象
Session域对象在请求转发和重定向中都可以使用
Servlet中:
package com.join.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Session域对象
*/
@WebServlet("/ser10")
public class Servlet10 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Session对象
HttpSession session = req.getSession();
//设置域对象
session.setAttribute("uname", "admin");
session.setAttribute("upwd", "123456");
//移除Session对象
session.removeAttribute("upwd");
//重定向到jsp页面
resp.sendRedirect("index1.jsp");
}
}
jsp中:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>获取域对象</title>
</head>
<body>
<%
//获取Session对象
String name = (String) session.getAttribute("uname");
String upwd = (String) session.getAttribute("upwd");
//输出
out.print("name:" + name + " upwd:" + upwd);
%>
</body>
</html>
网页输出:
name:admin upwd:null
Session对象的销毁
-
默认到期时间
Tomcat中Session默认存活时间为30分钟,即不操作30分钟后,销毁。期间若操作,重新计时。
可以在web.xml文件中修改该默认值
<session-config>
<session-timeout>30<session-timeout>
</session-config>
将30改为其他数字即可修改。
-
自己设定到期时间
通过session.setMaxInactiveInterval(int);来修改,单位为秒。
//获取Session对象
HttpSession session = req.getSession();
//设置Session最大不活动时间
session.setMaxInactiveInterval(15);
-
立即销毁
可以通过session.invalidate()方法让session立刻失效。
-
关闭浏览器
Session底层实现依赖Cookie,Cookie默认在关闭浏览器时失效。
-
关闭服务器
当关闭服务器,Session销毁。
mvc
MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范,是将业务逻辑、数据、显示分离的方法来组织代码。是一种架构模式。
构成:
控制器:用户在网页上单击一个 URL 路径,这对 Web 服务器来说,相当于用户发送了一个请求。而获取请求后如何解析用户的输入,并执行相关处理逻辑,最终跳转至正确的页面显示反馈结果,这些工作往往是控制层(Controller)来完成的。
模型:在请求的过程中,用户的信息被封装在 User 实体类中,该实体类在 Web 项目中属于数据模型层(Model)。
视图:在请求显示阶段,跳转的结果网页就属于视图层(View)。
主要作用:降低了视图与业务逻辑间的双向偶合。
优缺点:
优点:
-
多视图共享一个模型,大大提高了代码的可重用性
-
MVC 三个模块相互独立,松耦合架构
-
控制器提高了应用程序的灵活性和可配置性
-
有利于软件工程化管理
缺点:
-
原理复杂
-
增加了系统结构和实现的复杂性
-
视图对模型数据的低效率访问
JSP + JavaBean
JSP+JavaBean 模式在一定程度上实现了 MVC,即 JSP 将控制层和视图合二为一,JavaBean 为模型层。JSP+JavaBean 模式中 JSP 身兼数职,既要负责视图层的数据显示,又要负责业务流程的控制,结构较为混乱,并且也不是我们所希望的松耦合架构模式,所以当业务流程复杂的时候并不推荐使用。
Servlet + JSP + JavaBean
Servlet+JSP+JavaBean 模式的结构清晰,是一个松耦合架构模式,一般情况下,建议使用该模式。
jar包
jar包:
Java Archive File,类似zip包,区别在于jar包包含了一个 META-INF/MANIFEST.MF 文件,包含了该Jar包的版本、创建人和类搜索路径Class-Path等信息。可执行Jar包会包含Main-Class属性,表明Main方法入口,尤其是较为重要的Class-Path和Main-Class。
打jar包
-
在File中的 Project Structure 中,点击Artifacts,选择 JAR,Empty,从而创建出一个空的jar包。
-
之后在Name中重命名jar包,并从右侧把需要添加的模块拖入左侧jar包中,此时jar包下会包含有相应的模块,点击右下OK。
-
在Build,Build Artfacts中,选中该包,点击Build就建好包了。