servlet详解
Posted 互联网底层民工
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了servlet详解相关的知识,希望对你有一定的参考价值。
1.Servlet简介
Servlet(Server Applet)服务器的小程序。是用java编写的一个服务器程序,目的是和浏览器交互并且生成动态的web内容。Servlet广义上来讲指的是servlet接口,狭义上来讲指的是所有实现接口的实现类。
Servlet是指实现了Servlet接口类,Servlet运行于支持java的应用服务器(tomcat,Servlet是tomcat的一个组件)中。从原理上来讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务。
2.Servlet体系结构与Tomcat的关系
Servlet是Tomcat的一个组件。Servlet的功能需要依赖一个servlet-api.jar,这个包是由tomcat提供的。
Tomcat在初始化Servlet时,首先读取web.xml文件,根据web.xml文件中的参数信息初始化ServletConfig、ServletContext对象,同时帮助我们创建HttpServletRequest和HttpServletResponse对象一并交给Servlet实例,此时,Servlet就具有了相关的功能。
Servlet API 包含以下4个Java包:
1.javax.servlet:定义了servlet接口、GenericServlet抽象类等相关的通用接口和类。
2.javax.servlet.http: 主要定义了与HTTP协议相关的HttpServlet类,HttpServletRequest接口和HttpServletResponse接口。
3.javax.servlet.annotation:包含标注servlet,Filter,Listener的标注。它还为被标注元件定义元数据。
4.javax.servlet.descriptor:提供程序化登录Web应用程序的配置信息的类型。
3.Servlet实现方式
- 实现Servlet接口
- 继承GenericServelt抽象类(这个类是Servlet接口的一级实现类)
- 继承HttpServlet抽象类就可以间接的实现Servlet接口(位于javax.servlet.http包中的HttpServlet抽象类继承自GenericServlet类,是Servlet接口的二级实现类)
4. Get与Post请求
1.doGet、doPost方法与service方法的关系
在HttpServlet的API中,新增了两个特殊的方法doGet和doPost,这两个方法是对servlet方法的拆分,目的是希望不同的请求方式使用不同的方法处理。
这让大家联想到表单的两种常用提交方式get和post,如果是get提交方式则使用doGet方法处理,如果时post提交方式则使用doPost方法处理。
而service方法可以处理任何类型的请求,当我们去查看HttpServlet中service方法的源码,不难发现内部也通过对method请求方式做了验证决定调用doGet或doPost方法,所以三个方法之间的关系如下:
结论:
- 要么重写service方法
- 要么重写doget和dopost方法
二者选择其一
2.Get与Post请求的区别
1. 参数传递方式
Get:通过Url传递参数,Url与参数之间用?隔开,多个参数之间用&隔开,也是表单的默认提交参数。
Post:所有操作对用户来说都是不可见的,相对于get方式更安全。
2. 传递数据量大小不同
Get传送的数据量较小,这主要是因为受URL长度限制2kb-6kb;
Post传送的数据量较大,一般被默认为不受限制。
3. 信息缓存
Get提交的信息能被缓存,请求保留在浏览器历史记录中
Post提交的数据一般不能被缓存,请求不会保留在浏览器历史中
4. 编码方式与乱码处理问题
Get:默认编码类型是:application/x-www-form-urlencoded;Tomcat8之前的版本,默认编码格式是iso-8859-1,从Tomcat8版本之后默认编码改为UTF-8,所以如果是Tomcat8及以上版本就不需要进行转码处理,如果是Tomcat7及之前版本可以使用以下进行转码:
String name = request.getParameter(“name”);
String encodingName = new String(name.getBytes(“iso-8859-1”),””utf-8”) ;
Post: 支持多种编码类型,application/x-www-form-urlencoded或multipart/form-data.可以使用以下方法进行转码:
Request.setCharacterEncoding(“utf-8”);
在这里顺便说一下响应的乱码处理方法:
Response.setContentType(“text/html;charset=utf-8”);
5. 总结:
- get会把参数暴露在地址栏上,post不会;
- Get传递参数大小2-6kb,post没有限制
- Get会留下缓存,post不会
- 处理乱码:
- Get请求:tomcat8之后 默认字符集是utf-8 不需要处理
- Post请求: req.setCharacterEncoding(“utf-8”);
5. MVC设计模式
(一)分层开发
项目在实际开发过程中,会将整个项目从上到下分为表示层、业务逻辑层、数据访问层。三层开发是项目开发实践中典型的开发模式。
分层开发的目的:实现高内聚、低耦合。
各层的功能:
- 表示层:负责数据展示,搜集用户输入。即和用户进行交互的层。表示层一般是指jsp页面、html页面等。
- 业务逻辑层(service):负责处理功能模块的业务逻辑,以及界面层和数据层的数据传输。
- 数据访问层(dao):和数据库进行交互。一般在数据访问层会编写SQL语句完成增删改查的功能方法;(JDBC,Hibernate,mybatis)。
(二)MVC模式
MVC模式(Model-view-controller)是软件工程中的一种软件架构模式,他把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。这种模式用于应用程序的分层开发。
Model(模型):模型代表一个存取数据的对象或JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
View(视图):试图代表模型包含的数据的可视化,即对Model数据的呈现。
Controller(控制器):控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化更新视图。他是视图与模型分离开。
(三)MVC模式与分层的关系
Model不属于任何层,他可能在每个层中都会用到,是数据存储和传输的载体。V和C都属于表示层,所以在某些情况下,可以认为MVC模式是对表示层的进一步分层,通过C将数据显示与数据模型分类离,同样起到降低耦合性的作用,简化后续对程序的修改和扩展,并且是程序某一部分的重复利用成为可能。
(四)注意事项
- Servlet调用service,service调用dao
- Servlet不能直接调用dao
- Servlet不能去调用servlet
- Dao不能调用service和servlet
6. 多方法请求
1、目前一个servlet只能接受一个请求,项目是分模块开发的 2、用户模块,增删改查,需要5个servlet 3、使用多方法请求,可以让一个servlet来接收一个模块的请求 UserServlet 接收用户模块的所有请求<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="demo05?mark=insert">添加</a>
<a href="demo05?mark=query">查询</a>
<a href="demo05?mark=update">修改</a>
<a href="demo05?mark=delete">删除</a>
</body>
</html>
package com.ujiuye.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 多方法请求
* 一个servlet只有一个service方法,比如写项目,一个模块最低有增删改查4个功能
* 最低要创建5个servlet,项目有6个模块,30个servlet
*
* 用一个servlet去接收一个模块的所有请求
* UserServlet 去接收用户模块的所有请求
*
*/
public class Demo05Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
//先去获取mark参数
String mark = req.getParameter("mark");
//常量放到前面判断 防止空指针异常
if("insert".equals(mark))
//说明是添加的功能
insert(req,resp);
else if("query".equals(mark))
//查询
query(req,resp);
else if("update".equals(mark))
//修改
update(req,resp);
else if("delete".equals(mark))
//删除
delete(req,resp);
private void delete(HttpServletRequest req, HttpServletResponse resp)
System.out.println("删除的功能");
private void update(HttpServletRequest req, HttpServletResponse resp)
System.out.println("修改的功能");
private void query(HttpServletRequest req, HttpServletResponse resp)
System.out.println("查询的功能");
private void insert(HttpServletRequest req, HttpServletResponse resp)
System.out.println("执行添加的代码 调用service");
7. Servlet生命周期
(一)Servlet生命周期:从创建到销毁的全过程,共分为三个阶段
- 初始化init方法: 只会执行一次(启动tomcat的时候默认是不执行的,在访问的时候才会执行)
- 服务方法service: 可以执行多次
- 销毁方法destory: 只执行一次(停止服务器)
(二)测试Servlet的声明周期
1、创建一个servlet继承httpservlet,重写init,service,destroy方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="demo06">测试servlet生命周期</a>
</body>
</html>
ackage com.ujiuye.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Demo06Servlet extends HttpServlet
public Demo06Servlet()
System.out.println("无参构造方法执行了");
@Override
public void init() throws ServletException
//初始化方法 只会执行一次
System.out.println("init方法执行了");
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
//服务方法 可以执行多次
System.out.println("service方法执行了");
public void destroy()
//销毁的方法 只会执行一次
System.out.println("destroy方法执行了");
2、第一次访问servlet,实例化servlet,执行init方法,执行service
3、第二次访问servlet
4、停止服务器
通过以上案例发现Servlet默认情况下是在第一次访问时被创建并初始化的,为了减少内存的压力,我们 可否改变它的创建时机呢? 答案是肯定的。我们可以在web.xml文件中进行配置,使得Servlet在服务器启动时直接创建并初始化。 load-on-startup节点必须写在servlet节点的内部,且作为最后一个节点。取值必须是整数,如果是大于等于0的整数表示在服务器启动时就被创建并初始化,如果有多个Servlet设置了load-on-startup节点的 值,那么值越小越优先执行;如果是负数则表示第一次被访问时创建并初始化,也就是默认情况,可以省略不写。
总结:
1、分为三个阶段
(1)初始化阶段 会执行init方法 只会执行一次
默认是在第一次访问servlet时进行执行,可以在web.xml进行配置,配置<load-on-startup> 让servlet随着tomcat的启动而进行 初始化,配置的值大于0即可,配置为负数就是默认情况,当多个 servlet配置了<load-on-startup>值越小的优先执行
(2)服务阶段 执行service方法 会执行多次
(3)销毁阶段 会执行destroy方法 只会执行一次 点击停止服务器
2、注意点 访问servlet会实例化servlet对象,发现构造方法只会执行一次,说明对象只创建了一 次,即单例模式。8. Servlet映射方式(url-pattern)
1.第一种配置方式:/*
* 是通配符 例如:/hello、/admin/login、/*
/hello :<url-pattern>/hello<url-pattern>这个配置只拦截hello,其他的不拦截
/admin/login :层级目录的格式,请求的方式<url-pattern>/admin/login<url-pattern>
/* :拦截所有,无论是脚本、css、或者是单纯的一个页面,全部拦截
2. 第二种配置方式:*.?
*.do 表示特定的拦截方式,只拦截action中带.do的url(do可以换成任意字符)
<url-pattern>*.do</url-pattern>
3. URL的总结
servlet本身很特殊,因为前台的URL都会进入service方法或doGet、doPost方法,那么这些URL基本很少使用统配的形式,最常用的两种方式就是:/hello、/admin/login,而*.do的方式很少用。
正常情况下同一个业务实体的功能会对应一个servlet,例如:
用户管理UserServlet(用户所有的CRUD都在这个Servlet中执行,和页面无关)
权限控制RoleServlet(权限所有CRUD都在这个Servlet中执行)
9. Request对象
(1)Request理解
Request是由tomcat创建的。
Request对象是来获取请求信息的。
(2)Request对象的体系
ServletRequest 接口 javax.servlet
|^继承
HttpServletRequest 子接口 javax.servlet.http
(3)Request的作用
接收客户端的请求,获取请求中的信息。除了可以获取请求中携带的数据之外,还可以 获取比如主机地址、端口、请求方式、项目名称等一系列信息。
请求分类: 请求行、请求头、请求体。
1、获取请求行数据
请求行中,我们可以通过request对象的相应方法获取到比如协议名、服务名、端口号、项目名称、请求方式、参数列表等信息。
"协议名:" request.getScheme();
"服务器:" request.getServerName();
"端口号:" request.getServerPort();
"项目名:" request.getContextPath();
"请求方式:" request.getMethod();
"参数字符串:" request.getQueryString();
"项目名+具体地址:" request.getRequestURI();
"获取请求的URL:" request.getRequestURL();
"获取请求的URL:" request.getServletPath();
package com.ujiuye.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;
/**
* 请求头
* "协议名:" request.getScheme();
* "服务器:" request.getServerName();
* "端口号:" request.getServerPort();
* "项目名:" request.getContextPath();
* "请求方式:" request.getMethod();
* "参数字符串:" request.getQueryString();
* "项目名+具体地址:" request.getRequestURI();
* "获取请求的URL:" request.getRequestURL();
* "获取请求的URL:" request.getServletPath();
*/
@WebServlet("/demo09")
public class Demo09Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException
String scheme = req.getScheme();
System.out.println("scheme = " + scheme);
String serverName = req.getServerName();
System.out.println("serverName = " + serverName);
int serverPort = req.getServerPort();
System.out.println("serverPort = " + serverPort);
String contextPath = req.getContextPath();
System.out.println("contextPath = " + contextPath);
String method = req.getMethod();
System.out.println("method = " + method);
String queryString = req.getQueryString();
System.out.println("queryString = " + queryString);
String uri = req.getRequestURI();
System.out.println("uri = " + uri);
StringBuffer url = req.getRequestURL();
System.out.println("url = " + url);
String servletPath = req.getServletPath();
System.out.println("servletPath = " + servletPath);
2、获取请求头数据
请求头是当前对用户发送的数据的描述信息。
请求头信息在请求的时候不需要程序员手动添加,是浏览器发送的时候已经处理好的。
如果想查看请求头信息,也可以在Servlet中通过getHeader方法获取。
获取请求头数据的方法:
方法名 | 描述 |
String getHeader(String name) | 根据请求头的名称获取请求头信息 |
Enumeration getHeaderNames() | 返回此请求包含的所有头名称的枚举 |
package com.ujiuye.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.Enumeration;
/**
* 请求头
*/
@WebServlet("/demo10")
public class Demo10Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException
//通过请求头的key获取对应的value
String host = req.getHeader("Host");
System.out.println("host = " + host);
//获取所有头的key
Enumeration<String> en = req.getHeaderNames();
//遍历
while (en.hasMoreElements())
String name = en.nextElement();
//通过name获取value
String value = req.getHeader(name);
System.out.println(name + "..." + value);
3、获取请求正文数据
请求体就是请求中携带的数据,也就是我们需要获取的参数
获取请求参数的方法:
方法名 | 描述 |
String getParameter(String name) | 根据参数名获取参数值 |
String[] getParameterValues(String name) | 根据参数名获取参数值(可以是多个值) |
Enumeration getPatameterNames() | 获取所有的参数名 |
Map<String,String[]> getParameterMap() | 获取所有参数的map集合 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>请求体</title>
</head>
<body>
<!--
-->
<form action="demo11" method="get">
账号: <input type="text" name="username"> <br>
密码: <input type="password" name="password"> <br>
性别: <input type="radio" name="sex" value="男"> 男
<input type="radio" name="sex" value="女">女 <br>
爱好: <input type="checkbox" name="hobby" value="唱">唱
<input type="checkbox" name="hobby" value="跳">跳
<input type="checkbox" name="hobby" value="rap">rap
<input type="checkbox" name="hobby" value="打篮球">打篮球 <br>
学校: <select name="school">
<option value="小学">小学</option>
<option value="中学">中学</option>
<option value="大学">大学</option>
</select>
<br>
简介: <textarea name="mark" cols="30" rows="10"></textarea> <br>
<input type="submit" value="点我">
</form>
</body>
</html>
package com.ujiuye.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.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
/**
* 请求体
*/
@WebServlet("/demo11")
public class Demo11Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException
//获取文本框、密码框、隐藏框、日期框
//通过name值获取value 没有传递参数获取的是""
String username = req.getParameter("username");
System.out.println("username = " + username);
//单选框 通过name值获取被选中单选框的value值 没有选中获取的是bull
String sex = req.getParameter("sex");
System.out.println("sex = " + sex);
//复选框 getParameterValues获取 没有选中获取的是null
String[] hobbies = req.getParameterValues("hobby");
System.out.println(Arrays.toString(hobbies));
//下拉列表 通过name值获取被选中option的value值
String school = req.getParameter("school");
System.out.println("school = " + school);
//简介 通过name值获取
String mark = req.getParameter("mark");
System.out.println("mark = " + mark);
System.out.println("-----------------------------------------------------------------------");
//一次获取所有的参数
Map<String, String[]> map = req.getParameterMap();
Set<String> set = map.keySet();
for (String key : set)
System.out.println(key + "...." + Arrays.toString(map.get(key)));
System.out.println("-----------------------------------------------------------------------");
//获取所有的参数名
Enumeration<String> en = req.getParameterNames();
while (en.hasMoreElements())
System.out.println(en.nextElement());
10. Response对象
(1)Response理解
Response是由tomcat创建的
Response对象是来设置响应信息
(2)Response对象的体系
ServletResponse 接口
|^继承
HttpServletResponse 子接口
|^实现
org.apache.catalina.connector.ResponseFacade 实现类
(3)Response的作用
针对页面发送的请求做出数据响应,向页面输出信息,包括文本、图片、视频等。
响应分类: 响应行、响应头、响应体。
1、设置响应行
响应行中包含的信息:可以通过开发者模式F12-Network查看
由上图可知,响应行中包含协议和状态码
(1)HTTP协议
HTTP协议的特点:
1、 简单快捷。客户端向服务器请求时,只需要传送请求方法和路径。
2、灵活。HTTP协议允许传送任意格式的数据。正在传输的类型由content-type标明。
3、 无连接(长连接)。每个请求都建立一个连接,请求处理完毕并发送至客户端之后就断开连接。这样明显有其缺点,就是在需要连续发送请求时,需要为每一个请求单独的在此建立连接,很浪费时间和资源。
于是HTTP协议1.1版本之后引入了可持续连接,也就是在每个请求处理完毕后,他不会立刻就断开连接,而是在等待一段时间,如果在此期间又有新的请求过来,那么等待时间刷新,如果没有,则等待时间完毕后,连接关闭。
4、 无状态。是指协议对于每次请求的处理没有记忆能力,他不知道之前是否已经访问过,不保留访问痕迹。主要目的是保证数据传输的安全性。
HTTP的版本:
0.9 -适用于各种数据信息的简洁快速协议,仅限于传输文本
1.0 -该协议对每一次请求/响应建立并拆除一次连接,不支持长连接
1.1 -在1.0版本的基础上支持长连接keep-alive
HTTPS:以安全为目标的HTTP通道,简单的讲是HTTP的安全版本,即HTTP下加入SSL层,简称HTTPS。
其中HTTPS的安全基础为SSL,因此通过它的传输内容都是经过SSl加密的,他的主要作用可以分为两种:
- 建立一个信息安全通道来保证数据传输的安全
- 确保网站的真实性,凡是使用了HTTPS的网站,都可以通过点击浏览器地址栏的锁头标志来查看网站认证之后的真实信息。
HTTP底层协议:TCP协议
TCP的特点:面向连接,保证可靠性
TCP三次握手,因为HTTP是基于TCP协议的,所以也可以认为是HTTP的三次握手:
第一次握手:客户端向服务器发送一个请求消息,服务端收到信息后知道自己与客户端是可以连接成功的;
第二次握手:此时客户端并不知道服务端是否已经接收到了他的请求,所以服务端接收到消息后要向客户端做出响应,客户端的到服务端的反馈后,才确定自己与服务器是可以连接上的。
第三次握手:客户端确认与服务器建立连接后,才开始向服务器发送数据。
(2)状态码
响应状态码200表示响应成功,不同的状态码代表不同的含义。
1xx -信息,服务器收到请求,需要请求者继续执行操作
2xx -成功,操作被成功接收并处理
3xx -重定向,需要进一步的操作已完成请求
4xx -客户端错误,请求包含语法错误或无法请求
5xx -服务器错误,服务器在处理请求的过程中发生了错误
几种常见的状态码:
200 请求和响应成功
404 客户端的错误、浏览器url地址写错误、tomcat没有启动、servlet没有拦截路径
500 服务器错误、后台代码有问题、查看控制台寻找错误信息、java代码或者jsp代码出错、空指针异常
案例实现:
package com.ujiuye.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;
@WebServlet("/demo01")
public class Demo01Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
//设置错误的状态码和提示消息 测试一下 一般不用
resp.sendError(500,"你自己写的烂代码心里没有b数吗?");
2、设置响应头
当我们在浏览器中打开network工具时,每一次的请求响应数据,都可以被捕捉到,而在内容中Response Headers中的的内容就是当前这一次请求响应的响应头信息。
设置响应头信息可以通过以下两种方法: response.setHeader("Content-Type", "text/html;charset=utf-8"); response.addHeader("Content-Type", "text/html;charset=utf-8"); 二者的区别: response.setHeader(String name, String value);一个关键字对应一个值,如果设置 了多个值,则会覆盖。 name zhangsan name lisi response.addHeader(String name, String value);一个关键字可以对应多个值 在实际开发中,同一个响应头信息只会对应一个值,所以在使用时一般没什么区别。 案例实现:
package com.ujiuye.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;
@WebServlet("/demo02")
public class Demo02Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
resp.setHeader("name","zhangsan");
resp.setHeader("name","lisi");
resp.addHeader("age","17");
resp.addHeader("age","18");
3、设置响应体
响应的数据就是响应体。响应对象response在返回数据、响应数据的时候,会将一些HTML、text、流数据等信息通过响应主体返回给页面,而响应体返回给页面,而响应体绝大多数都是文本类型。
响应数据需要通过流来进行数据传输,而response自带的流有两个:
Response.getWriter() ==>PrintWriter 输出文本信息
Response.getOutputStream ==> ServletOutputStream 输出字节信息
注意:这两个流不能同时共存
需求:演示两个流不能同时共存
案例实现:
package com.ujiuye.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
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.io.PrintWriter;
@WebServlet("/demo03")
public class Demo03Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException
//设置请求和响应的字符集
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
/*//1、第一种响应流
PrintWriter w = resp.getWriter();
w.print("zhangsan");
//可以识别标签和样式
w.print("<h2 style='color:red;'>李四</h2>");*/
/*
2、第二种响应体 处理字节信息 处理字符没有优势
1、通过字符串的构造器将张三转成iso8859-1
2、配置tomcat的vm -Dfile.encoding=utf-8
才能在页面上看到汉字张三
*/
ServletOutputStream sos = resp.getOutputStream();
sos.print(new String("张三".getBytes(),"iso8859-1"));
案例:响应表格
package com.ujiuye.servlet;
import com.ujiuye.bean.Student;
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.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
* 响应表格
*/
@WebServlet("/demo04")
public class Demo04Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException
//设置请求和响应的字符集
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
List<Student> lists = new ArrayList<>();
lists.add(new Student(1,"张三1","男","吃饭","大学"));
lists.add(new Student(2,"张三2","男","吃饭","大学"));
lists.add(new Student(3,"张三3","男","吃饭","大学"));
lists.add(new Student(4,"张三4","男","吃饭","大学"));
lists.add(new Student(5,"张三5","男","吃饭","大学"));
lists.add(new Student(6,"张三6","男","吃饭","大学"));
PrintWriter w = resp.getWriter();
w.print("<table border=1 width=400px>");
w.print("<tr><td>学号</td><td>姓名</td><td>性别</td><td>爱好</td><td>学校</td></tr>");
//遍历lists集合 每遍历一次 拼接一行tr
for (Student stu : lists)
w.print("<tr>");
w.print("<td>" + stu.getSid() + "</td>");
w.print("<td>" + stu.getSname() + "</td>");
w.print("<td>" + stu.getSex() + "</td>");
w.print("<td>" + stu.getHobby() + "</td>");
w.print("<td>" + stu.getSchool() + "</td>");
w.print("</tr>");
w.print("</table>");
11. ServletContext对象
(1)ServletContext理解
当 Tomcat 启动时,Tomcat 会为每个 Web 应用创建一个唯一的 ServletContext 对象代 表当前的 Web 应用,该对象封装了当前 Web 应用的所有信息。可以利用该对象获取整个Web 应用程序的初始化信息、读取资源文件等。它与Servlet是一对多的关系,其内部封装的信息可以被同一web 应用内的所有Servlet共享。 servletcontext是全局的配置对象 代表的web应用 被所有的servlet共享(2)ServletContext的获取
获取方式有两种:1. request.getServletContext();
2. this.getServletContext();
(3)ServletContext的作用
1.域对象:共享数据
ServletContext是一个全局作用域对象,在整个Web应用内所有用户所有请求的数据(周期长)。 request域对象,作用范围是一次请求package com.ujiuye.servlet;
import com.ujiuye.bean.Student;
import javax.servlet.ServletContext;
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.HashMap;
import java.util.Map;
/**
* 获取ServletContext 和域对象
*/
@WebServlet("/demo05")
public class Demo05Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException
//获取 servletContext在web应用是唯一的
/*ServletContext sc1 = getServletContext();
ServletContext sc2 = this.getServletContext();
System.out.println(sc1 == sc2);//true*/
/**
* ServletContext和request都是域对象
* 什么是域对象 可以向对象中存值取值的对象,并且拥有作用域范围
* 1、域对象如何存值、取值、移除值
* 存 setAttribute(string,object)
* 取 getAttribute(key)
* 移除 removeAttribute(key)
* 2、ServletContext和request存取移除值的方式都是一样的
* 3、作用域范围
* ServletContext是整个web应用
* request作用域范围是一次请求
* 4、获取文件的真实路径 获取的是工件在本地的真实路径
* getRealPath
*/
ServletContext sc = getServletContext();
//存值
Student s = new Student();
s.setSname("张三");
sc.setAttribute("student",s);
//取值 通过key获取value
Student student = (Student) sc.getAttribute("student");
System.out.println("student = " + student);
//移除值
sc.removeAttribute("student");
System.out.println(sc.getAttribute("student"));
String realPath = sc.getRealPath("/");
System.out.println("realPath = " + realPath);
ServletContext作用域:
1、在Demo06Servlet中存值
package com.ujiuye.servlet;
import javax.servlet.ServletContext;
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;
/**
* 测试ServletContext和request的作用范围
*/
@WebServlet("/demo06")
public class Demo06Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException
//设置请求和响应的字符集
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
ServletContext sc = getServletContext();
sc.setAttribute("name","张三");
req.setAttribute("age",17);
2、 在Demo07Servlet中取值
package com.ujiuye.servlet;
import javax.servlet.ServletContext;
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;
/**
* 测试ServletContext和request的作用范围
*/
@WebServlet("/demo07")
public class Demo07Servlet extends HttpServlet
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException
//设置请求和响应的字符集
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//获取servletContext中的值
ServletContext sc = getServletContext();
Object name = sc.getAttribute("name");
Servlet详解
6、Servlet
6.1、Servlet简介
- Servlet就是sun公司开发动态web的一门技术
- Sun在这些API中提供了一个接口,叫做:Servlet,如果你想开发一个Servlet程序,只需要完成两个小步骤:
- 编写一个类,实现Servlet接口
- 把开发好的java类部署到web服务器中。
把实现了Servlet接口的Java程序叫做,Servlet
6.2、HelloServlet
Servlet接口Sun公司有两个默认的实现类:HttpServlet,GenericServlet
-
构建一个Maven项目,删掉里面的src目录,以后我们的学习就在这个项目里面建立Module;这个空的工程就是Maven主工程;
-
关于Maven父子工程的理解:
父项目中会有
<modules>
<module>servlet-01</module>
</modules>
子项目会有
<parent>
<artifactId>javaweb-02-servlet</artifactId>
<groupId>com.kuang</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
父项目中的ajva子项目可以直接使用
son extends father
-
Maven环境优化
- 修改web.xml为最新的
- 将maven的结构搭建完整
-
编写一个Servlet程序
-
编写一个普通类
-
实现Servlet接口,这里我们直接继承HttpServlet
public class HelloServlet extends HttpServlet
//由于get或者post只是请求实现的不同的方式,可以相互调用,业务逻辑都一样
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
//ServletOutputStream outputStream = resp.getOutputStream();
PrintWriter writer = resp.getWriter();//响应流
writer.print("Hello,Servlet");
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
this.doGet(req, resp);
-
编写Servlet的映射
为什么需要映射:我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需要给它一个浏览器能够访问的路径;
<!--注册Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求(映射)路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
-
配置Tomcat
注意:配置项目发布的路径就可以了
-
启动测试
url-pattern必须为/hello,其中/不可省略
原理
6.3、Servlet原理
Servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会:
6.4、Mapping问题
-
一个Servlet可以指定一个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
-
一个Servlet可以指定多个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello4</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello5</url-pattern>
</servlet-mapping>
-
一个Servlet可以指定通用映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
-
默认请求路径
<!--默认请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
-
指定一些后缀或者前缀等等...
<!--可以自定义后缀实现请求映射
注意点,*前面不能加项目映射的路径
hello/sajdlkajda.qinjiang-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.qinjiang</url-pattern>
</servlet-mapping>
-
优先级问题
指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求;
<!--404-->
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.kuang.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
6.5、ServletContext
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用;
1、共享数据
原理:
我在这个Servlet中保存的数据,可以在另外一个Servlet中拿到;
public class HelloServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
//this.getInitParameter(); 初始化参数
//this.getServletConfig(); Servlet配置
//this.getServletContext(); Servlet上下文
ServletContext context = this.getServletContext();
String username = "秦疆"; //数据
context.setAttribute("username",username); //将一个数据保存在ServletContext中,名字为:username,值:username
public class GetServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
ServletContext context = this.getServletContext();
String username = (String) context.getAttribute("username");
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
resp.getWriter().print("名字"+username);
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
doGet(req, resp);
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getc</servlet-name>
<servlet-class>com.kuang.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getc</servlet-name>
<url-pattern>/getc</url-pattern>
</servlet-mapping>
测试访问结果;
2、获取初始化参数
<!--配置一些web应用的初始化参数-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
3、请求转发
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
ServletContext context = this.getServletContext();
System.out.println("进入了ServletDemo04");
//RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); //转发的请求路径
//requestDispatcher.forward(req,resp); //调用forward实现请求转发;
context.getRequestDispatcher("/gp").forward(req,resp);
4、读取资源文件
Properties
- 在java目录下新建properties
- 在resours目录下新建properties
发现:都被打包到了同一个路径下:classes,我们俗称这个路径为classpath;
思路:需要一个文件流;
username=root
password=123456
public class ServletDemo05 extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.getWriter().print(user+":"+pwd);
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
doGet(req, resp);
访问测试即可ok;
6.6、HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的HttpServletResponse对象;
- 如果要获取客户端请求过来的参数:找HttpServletRequest
- 如果要给客户端响应一些信息:找HttpServletResponse
1、简单分类
负责向浏览器发送数据的方法
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
相应的状态码
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
2、下载文件
- 向浏览器输出消息 (getWriter...)
- 下载文件
- 要获取下载文件的路径
- 下载的文件名是啥?
- 设置想办法让浏览器能够支持下载我们需要的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端!
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
// 1. 要获取下载文件的路径
String realPath = "D:\\\\environment\\\\java_project\\\\javaweb-02-servlet\\\\response\\\\target\\\\response\\\\WEB-INF\\\\classes\\\\王.png";
System.out.println("下载文件的路径:"+realPath);
// 2. 下载的文件名是啥?
String fileName = realPath.substring(realPath.lastIndexOf("\\\\") + 1);
// 3. 设置想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件名URLEncoder.encode编码,否则有可能乱码
resp.setHeader("Content-Disposition","attachment;filename"+URLEncoder.encode(fileName,"UTF-8"));
// 4. 获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
// 5. 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
// 6. 获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
// 7. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端!
while ((len=in.read(buffer))>0)
out.write(buffer,0,len);
in.close();
out.close();
3、验证码功能
验证码怎么来的?
- 前端实现
- 后端实现,需要用到java的图片类,生成一个图片
public class ImageServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
//如何让浏览器3秒自动刷新一次;
resp.setHeader("refresh","3");
//在内存中创建一个图片
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_BGR);
//得到图片
Graphics2D g = (Graphics2D) image.getGraphics(); //笔
//设置图片的背景颜色
g.setColor(Color.WHITE);
g.fillRect(0,0,80,20);
//给图片写数据
g.setColor(Color.BLUE);
g.setFont(new Font(null,Font.BOLD,20));
g.drawString(makeNum(),0,20);
//告诉浏览器,这个请求用图片的方式打开
resp.setContentType("image/jpeg");
//网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//把图片写给浏览器
ImageIO.write(image, "jpg", resp.getOutputStream());
//生成随机数
private String makeNum()
Random random = new Random();
String num = random.nextInt(9999999) + "";//转换为字符串
StringBuffer sb = new StringBuffer(); //可变长字符串
for (int i = 0; i < 7-num.length(); i++)
sb.append("0");
num = sb.toString() + num;
return num;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
doGet(req, resp);
4、实现重定向
B一个web资源收到客户端A请求后,B他会通知A客户端去访问另外一个web资源C,这个过程叫重定向
常见场景:
- 用户登录
void sendRedirect(String var1) throws IOException;
测试:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
/*
resp.setHeader("Location","/r/img");
resp.setStatus(302);
*/
resp.sendRedirect("/r/img"); //重定向,需要是该程序下的完整路径
面试题:请你聊聊重定向和转发的区别?
相同点
- 页面都会实现跳转
不同点
- 请求转发的时候,url不会产生变化
- 重定向的时候,url地址栏会发生变化
获取表单信息并跳转至新的页面
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>success</h1>
</body>
</html>
====================================
<html>
<body>
<h2>Hello World!</h2>
<%--这里提交的路径,需要寻找到项目的路径--%>
<%--$pageContext.request.contextPath代表当前的项目--%>
<form action="$pageContext.request.contextPath/login" method="get">
用户名:<input type="text" name="username"> <br>
密码:<input type="password" name="password"> <br>
<input type="submit">
</form>
</body>
</html>
public class RequestTest extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
//处理请求
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username+":"+password);
//重定向的时候,一定要注意路径问题,否则404
resp.sendRedirect("/r/success.jsp");
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
doGet(req, resp);
6.7、HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,Http请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息
获取前端传递的参数,请求转发
public class LoginServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobby = req.getParameterValues("hobby");
System.out.println("=====================================");
//后台接收中文乱码问题
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobby));
System.out.println("=====================================");
//通过请求转发
//这里的 / 代表当前的web应用
req.getRequestDispatcher("/success.jsp").forward(req,resp);
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
doGet(req, resp);
面试题:请你聊聊重定向和转发的区别?
相同点
- 页面都会实现跳转
不同点
- 请求转发的时候,url不会产生变化 307
- 重定向的时候,url地址栏会发生变化 302
作者:wangyudong
本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
以上是关于servlet详解的主要内容,如果未能解决你的问题,请参考以下文章