1. HTTP 协议
1.1 概述
概述
- HTTP协议是
Hyper Text Transfer Protocol
(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。 - HTTP 是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。
- HTTP 是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的规范化工作正在进行之中,而且HTTP-NG(Next Generation of HTTP)的建议已经提出。
- HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。
简而言之,就是定义了,客户端和服务器端通信时,发送数据的格式。
主要特点
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
- 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
- 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
- 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
- 支持 B/S 及 C/S 模式。
- 默认端口号 80
1.2 URI和URL的区别
- URL 是一种特殊类型的 URI ,可以说 URI 是抽象的,而具体要使用URL来定位资源。
- 比如 身份证的地址为 URL,身份证号为 URI 。
URI
URI,是 uniform resource identifier
,统一资源标识符,用来唯一的标识一个资源。Web上可用的每种资源如HTML文档、图像、视频片段、程序等都是一个来URI来定位的。
URI一般由三部组成:
- 访问资源的命名机制
- 存放资源的主机名
- 资源自身的名称,由路径表示,着重强调于资源。
URL
URL 是uniform resource locator
,统一资源定位器,它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。URL是Internet上用来描述信息资源的字符串,主要用在各种WWW客户程序和服务器程序上,特别是著名的Mosaic。
采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。URL一般由三部组成:
- 协议(或称为服务方式)
- 存有该资源的主机IP地址(有时也包括端口号)
- 主机资源的具体地址。如目录和文件名等
URN
URN,uniform resource name
,统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com
。
URI是以一种抽象的,高层次概念定义统一资源标识,而 URL 和URN则是具体的资源标识的方式。URL和URN都是一种URI。笼统地说,每个 URL 都是 URI,但不一定每个 URI 都是 URL。这是因为 URI 还包括一个子类,即统一资源名称 (URN),它命名资源但不指定如何定位资源。上面的 mailto、news 和 isbn URI 都是 URN 的示例。
在Java的 URI 中,一个URI实例可以代表绝对的,也可以是相对的,只要它符合URI的语法规则。而URL类则不仅符合语义,还包含了定位该资源的信息,因此它不能是相对的。
在Java类库中,URI类不包含任何访问资源的方法,它唯一的作用就是解析。相反的是,URL类可以打开一个到达资源的流。
1.3. 请求消息 Request
客户端发送一个HTTP请求到服务器的请求消息包括以下四部分
- 请求行(request line)
- 请求头部(header)
- 请求空行
- 请求体
请求行
请求方式 请求url 请求协议/版本
GET /login.html HTTP/1.1
- 请求方式:HTTP协议有 7 中请求方式,常用的有 2 种:
- GET:请求参数在请求行中,在 url 后;请求的url长度有限制的;不太安全,获取资源(查询用)
- POST:请求参数在请求体中,请求的url长度没有限制的,相对安全,向指定资源提交数据进行处理请求(例如提交表单或者上传文件),新增用
- PUT: 用于向指定的URI传送更新资源,是幂等的,自身不带验证机制,存在安全性问题,更新用
- HEAD:获得报文首部
- DELETE:删除文件
- OPTIONS: 询问支持的方法
- TRACE:追踪路径
- CONNECT: 要求用隧道协议代理
请求头:客户端浏览器告诉服务器一些信息
请求头名称: 请求头值。
常见的请求头:
User-Agent
:浏览器告诉服务器,使用的浏览器版本信息(用于解决浏览器的兼容性问题)Referer:http://localhost/login.html
告诉服务器,当前请求从哪里来?- 防盗链,统计工作
请求空行
空行,就是用于分割 POST 请求的请求头,和请求体的。
请求体(正文)
封装POST请求消息的请求参数的
POST /login.html HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
1.4.响应消息 Response
一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。
HTTP 响应也由四个部分组成,分别是:
- 响应行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。
- 响应头,用来说明客户端要使用的一些附加信息
- Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
- Content-disposition:服务器告诉客户端以什么格式打开响应体数据
- in-line:默认值,在当前页面内打开
- attachment;filename=xxx:以附件形式打开响应体。文件下载
- 响应空行,响应头后面的空行是必须的
- 响应正文,服务器返回给客户端的文本信息。
HTTP/1.1 200 OK
Date: Fri, 22 May 2009 06:07:21 GMT
Content-Type: text/html; charset=UTF-8
<html>
<head></head>
<body>
<!--body goes here-->
</body>
</html>
1.5.状态码
状态码 | 响应类别 |
---|---|
1xx | 指示信息–表示请求已接收,继续处理 |
2xx | 成功–表示请求已被成功接收、理解、接受 |
3xx | 重定向–要完成请求必须进行更进一步的操作 |
4xx | 客户端错误–请求有语法错误或请求无法实现 |
5xx | 服务器端错误–服务器未能实现合法的请求 |
选取几个代表的
- 200(成功)
- 302(重定向)
- 304(访问缓存)
- 404(请求路径没有对应的资源)
- 405(请求方式没有对应的doXxx方法)
- 500(服务器内部出现异常)
1.6. HTTP 1.0 和 HTTP 1.1
主要区别
- 长连接 : 在HTTP/1.0中,默认使用的是短连接,也就是说每次请求都要重新建立一次连接。HTTP 是基于TCP/IP协议的,每一次建立或者断开连接都需要三次握手四次挥手的开销,如果每次请求都要这样的话,开销会比较大。因此最好能维持一个长连接,可以用个长连接来发多个请求。HTTP 1.1起,默认使用长连接 ,默认开启Connection: keep-alive。 HTTP/1.1的持续连接有非流水线方式和流水线方式 。流水线方式是客户在收到HTTP的响应报文之前就能接着发送新的请求报文。与之相对应的非流水线方式是客户在收到前一个响应后才能发送下一个请求。
- 错误状态响应码 :在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
- 缓存处理 :在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
- 带宽优化及网络连接的使用 :HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
长连接与短连接
- HTTP/1.0中默认使用短连接,客户端和服务器每进行一次HTTP操作(如请求资源),就建立一次连接,任务结束就中断连接。
- HTTP/1.1起,默认使用长连接,用以保持连接特性。当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。
1.7. Cookie
客户端会话技术,将数据保存到客户端
实现原理:基于响应头 set-cookie和 请求头 cookie 实现
使用步骤:
- 创建Cookie对象,绑定数据
*new Cookie(String name, String value)
- 发送Cookie对象
response.addCookie(Cookie cookie)
- 获取Cookie,拿到数据
Cookie[] request.getCookies()
一些细节
一次可不可以发送多个cookie?
可以创建多个Cookie对象,使用 response 调用多次 addCookie 方法发送 cookie 即可。
cookie 在浏览器中保存多长时间?
- 默认情况下,当浏览器关闭后,Cookie 数据被销毁
- 持久化存储:
setMaxAge(int seconds)
- 正数:将Cookie数据写到硬盘的文件中。持久化存储。并指定cookie存活时间,时间到后,cookie文件自动失效
- 负数:默认值
- 零:删除cookie信息
cookie 能不能存中文?
- 在tomcat 8 之前 cookie中不能直接存储中文数据。
- 需要将中文数据转码— 一般采用URL 编码(%E3)
- 在tomcat 8 之后,cookie 支持中文数据。特殊字符还是不支持,建议使用 URL 编码存储,URL 解码解析
cookie共享问题?
假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?
- 默认情况下cookie不能共享
setPath(String path)
:设置cookie的获取范围。默认情况下,设置当前的虚拟目录
- 如果要共享,则可以将path设置为
"/"
不同的tomcat服务器间cookie共享问题?
setDomain(String path)
:如果设置一级域名相同,那么多个服务器之间cookie可以共享setDomain(".baidu.com")
那么tieba.baidu.com
和news.baidu.com
中cookie可以共享
1.8 Session
服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。HttpSession
原理: Session的实现是**依赖于 Cookie **的。
快速使用
- 获取HttpSession对象:
-HttpSession session = request.getSession();
- 使用HttpSession对象:
Object getAttribute(String name)
void setAttribute(String name, Object value)
void removeAttribute(String name)
一些细节
当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
- 默认情况下。不是。
* 如果需要相同,则可以创建 Cookie ,键为JSESSIONID
,设置最大存活时间,让 cookie 持久化保存。
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60*60);
response.addCookie(c);
客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
- 不是同一个,但是要确保数据不丢失。tomcat自动完成以下工作
* session 的钝化:在服务器正常关闭之前,将 session对象系列化到硬盘上- session 的活化:在服务器启动后,将 session文件转化为内存中的 session对象即可。
session什么时候被销毁?
- 服务器关闭
- session对象调用
invalidate()
。 - session默认失效时间 30分钟
选择性配置修改
<session-config>
<session-timeout>30</session-timeout>
</session-config>
两者区分
Cookie 和 Session都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。
Cookie 一般用来保存用户信息。比如:
- 我们在 Cookie 中保存已经登录过得用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了;
- 一般的网站都会有保持登录也就是说下次你再访问网站的时候就不需要重新登录了,这是因为用户登录的时候我们可以存放了一个 Token 在 Cookie 中,下次登录的时候只需要根据 Token 值来查找用户即可(为了安全考虑,重新登录一般要将 Token 重写);
- 登录一次网站后访问网站其他页面不需要重新登录。
- 浏览器对于单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制(20个)
Session 的主要作用就是通过服务端记录用户的状态。
-
典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。
-
session可以存储任意类型,任意大小的数据
-
Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。
-
Cookie 存储在客户端中,而Session存储在服务器上,相对来说 Session 安全性更高。如果要在 Cookie 中存储一些敏感信息,不要直接写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。
简而言之
- session 存储数据在服务器端,Cookie 在客户端
- session 没有数据大小限制,Cookie有
- session 数据安全,Cookie相对于不安全
HTTP是不保存状态的协议,如何保存用户状态?
HTTP 是一种不保存状态,即无状态(stateless)协议。
Session 机制的存在就是为了解决这个问题,Session 的主要作用就是通过服务端记录用户的状态。典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了(一般情况下,服务器会在一定时间内保存这个 Session,过了时间限制,就会销毁这个Session)。
在服务端保存 Session 的方法很多,最常用的就是内存和数据库(比如是使用内存数据库redis保存)。既然 Session 存放在服务器端,那么我们如何实现 Session 跟踪呢?大部分情况下,我们都是通过在 Cookie 中附加一个 Session ID
来方式来跟踪。
Cookie 被禁用怎么办?
最常用的就是利用 URL 重写把 Session ID 直接附加在URL路径的后面。
2. Tomcat
常见的java相关的web服务器软件:
- webLogic:oracle公司,大型的 JavaEE 服务器,支持所有的 JavaEE 规范,收费的。
- webSphere:IBM公司,大型的 JavaEE 服务器,支持所有的 JavaEE 规范,收费的。
- JBOSS:JBOSS公司的,大型的 JavaEE 服务器,支持所有的 JavaEE 规范,收费的。
- Tomcat:Apache基金组织,中小型的 JavaEE 服务器,仅仅支持少量的 JavaEE 规范 servlet/jsp。开源的,免费的。
搭建
-
下载:http://tomcat.apache.org/
-
安装:解压压缩包即可。安装目录建议不要有中文和空格
-
卸载:删除目录就行了
-
启动:
bin/startup.bat
,双击运行该文件即可- 访问:浏览器输入:
http://localhost:8080
回车访问自己;http://别人的ip:8080
访问别人
-
可能遇到的问题
- 黑窗口一闪而过:正确配置 JAVA_HOME 环境变量
- 启动报错:
netstat -ano
找到占用的端口号,并且找到对应的进程,杀死该进程 conf/server.xml
中修改自身的端口号- 一般会将 tomcat 的默认端口号修改为80**。80端口号是 http 协议的默认端口号。**
-
关闭:
bin/shutdown.bat
- ctrl+c
- 点击启动窗口的 ×
配置部署
-
直接将项目放到 webapps 目录下即可。
*/hello
:项目的访问路径–>虚拟目录- 简化部署:将项目打成一个 war 包,再将 war 包放置到 webapps 目录下。(war包会自动解压缩)
-
配置
conf/server.xml
文件
- 在
<Host>
标签体中配置<Context docBase="D:\hello" path="/hehe" />
- docBase:项目存放的路径
- path:虚拟目录
- 在
conf\Catalina\localhost
创建任意名称的 xml 文件。在文件中编写<Context docBase="D:\hello" />
- 虚拟目录:xml 文件的名称
目录结构
java 动态项目的目录结构:
- 项目的根目录
-- WEB-INF目录:
--- web.xml:web项目的核心配置文件
--- classes目录:放置字节码文件的目录
--- lib目录:放置依赖的jar包
3. Servlet
概述
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。参考菜鸟教程
Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:
- Servlet 在 Web 服务器的地址空间内执行。
- 在传统的CGI中,每个请求都要启动一个新的进程,如果CGI程序本身的执行时间较短,启动进程所需要的开销很可能反而超过实际执行时间。
- 而在Servlet中,每个请求由一个轻量级的Java线程处理(而不是重量级的操作系统进程)。
- 一般来说,Servlet 进程只是在Web Server卸载时被卸载。
- 方便 :Servlet提供了大量的实用工具例程,例如自动地解析和解码HTML表单数据、读取和设置HTTP头、处理Cookie、跟踪会话状态等。
- 功能强大:在Servlet中,许多使用传统CGI程序很难完成的任务都可以轻松地完成。例如,Servlet能够直接和Web服务器交互,而普通的CGI程序不能。Servlet还能够在各个程序之间共享数据,使得数据库连接池之类的功能很容易实现。
- 可移植性好: Servlet用 Java 编写,Servlet API具有完善的标准。因此,为IPlanet Enterprise Server写的Servlet无需任何实质上的改动即可移植到Apache、Microsoft IIS或者WebStar。几乎所有的主流服务器都直接或通过插件支持Servlet。
3.1.三种实现方法
- 实现 Servlet 接口: 接口,我们需要实现接口里所有方法。
//Servlet的生命周期:从Servlet被创建到Servlet被销毁的过程
//一次创建,到处服务
//一个Servlet只会有一个对象,服务所有的请求
/*
* 1.实例化(使用构造方法创建对象)
* 2.初始化 执行init方法
* 3.服务 执行service方法
* 4.销毁 执行destroy方法
*/
public class ServletDemo1 implements Servlet {
//public ServletDemo1(){}
//生命周期方法:当Servlet第一次被创建对象时执行该方法,该方法在整个生命周期中只执行一次
public void init(ServletConfig arg0) throws ServletException {
System.out.println("=======init=========");
}
//生命周期方法:对客户端响应的方法,该方法会被执行多次,每次请求该servlet都会执行该方法
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
System.out.println("hehe");
}
//生命周期方法:当Servlet被销毁时执行该方法
public void destroy() {
System.out.println("******destroy**********");
}
//当停止tomcat时也就销毁的servlet。
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
}
- 继承 GenericServlet 类,极少用。
public class ServletDemo2 extends GenericServlet {
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
System.out.println("heihei");
}
}
- 继承 HttpServlet 方法(常用)
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("haha");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("ee");
doGet(req,resp);
}
}
Servlet的体系结构
Servlet -- 接口
|
GenericServlet -- 抽象类
|
HttpServlet -- 抽象类
-
GenericServlet:将 Servlet 接口中其他的方法做了默认空实现,只将
service()
方法作为抽象- 将来定义 Servlet 类时,可以继承 GenericServlet,实现
service()
方法即可
- 将来定义 Servlet 类时,可以继承 GenericServlet,实现
-
HttpServlet:对 http 协议的一种封装,简化操作
- 定义类继承 HttpServlet
- 复写doGet/doPost方法
3.2.配置
3.2.1. xml 配置
在 web.xml
中配置:
<!--配置Servlet -->
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>cn.leyou.web.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
执行原理
- 当服务器接受到客户端浏览器的请求后,会解析请求 URL 路径,获取访问的 Servlet 的资源路径
- 查找
web.xml
文件,是否有对应的<url-pattern>
标签体内容。 - 如果有,则在找到对应的
<servlet-class>
全类名 - tomcat 会将字节码文件加载进内存,并且创建其对象
- 调用其方法
3.2.2.生命周期
Servlet 的生命周期: 从 Servlet 被创建到 Servlet 被销毁的过程:
-
实例化:使用构造方法创建对象
-
初始化:执行 init 方法
-
服务:执行 service 方法
-
销毁:执行 destroy 方法
被创建:执行 init 方法,只执行一次
Servlet什么时候被创建?
- 默认情况下,第一次被访问时,Servlet 被创建
- 可以配置执行 Servlet 的创建时机。 在
<servlet>
标签下配置- 第一次被访问时,创建
<load-on-startup>
的值为负数 - 在服务器启动时,创建
<load-on-startup>
的值为0或正整数
- 第一次被访问时,创建
Servlet的 init 方法,只执行一次,说明一个Servlet 在内存中只存在一个对象,Servlet 是单例的
- 多个用户同时访问时,可能存在线程安全问题。
- 解决:尽量不要在 Servlet 中定义成员变量。即使定义了成员变量,也不要对修改值。
提供服务:执行service方法,执行多次
每次访问 Servlet 时,Service 方法都会被调用一次。
被销毁:执行destroy方法,只执行一次
- Servlet被销毁时执行。服务器关闭时,Servlet 被销毁
- 只有服务器正常关闭时,才会执行 destroy 方法。
- destroy 方法在 Servlet 被销毁之前执行,一般用于释放资源
3.2.3.注解配置
Servlet3.0 支持注解配置。可以不需要 web.xml 了。
- 在类上使用 @WebServlet 注解,进行配置:
@WebServlet("资源路径")
- 一个 Servlet 可以定义多个访问路径 :
@WebServlet({"/d4","/dd4","/ddd4"})
路径定义规则:
- /xxx:路径匹配
- /xxx/xxx:多层路径,目录结构
- *.do:扩展名匹配 (这里的 do 也可以替换成其它任意的字符)
注解配置的源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
String name() default "";//相当于<Servlet-name>
String[] value() default {};//代表urlPatterns()属性配置
String[] urlPatterns() default {};//相当于<url-pattern>
int loadOnStartup() default -1;//相当于<load-on-startup>
WebInitParam[] initParams() default {};
boolean asyncSupported() default false;
String smallIcon() default "";
String largeIcon() default "";
String description() default "";
String displayName() default "";
}
3.2.4. ServletConfig
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig 对象中,并在调用 servlet 的 init() 方法时,将 ServletConfig 对象传递给 servlet 。进而,通过ServletConfig 对象就可以得到当前 servlet 的初始化参数。 参考链接
相关方法
String getServletName()
– 获取当前Servlet在web.xml中配置的名字String getInitParameter(String name)
– 获取当前Servlet指定名称的初始化参数的值Enumeration getInitParameterNames()
– 获取当前Servlet所有初始化参数的名字组成的枚举ervletContext getServletContext()
– 获取代表当前web应用的ServletContext对象
1)初始化参数配置在
<init-param>
标签中
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/conf/app-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
2)服务器会自动封装在ServletConfig中,我们利用init()或者init(ServletConfig config)方法,可以只获取一次ServletConfig对象,然后通过其getInitParameter()方法,获取配置好的参数。
ServletConfig 对象也可以直接通过父类 GenericServlet 封装好的 getServletConfig() 方法获取或直接通过父类封装的 getInitParameter() 直接获取。
@Override
public void init(ServletConfig config) throws ServletException {
// 如果重写父类带参数的init()方法,这句一定写。
// 不然拿不到ServletConfig 对象,空指针。
// 原因在于,父类中代参的init()实现的时候,先this.config = config;然后调用无参this.init();
super.init(config);
String userName = config.getInitParameter("userName");
String userName2 = this.getInitParameter("userName");
String userName3 = this.getServletConfig().getInitParameter("userName");
logger.debug("初始化参数1: "+userName+" | "+"初始化参数2: "+userName2+" | 初始化参数3: "+userName3);
}
@Override
public void init() throws ServletException {
String userName2 = this.getInitParameter("userName");
String userName3 = this.getServletConfig().getInitParameter("userName");
logger.debug("初始化参数4: "+userName2+" | 初始化参数5: "+userName3);
}
3)注意点:如果重写父类带参数的 init() 方法,这句一定写,super.init(config),不然拿不到 ServletConfig 对象,空指针。原因在于,父类中代参的init()实现的时候,先this.config = config;然后调用无参this.init()。其实你复写两个 init() 方法后,输出的顺序也解释了这一点。推荐使用不带参数的 init() 方法。父类代码以及上例输出如下:
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
4)通过 getInitParameterNames() ,可以获取枚举类型的所有初始化参数。
3.3. Request
- request 和 response 对象是由服务器创建的。我们来使用它们
- request 对象是来获取请求消息,response 对象是来设置响应消息
request对象继承体系结构:
ServletRequest -- 接口
| 继承
HttpServletRequest -- 接口
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat)
接下来我们将介绍他的功能
3.3.1.获取请求数据
获取请求行数据 :
GET /day14/demo1?name=zhangsan HTTP/1.1
- 获取请求方式 :GET
String getMethod()
- 获取虚拟目录:
/day14
String getContextPath()
- 获取 Servlet 路径:
/demo1
String getServletPath()
- 获取 get 方式请求参数:
name=zhangsan
String getQueryString()
- 获取请求URI:
/day14/demo1
String getRequestURI()
: `/day14/demo1``- ``StringBuffer getRequestURL()
:
http://localhost/day14/demo1` - URL:统一资源定位符 :
http://localhost/day14/demo1
xxxxx医院 - URI:统一资源标识符:
/day14/demo1
医院
- 获取协议及版本:`HTTP/1.1``
- ``String getProtocol()`
- 获取客户机的IP地址:
String getRemoteAddr()
获取请求头数据
String getHeader(String name)
:通过请求头的名称获取请求头的值Enumeration<String> getHeaderNames()
:获取所有的请求头名称
获取请求体数据
请求体:只有 POST 请求方式,才有请求体,在请求体中封装了POST请求的请求参数
获取流对象,再从流对象中拿数据
BufferedReader getReader()
:获取字符输入流,只能操作字符数据ServletInputStream getInputStream()
:获取字节输入流,可以操作所有类型数据
3.3.2.获取请求参数通用方式
不论 get 还是 post 请求方式都可以使用下列方法来获取请求参数
String getParameter(String name)
:根据参数名称获取参数值username=zs&password=123
String[] getParameterValues(String name)
:根据参数名称获取参数值的数组hobby=xx&hobby=game
Enumeration<String> getParameterNames()
:获取所有请求的参数名称Map<String,String[]> getParameterMap()
:获取所有参数的 map集合
中文乱码
- get 方式:tomcat 8 已经将 get 方式乱码问题解决了
- post方式:会乱码,在获取参数前,设置 request 的编码
request.setCharacterEncoding("utf-8")
;
3.3.3.请求转发
一种在服务器内部的资源跳转方式
- 通过 request 对象获取请求转发器对象:
RequestDispatcher getRequestDispatcher(String path)
- 使用 RequestDispatcher 对象来进行转发:
forward(ServletRequest request, ServletResponse response)
特点
- 浏览器地址栏路径不发生变化
- 只能转发到当前服务器内部资源中
- 转发是一次请求
3.3.4.其它功能
共享数据
-
域对象:一个有作用范围的对象,可以在范围内共享数据
-
request 域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
相关方法
void setAttribute(String name,Object obj)
:存储数据Object getAttitude(String name)
:通过键获取值void removeAttribute(String name)
:通过键移除键值对ServletContext getServletContext()
:获取ServletContext
3.4. Response 对象
3.4.1.设置响应消息
设置响应行
- 格式:HTTP/1.1 200 ok
- 设置状态码:
setStatus(int sc)
设置响应头
setHeader(String name, String value)
设置响应体
获取输出流,使用输出流,将数据输出到客户端浏览器
- 字符输出流:
PrintWriter getWriter()
- 字节输出流:
ServletOutputStream getOutputStream()
3.4.2.重定向与转发
重定向:资源跳转的方式
//1. 设置状态码为302
response.setStatus(302);
//2.设置响应头location
response.setHeader("location","/day15/responseDemo2");
/简单的重定向方法
response.sendRedirect("/day15/responseDemo2");
过程分析
重定向过程:
客户浏览器发送http请求——>web服务器接受后发送302状态码响应及对应新的location给客户浏览器——>客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址——>服务器根据此请求寻找资源并发送给客户。
在这里location可以重定向到任意URL, 既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向 的路径,客户可以观察到地址的变化的。 重定向行为是浏览器做了至少两次的访问请求的。
转发过程:
客户浏览器发送http请求——>web服务器接受此请求——>调用内部的一个方法在容器内部完成请求处理和转发动作——>将目标资源发送给客户;
在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。 在客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。
重定向的特点:redirect
- 地址栏发生变化
- 重定向可以访问其他站点(服务器)的资源
- 重定向是两次请求。不能使用 request 对象来共享数据
转发的特点:forward
- 转发地址栏路径不变
- 转发只能访问当前服务器下的资源
- 转发是一次请求,可以使用 request 对象来共享数据
相对路径:通过相对路径不可以确定唯一资源
不以/
开头,以.
开头路径,如:./index.html
规则:找到当前资源和目标资源之间的相对位置关系
./
:当前目录../
:后退一级目录
绝对路径:通过绝对路径可以确定唯一资源
以/
开头的路径如:
- 绝对路径:
http://localhost/day15/responseDemo2
- 相对路径:
/day15/responseDemo2
转发还是重定向?
规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
- 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
- 建议虚拟目录动态获取:
request.getContextPath()
<a> , <form>
重定向…- 给服务器使用:不需要加虚拟目录
* 转发路径
- 建议虚拟目录动态获取:
乱码问题
设置编码,是在获取流之前设置:response.setContentType("text/html;charset=utf-8")
;
3.5. ServletContext 对象
概述
- 生命周期:Web容器在启动时,它会为每个Web应用程序都创建一个对应的 ServletContext 对象,它代表当前Web应用。ServletContext对象只在Web应用被关闭时才被销毁;不同的Web应用,ServletContext各自独立存在。
- 只要在同一个项目里面都可以取
- 作用:由于一个Web应用中的所有 Servlet 共享同一个 ServletContext 对象,因此 Servlet 对象之间可以通过ServletContext 对象来实现通讯。 ServletContext 对象通常也对称之为 context 域对象。
ServletContext 的获取
- 通过SevletConfig 对象
SevletConfig.getServletContext()
方法 - 直接调用封装好的
getServletContext ()
方法即可获取。- 通过 HttpServlet 获取
this.getServletContext();
- 通过 request 对象获取
request.getServletContext();
- 通过 HttpServlet 获取
接下来我们介绍几个它的功能
获取MIME类型
MIME 类型:在互联网通信过程中定义的一种文件数据类型
- 格式: 大类型/小类型 text/html image/jpeg
- 获取:
String getMimeType(String file)
域对象:共享数据
setAttribute(String name,Object value)
getAttribute(String name)
removeAttribute(String name)
ServletContext 对象范围:所有用户所有请求的数据
获取文件的真实(服务器)路径
String getRealPath(String path)
4. Filter 过滤器
web 中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。如:登录验证、统一编码处理、敏感字符过滤…
快速入门
- 定义一个类,实现接口Filter
- 复写方法
- 配置拦截路径
web.xml
- 注解
@WebFilter("/*")//访问所有资源之前,都会执行该过滤器
//实现 Filter 类
public class LogFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
// Filter 的 init 方法中提供了一个 FilterConfig 对象。
//获取初始化参数
String site = config.getInitParameter("Site");
// 输出初始化参数
System.out.println("网站名称: " + site);
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
// 输出站点名称
System.out.println("站点网址:http://www.runoob.com");
// 把请求传回过滤链
chain.doFilter(request,response);
}
public void destroy( ){
/* 在 Filter 实例被 Web 容器从服务移除之前调用 */
}
}
一些细节
web.xml
配置
<web-app>
<filter>
<filter-name>LogFilter</filter-name>
<filter-class>com.runoob.test.LogFilter</filter-class>
<init-param>
<param-name>Site</param-name>
<param-value>菜鸟教程</param-value>
</init-param>
</filter>
<!-- 一个过滤器,配置一个 filter-mapping -->
<filter-mapping>
<filter-name>>LogFilter</filter-name>
<!-- 拦截路径 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<!-- 类名 -->
<servlet-name>DisplayHeader</servlet-name>
<!-- 所在的包 -->
<servlet-class>com.runoob.test.DisplayHeader</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DisplayHeader</servlet-name>
<!-- 访问的网址 -->
<url-pattern>/TomcatTest/DisplayHeader</url-pattern>
</servlet-mapping>
</web-app>
过滤器生命周期方法
init
:在服务器启动后,会创建Filter对象,然后调用 init 方法。只执行一次。用于加载资源doFilter
:每一次请求被拦截资源时,会执行。执行多次destroy
:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行 destroy 方法。只执行一次。用于释放资源
过滤器配置详解
拦截路径配置:
- 具体资源路径:
/index.jsp
只有访问index.jsp
资源时,过滤器才会被执行 - 拦截目录:
/user/*
访问/user
下的所有资源时,过滤器都会被执行 - 后缀名拦截:
*.jsp
访问所有后缀名为 jsp 资源时,过滤器都会被执行 - 拦截所有资源:
/*
访问所有资源时,过滤器都会被执行
拦截方式配置:资源被访问的方式
- 注解配置:设置
dispatcherTypes
属性- REQUEST:默认值。浏览器直接请求资源
- FORWARD:转发访问资源
- INCLUDE:包含访问资源
- ERROR:错误跳转资源
- ASYNC:异步访问资源
web.xml
配置:设置<dispatcher></dispatcher>
标签即可,可以设置多个
过滤器执行流程
- 执行过滤器
- 执行放行后的资源
- 回来执行过滤器放行代码下边的代码
过滤器链(配置多个过滤器)
执行顺序:如果有两个过滤器:过滤器1和过滤器2
- 过滤器1
- 过滤器2
- 资源执行
- 过滤器2
- 过滤器1
过滤器先后顺序问题
- 注解配置:按照类名的字符串比较规则比较,值小的先执行
* 如: AFilter 和 BFilter,AFilter就先执行了。 - web.xml配置:
<filter-mapping>
谁定义在上边,谁先执行 - 如果同时配置,xml 的先执行
案例:过滤敏感词汇
5. Listener 监听器
web 的三大组件(Servlet、Filter、Listener)之一。
事件监听机制
* 事件 :一件事情
- 事件源 :事件发生的地方
- 监听器 :一个对象
- 注册监听:将事件、事件源、监听器绑定在一起。 当事件源上发生某个事件后,执行监听器代码
ServletContextListener: 监听 ServletContext 对象的创建和销毁
void contextDestroyed(ServletContextEvent sce)
:ServletContext 对象被销毁之前会调用该方法void contextInitialized(ServletContextEvent sce)
:ServletContext 对象创建后会调用该方法
配置
- web.xml
<listener> <listener-class>cn.leyou.web.listener.ContextLoaderListener</listener-class>
</listener>
- 注解
- @WebListener
6. 线程安全问题
Servlet 不是线程安全的。
每一个 Servlet 对象再Tomcat容器中只有一个实例对象,即是单例模式,共享一个对象
ServletRequest 对象是线程安全的
对于每一个请求由一个工作线程来执行,都会创建一个
ServletContext 是线程不安全
ServletContext:它是线程不安全的,多线程下可以同时进行读写,因此我们要对其读写操作进行同步或者深度的clone。
HttpSession:同样是线程不安全的,和 ServletContext 的操作一样。
Application对象也是线程不安全的
Application 对象用于存储和访问来自任何页面的变量,类似于 session 对象。不同之处在于,所有的用户分享一个 Application 对象,而 session 对象和用户的关系是一一对应的。
Spring 中 bean对象的作用域
默认 spring 容器中的 bean 是单例的。当单例中存在竞态条件,即有线程安全问题。spring 并不能保证 bean 的线程安全。
作用域 | 说明 |
---|---|
singleton | 默认的作用域,这种情况下的bean都会被定义为一个单例对象,该对象的生命周期是与Spring IOC容器一致的(但出于Spring懒加载机制,只有在第一次被使用时才会创建) |
prototype | bean被定义为在每次注入时都会创建一个新的对象 |
request | bean被定义为在每个HTTP请求中创建一个单例对象,也就是说在单个请求中都会复用这一个单例对象 |
session | bean被定义为在一个session的生命周期内创建一个单例对象 |
application | bean被定义为在ServletContext的生命周期中复用一个单例对象 |
websocket | bean被定义为在websocket的生命周期中复用一个单例对象 |
7.日志组件
第一步:导入log4j-1.2.15.jar(版本自定)包,在src目录下创建
src/com/company/resource/log4J.properties
文件。
log4j.appender.INFO.File
指定日志文件存储目录;log4j.appender.INFO.layout.ConversionPattern
形成的日志不仅包括打印的日志信息,还会存储日志产生的时间和产生日志的方法(具体参数可百度
log4j.rootLogger=DEBUG,CONSOLE,A,INFO,ERROR,WARN
log4j.addivity.org.apache=false
#console
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=DEBUG
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.CONSOLE.Target=System.log
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
#info
#log4j.logger.INFO=INFO
log4j.appender.INFO=org.apache.log4j.DailyRollingFileAppender
log4j.appender.INFO.File= ${catalina.home}/logs/CcbClient/info/info.log
log4j.appender.INFO.Append=true
log4j.appender.INFO.Threshold=INFO
log4j.appender.INFO.layout=org.apache.log4j.PatternLayout
log4j.appender.INFO.layout.ConversionPattern=[CcbClient] [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.INFO.filter.F1=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.INFO.filter.F1.LevelMin=INFO
log4j.appender.INFO.filter.F1.LevelMax=INFO
#debug
#log4j.logger.DEBUG=DEBUG
log4j.appender.A=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A.File= ${catalina.home}/logs/CcbClient/debug/debug.log
log4j.appender.A.Append=true
log4j.appender.A.Threshold=DEBUG
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=[CcbClient] [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.A.filter.F1=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.A.filter.F1.LevelMin=DEBUG
log4j.appender.A.filter.F1.LevelMax=DEBUG
#error
#log4j.logger.ERROR=ERROR
log4j.appender.ERROR=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ERROR.File=${catalina.home}/logs/CcbClient/error/error.log
log4j.appender.ERROR.Append=true
log4j.appender.ERROR.Threshold=ERROR
log4j.appender.ERROR.layout=org.apache.log4j.PatternLayout
log4j.appender.ERROR.layout.ConversionPattern=[CcbClient] [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.ERROR.filter.F1=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.ERROR.filter.F1.LevelMin=ERROR
log4j.appender.ERROR.filter.F1.LevelMax=ERROR
#WANR
log4j.appender.WARN=org.apache.log4j.DailyRollingFileAppender
log4j.appender.WARN.File=${catalina.home}/logs/CcbClient/warn/warn.log
log4j.appender.WARN.Append=true
log4j.appender.WARN.Threshold=WARN
log4j.appender.WARN.layout=org.apache.log4j.PatternLayout
log4j.appender.WARN.layout.ConversionPattern=[CcbClient] [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
log4j.appender.WARN.filter.F1=org.apache.log4j.varia.LevelRangeFilter
log4j.appender.WARN.filter.F1.LevelMin=WARN
log4j.appender.WARN.filter.F1.LevelMax=WARN
该配置可以在控制台或者后台文件中输出日志记录。info,error,debug都可以输出。导出文件路径自己可以修改,格式内容自己也可以修改。
第二步:在 src/util 下创建 log4j 加载文件(servlet)
public class Log4jInit extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.doPost(req, resp);
}
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
super.init();
String prefix = getServletContext().getRealPath("/");
String file = getInitParameter("log4j-init-file");
if (file != null) {
System.out.println("read log4j.properties:"+prefix + file);
PropertyConfigurator.configure(prefix + file);
}
}
}
第三步:在 web.xml 中配置 log4j 加载 servlet 路径,项目启动时进行加载
<servlet>
<servlet-name>log4j-init</servlet-name>
<servlet-class>util.Log4jInit</servlet-class>
<init-param>
<param-name>log4j-init-file</param-name>
<param-value>\WEB-INF\classes\com\company\resource\log4J.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
第四步:在需要输入日志信息的类中,创建 log 对象进行引用
public Logger log=Logger.getLogger(GetAccountInfo.class);
log.info("something");
log.debug("something");
log.warn("something");
log.error("something");
8.整合 JDBC
用户登录案例需求:
- 编写 login.html 登录页面 username & password 两个输入框
- 使用 Druid 数据库连接池技术,操作mysql,day14 数据库中 user 表
- 使用 JdbcTemplate 技术封装JDBC
- 登录成功跳转到 SuccessServlet 展示:登录成功!用户名,欢迎您
- 登录失败跳转到 FailServlet 展示:登录失败,用户名或密码错误
创建项目,导入html页面,配置文件,jar包
创建数据库环境
CREATE DATABASE day14;
USE day14;
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32) UNIQUE NOT NULL,
PASSWORD VARCHAR(32) NOT NULL
);
创建包 cn.leyou.domain ,创建类 User
/**
* 用户的实体类
*/
public class User {
private int id;
private String username;
private String password;
//省略 getter,setter及tostring
}
创建包 cn.leyou.util ,编写工具类 JDBCUtils
/**
* JDBC工具类 使用Durid连接池
*/
public class JDBCUtils {
private static DataSource ds ;
static {
try {
//1.加载配置文件
Properties pro = new Properties();
//使用ClassLoader加载配置文件,获取字节输入流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.初始化连接池对象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接池对象
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 获取连接Connection对象
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
创建包 cn.leyou.dao ,创建类UserDao,提供login方法
/**
* 操作数据库中User表的类
*/
public class UserDao {
//声明JDBCTemplate对象共用
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
* 登录方法
* @param loginUser 只有用户名和密码
* @return user包含用户全部数据,没有查询到,返回null
*/
public User login(User loginUser){
try {
//1.编写sql
String sql = "select * from user where username = ? and password = ?";
//2.调用query方法
User user = template.queryForObject(sql,
new BeanPropertyRowMapper<User>(User.class),
loginUser.getUsername(), loginUser.getPassword());
return user;
} catch (DataAccessException e) {
e.printStackTrace();//记录日志
return null;
}
}
}
使用 queryForObject 需注意:
- 其实支持的是标量子查询,只能传入一个基本类型的包装类的class,并返回一个基本类型对应包装类型的对象.
- 如果 sql 语句查询出0条或者多条数据的话,
queryForObject
会抛出EmptyResultDataAccessException
或IncorrectResultSetColumnCountException
的异常,而如果干脆使用方法 query ,或者 queryForList 则可以在编码中处理掉这种问题,而无需 try-catch
编写 cn.leyou.web.servlet.LoginServlet类
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置编码
req.setCharacterEncoding("utf-8");
//2.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
//3.封装user对象
User loginUser = new User();
loginUser.setUsername(username);
loginUser.setPassword(password);
//4.调用UserDao的login方法
UserDao dao = new UserDao();
User user = dao.login(loginUser);
//5.判断user
if(user == null){
//登录失败
req.getRequestDispatcher("/failServlet").forward(req,resp);
}else{
//登录成功
//存储数据
req.setAttribute("user",user);
//转发
req.getRequestDispatcher("/successServlet").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
编写FailServlet和SuccessServlet类
@WebServlet("/successServlet")
public class SuccessServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取request域中共享的user对象
User user = (User) request.getAttribute("user");
if(user != null){
//给页面写一句话
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出
response.getWriter().write("登录成功!"+user.getUsername()+",欢迎您");
}
}
@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//给页面写一句话
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出
response.getWriter().write("登录失败,用户名或密码错误");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}