Servlet 是Java Server Applet的简称,称为小服务器程序,用Java编写的服务器端程序,主要功能交互式地浏览和修改数据,生成动态Web内容。
Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
Servlet编程需要使用到javax.servlet 和 javax.servlet.http两个包下面的类和接口,在所有的类和接口中,javax.servlet.Servlet 接口最为重要。所有的servlet程序都必须实现该接口或者继承实现了该接口的类。javax.servlet.ServletConfig;
javax.servlet.ServletException;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
javax.servlet.http.HttpSession;
javax.servlet.http.Cookie
GenericServlet抽象类,实现了 Servlet, ServletConfig, Serializable三个接口,使编写 servlet 变得更容易。它提供生命周期方法 init 和 destroy 的简单实现,要编写一般的 servlet,只需重写抽象service 方法即可。
HttpServlet类,是【继承GenericServlet的基础上进一步的扩展】,提供将要被子类化以创建适用于 Web 站点的 HTTP servlet 的抽象类。HttpServlet 的子类至少必须重写一个方法,该方法通常是以下这些方法之一:
doGet,如果 servlet 支持 HTTP GET 请求
doPost,用于 HTTP POST 请求
doPut,用于 HTTP PUT 请求
doDelete,用于 HTTP DELETE 请求
init 和 destroy,用于管理 servlet 的生命周期内保存的资源 ,当服务器关闭,执行destroy方法
getServletInfo,servlet 使用它提供有关其自身的信息
一般我们只需要重写doPost和doGet方法即可。
@WebServlet(name = "MyServlet",value = "myservlet")
public class MyServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//在doGet方法里面调用doPost方法
doPost(request, response);
}
}
web开发出现乱码的问题主要是两个地方:一个是从浏览器表单中接收的数据,一个是服务器向浏览器发送的数据。
接收数据
接收数据使用的是【getParameter】方法,要在doPost方法内书写。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String request.getParameter(String var1); //var1是前端的变量名,或者表单的name, 返回的是value值
String[] getParameterValues(String var1); //返回的是数组,一般用于表单的多项选择,获取值
}
但是从前端获取的是utf-8编码的value,而服务器的request对象使用的是ISO8859-1(西欧编码集)这个字符编码来接收数据,浏览器和服务器沟通的编码不一致因此才会产生中文乱码的,所以使用【setCharacterEncoding】方法设置request编码
request.setCharacterEncoding("utf-8");
向浏览器响应数据
服务器向浏览器进行数据反馈响应,使用的是字节流和字符流的方法。
注意:字节流和字符流方法,只能二选一,不可同时用,一般传输音频、视频等需要字节流,文本使用字符流
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter writer = response.getWriter();//字符流
ServletOutputStream os = response.getOutputStream();//字节流
}
这样输出的结果是乱码,浏览器识别不到返回的中文是什么编码格式,就会默认使用GB2312,如果返回的是UTF-8格式的那么在浏览器上就会显示乱码的问题,问题出在浏览器上。需要使用【setContentType】方法设置response编码
//设置响应内容类型及编码,在响应报文中添加一个响应头 content-type:text/html;charset=utf-8
response.setContentType("text/html;charset=utf-8");
必须配置servlet的url,用于html表单里面的【action】属性。
配置方式主要有两种:一种是注解,一种是配置WEB-INF下的【web.xml】文件
注解类 【@WebServlet】,放到Servlet类的上面,主要属性:
name:serlvet名字 (可选)
value: 配置url路径,必须配置,value是数组,可以匹配多个url
urlPatterns:配置url路径 ,数组,可以匹配多个url,和value作用一样,不能同时使用
loadOnStartup:配置Servlet的创建的时机, 如果是0或者正数 启动程序时创建,如果是负数,则浏览器访问时创建,数子越小优先级越高,主要用于【init】方法
initParams:配置Servlet的初始化参数,可以在【init】方法中调取
Servlet中的url路径,共有四种方式:
注意有精确匹配优先精确匹配,否则模糊匹配
//必须有斜杠
@WebServlet(name = "MyServlet5",value = "/myservlet5")
@WebServlet(name = "MyServlet4",urlPatterns = "*.do")
/*匹配所有请求,包含服务器的所有资源,但会导致静态网页html和jsp无法访问*/
@WebServlet(name = "MyServlet4",urlPatterns = "/*")
/*匹配所有请求,包含服务器的所有资源,不包括.jsp,但会导致静态网页html无法访问*/
@WebServlet(name = "MyServlet4",urlPatterns = "/")
/*可以参考D:\TomCat\apache-tomcat-8.5.45\conf\web.xml下的官方web.xml文件*/
/*对于导致静态网页html无法访问的问题,可以通过配置web.xml文件来解决,*/
/*配置默认类,然后在映射中使用通配符【*.html】来给html文件放行,从而可以访问*/
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
/*添加servlet节点*/
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
</servlet>
/*添加servlet-mapping节点*/
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>
在html静态网页文件的form表单中的url路径书写格式
<body>
<h2>用户信息h2>
<form action="myservlet5" method="post" enctype="application/x-www-form-urlencoded">
姓名<input type="text" name="username"><br/>
年龄<input type="text" name="age"><br/>
地址<input type="text" name="address"><br/>
<input type="submit" value="提交"><br/>
form>
body>
关于【initParams】属性
可以在注解中设置参数,然后在init方法内调用,该属性是数组,可以传入多个参数,传参数需要再调用【@WebInitParam】注解
@WebServlet(value = "/myservlet",initParams = {@WebInitParam(name = "username", value = "张三"), @WebInitParam(name = "age", value = "20")})
public class MyServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
String username = config.getInitParameter("username");
String age = config.getInitParameter("age");
System.out.println("姓名:" + username + ",年龄:" + age);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("我的servlet......");
}
}
除了注解的方式,还有一种常用方式是通过配置【WEB-INF】下的【web.xml】文件,分为两个步骤,一个是添加servlet类节点,找到类位置,执行servlet类方法;一个是添加servlet-mapping映射节点,设置url路径,保证浏览器可以访问。
注意:1.两个步骤的【servlet-name】必须一致;2. 在【
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>myservlet3servlet-name>
<servlet-class>com.servlet.MyServlet3servlet-class>
<init-param>
<param-name>nameparam-name>
<param-value>李四param-value>
init-param>
<init-param>
<param-name>addressparam-name>
<param-value>北京param-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>myservlet3servlet-name>
<url-pattern>/myservlet3url-pattern>
servlet-mapping>
web-app>
与继承的方法基本一致,但是需要实现多个方法,比较麻烦。
@WebServlet(name = "MyServlet2",urlPatterns = "/myservlet2",loadOnStartup = 0,
initParams = {@WebInitParam(name = "username", value = "张三"),@WebInitParam(name = "age", value = "20")})
public class Myservlet2 implements Servlet {
//注解:初始化servlet,loadOnStartup为-1,表示浏览器访问的时候初始化,0或正数表示创建的时候初始化
//注解:initParams 初始化数组
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("初始化servlet:init");
//获取注解中的initParams初始化数组中的参数
String username = servletConfig.getInitParameter("username");
String age = servletConfig.getInitParameter("age");
System.out.println("姓名:" + username + ",年龄" + age);
}
//获取servlet配置
@Override
public ServletConfig getServletConfig() {
System.out.println("获取servlet配置");
return null;
}
//服务方法,处理请求的方法,相当于doPost和doGet
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("服务方法执行:service()");
}
//获取servlet基本信息
@Override
public String getServletInfo() {
System.out.println("获取servlet基本信息:作者、版权等");
return null;
}
//销毁
@Override
public void destroy() {
System.out.println("servlet销毁,当服务器停止的时候调用");
}
}
servlet可以获取浏览器表单数据,然后再利用数据库操作,可以将表单数据导入数据库,完成前后端的合并。
//激活码工具
public class ActiveCodeUtils {
public static String createActiveCode() {
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmssSSS");
String s1=sdf.format(date);
String s2=Integer.toHexString(new Random().nextInt(900)+100);
return s1+s2;
}
}
因为每次请求都会创建一个线程,如果多人同时请求,那么就会存在多个线程操作同一个Servlet对象,那么如果在对应的方法中操作了成员变量,就有可能产生线程安全的问题。‘’
解决办法:
实例化阶段是Servlet生命周期中的第一步,由Servlet容器调用Servlet的构造器创建一个具体的Servlet对象的过程。而这个创建的时机可以是在容器收到针对这个组件的请求之后,即用了才创建;也可以在容器启动之后立刻创建实例,而不管此时Servlet是否使用的上。使用如下代码可以设置Servlet是否在服务器启动时就执行创建
Servlet在被加载实例化之后,必须要初始化它。在初始化阶段,init()方法会被调用。这个方法在javax.servlet.Servlet接口中定义。其中,方法以一个ServletConfig类型的对象作为参数。ServletConfig对象由Servlet引擎负责创建,从中可以读取到事先在web.xml文件中通过
一般情况下,init方法不需要编写,因GenericServlet已经提供了init方法的实现,并且提供了getServletConfig方法来获得ServletConfig对象。
注:init方法只被执行一次
Servlet被初始化以后就处于能够响应请求的就绪状态。每个对Servlet的请求由一个ServletRequest对象代表,Servlet给客户端的响应由一个ServletResponse对象代表。当客户端有一个请求时,容器就会将请求与响应对象转给Servlet,以参数的形式传给service方法。service方法由javax.servlet.Servlet定义,由具体的Servlet实现
HttpServlet将service方法拆分了。doGet和doPost
Servlet容器在销毁Servlet对象时会调用destroy方法来释放资源。通常情况下Servlet容器停止或者重新启动都会引起销毁Servlet对象的动作,但除此之外,Servlet容器也有自身管理Servlet对象的准则,整个生命周期并不需要人为进行干预
超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式。
HTTP协议的主要特点如下:
1.支持客户端/服务器模式。
2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3.灵活:HTTP允许传输任意类型的数据对象。传输的类型由Content-Type加以标记。
4.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
5.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
Http协议的通信
HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤:
1、 建立TCP连接
在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更高层协议的连接。因此,首先要建立TCP连接,一般TCP连接的端口号是80
2、 浏览器向Web服务器发送请求命令
一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令
例如:GET /sample/hello.html HTTP/1.1
3、 浏览器发送请求头信息
浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
4、 Web服务器应答
客户机向服务器发出请求后,服务器会客户机回送应答,
HTTP/1.1 200 OK
应答的第一部分是协议的版本号和应答状态码
5、 Web服务器发送应答头信息
正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。
6、 Web服务器向浏览器发送数据
Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据
7、 Web服务器关闭TCP连接
一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码
Connection:keep-alive
TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽
关于http协议问题,参考文章《HTTP请求报文和HTTP响应报文》——华山大师兄
HTTP请求报文和响应报文
HTTP请求报文 包括 请求行 请求头 空行 请求体
HTTP响应报文 包括 状态行、消息报头、空行、响应正文。
请求头:
accept:浏览器通过这个头告诉服务器,它所支持的数据类型
Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
Accept-Encoding:浏览器通过这个头告诉服务器,支持的压缩格式
Accept-Language:浏览器通过这个头告诉服务器,它的语言环境
Host:浏览器通过这个头告诉服务器,想访问哪台主机
If-Modified-Since: 浏览器通过这个头告诉服务器,缓存数据的时间
Referer:浏览器通过这个头告诉服务器,客户机是哪个页面来的 防盗链
Connection:浏览器通过这个头告诉服务器,请求完后是断开链接还是保持链接
响应头
Location: 服务器通过这个头,来告诉浏览器跳到哪里 重定向
Server:服务器通过这个头,告诉浏览器服务器的型号
Content-Encoding:服务器通过这个头,告诉浏览器,数据的压缩格式
Content-Length: 服务器通过这个头,告诉浏览器回送数据的长度
Content-Language: 服务器通过这个头,告诉浏览器语言环境
Content-Type:服务器通过这个头,告诉浏览器回送数据的类型
Refresh:服务器通过这个头,告诉浏览器定时刷新
Content-Disposition: 服务器通过这个头,告诉浏览器以下载方式打数据
Transfer-Encoding:服务器通过这个头,告诉浏览器数据是以分块方式回送的
Expires: -1 控制浏览器不要缓存
Cache-Control: no-cache 控制浏览器不要缓存
Pragma: no-cache 控制浏览器不要缓存
状态行
状态代码由3位数字组成,表示请求是否被理解或被满足。
状态描述:
状态描述给出了关于状态代码的简短的文字描述。
状态代码的第一个数字定义了响应的类别,后面两位没有具体的分类。
第一个数字有五种可能的取值:
1xx: 指示信息—表示请求已接收,继续处理。
2xx: 成功—表示请求已经被成功接收、理解、接受。
3xx: 重定向—要完成请求必须进行更进一步的操作。
4xx: 客户端错误—请求有语法错误或请求无法实现。
5xx: 服务器端错误—服务器未能实现合法的请求。
状态代码 | 状态描述 | 说明 |
---|---|---|
200 | OK | 客户端请求成功 |
301 | 永久重定向 | |
302 | 临时重定向 | |
400 | Bad Request | 由于客户端请求有语法错误,不能被服务器所理解 |
401 | Unauthonzed | 请求未经授权。这个状态代码必须和WWW-Authenticate报头域一起使用 |
403 | Forbidden | 服务器收到请求,但是拒绝提供服务。服务器通常会在响应正文中给出不提供服务的原因 |
404 | Not Found | 请求的资源不存在,例如,输入了错误的URL |
500 | Internal Server Error | 服务器发生不可预期的错误,导致无法完成客户端的请求 |
503 | Service Unavailable | 服务器当前不能够处理客户端的请求,在一段时间之后,服务器可能会恢复正常 |