Servlet
Servlet是Java Servlet的简称,称为小服务程序或者服务连接器,用java编写的服务端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态WEB内容.
1.Servlet简介
Servlet是由sun公司制定的一种用来扩展Web服务器功能的组件规范.
1.扩展Web服务器功能
Web服务器只能够处理静态资源的请求(需要事先将html文件写好),不能够处理动态资源的请求之后,如果需要计算,则交给Servlet来处理,常见的Web服务器有apache,iis,nginx等.
2.组件规范
1.什么是组件
符合某种规范,实现部分功能,并且需要部署到相应的容器里面才能运行的软件模块.Servlet就是一个组件,需要部署到Servlet容器里面才能运行.
2.什么是容器
符合规范,提供组件的运行环境的程序.Tomcat就是一个Servlet容器,为Servlet提供运行环境(提供网络相关的服务)
2.Servlet入门案例
1.入门案例
public class HelloServlet extends HttpServlet {
/**
1.Servlet容器接收到请求之后,会调用Servlet的service方法来处理请求
2. Servlet容器会将请求包数据中的数据解析出来,然后将解析到的数据添加到
request对象里面,同时还会创建一个response对象.
3.容器会将request和response对象作为参数传过来,开发人员只需要调用request和
response对象的方法即可
*/
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("helloServlet的service方法");
//设置响应类型,用于设置content-type消息头的值.浏览器收到之后,就会按照指定的类型去解析
res.setContentType("text/html");
res.setCharacterEncoding("utf-8");
//获取输出流
PrintWriter writer = res.getWriter();
//将处理结果写到reponse对象里面,容器会从reponse对象中获取处理结果
//然后创建一个响应数据包发给浏览器
writer.println("你好");
//关闭输出流
writer.close();
}
}
web.xml配置文件
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.fjp.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/helloservlet</url-pattern>
</servlet-mapping>
2.项目打包原理
1.打包(即创建一个具有如下结构的文件夹)
webapp //appname应用程序名
WEB-INF
classes//存放.class文件
lib //存放.jar文件该文件夹可选
web.xml //部署描述文件
2.部署.将上述创建好的整个文件夹拷贝到容器相应的位置 .可以将整个文件夹使用jar命令先压缩成.war为后缀的文件然后再拷贝.
说明:IDEM会在容器指定的位置创建符合servlet规范的文件夹(默认以工程名作为应用名),并且会将.java文件编译成.class文件并放到classes文件夹下,就是说IDEM会简化编译/打包/部署/运行的过程.
3.状态码
开发中常见的状态码问题:
状态码 | 含义 | 产生原因 |
---|---|---|
404 | 服务器依据请求路径找不到对应资源.404是一个状态码,状态码是一个三位数字,表示服务器处理请求的状态 | 请求路径写错 |
405 | 找不到处理方法 | 没有正确重写HttpServlet类中的seivice()方法 |
500 | 系统出错 | 没有严格按照规范写代码(包括部署描述文件写错).代码不严谨,比如对请求参数不做检查直接做类型转换 |
3.Servlet运行原理
1.Servlet执行过程分析
在浏览器地址栏输入https://ip:port/appname/url请求,具体步骤如下
- 浏览器根据ip和port建立连接
- 浏览器将相关数据放到请求数据包里面,然后发送给服务器
- 服务器解析请求数据包,将解析的数据添加到request对象,同时创建一个response对象
- 服务器创建Servlet对象,然后调用该对象的service方法,服务器会将request和response对象作为参数传递过来,开发者只需调用这两个对象的方法即可,不用考虑网络相关的问题.比如读取请求数据包中的数据,只需要调用request对象的方法就可以了,类似的要向浏览器发送处理结果,只需要调用response对象的方法就可以了
- 服务器从response对象获取得到处理结果,然后创建响应数据包发送给浏览器.
- 浏览器解析响应数据包,生成相应页面
2.Servlet执行流程图
4.HTTP协议
1.HTTP协议简介
Http协议是一种网络协议,规定了浏览器与WEB服务器之间如何通信以及相应的数据包的结构
TCP/IP协议:属于传输层和网络层协议,保证数据可靠传输,Http协议属于应用层协议,需要依赖TCP/IP协议来传递数据包
浏览器和web之间服务器之间如何通信:
- 建立连接
- 发送请求
- 发送响应
- 关闭连接
特点:“一次请求,一次连接”.这样的好处是,服务器可以利用有限的连接尽可能多的请求服务
2.数据包结构
1.请求数据包
1.请求行:请求方式 请求资源路径 协议和版本
2.消息头:消息头是一些键值对,浏览器和服务器之间可以通过消息头来传递一些特定的信息
3.实体内容:只有当请求方式为post时,实体内容才会有数据.请求方式为get时,实体内容为空
2.响应数据包
1.状态行:协议和版本,状态码,状态描述
200:正常
404:依据请求路径找不到对应的资源
405:找不到处理方法
500:系统出错
2.若⼲消息头:服务器也可以发送⼀些消息头给浏览器。⽐如,可以发送"content-type"消息头告诉浏览器,服务器返回的数据类型。
3.实体内容:程序的处理结果,浏览器会解析出来,⽣成相应的⻚⾯
3.请求方式
1.GET请求
1.那些情况下,浏览器会发送GET请求
1.在浏览器地址框直接输入某个地址
2.点击链接
3.表单默认的提交方式
2.特点:GET请求会将请求参数信息添加到请求资源路径的后面,只能提交少量的数据给服务器
3.说明:
- 请求行只能存放大约2k左右的数据。会将请求参数显示在浏览器地址栏,不安全。
- 有些网络设备,比如路由器就会记录包含了请求参数的请求地址信息
2.POST请求
1.哪些情况下,浏览器会发送破石头请求
将form表单的method属性的属性值设置为post
2.特点:会将请求参数添加到实体内容里面,可以提交大量的数据给服务器,不会将请求参数显示在浏览器地址栏,相对安全
3.说明:HTTP协议并不会堆数据加密,所以对于铭感数据需要进行加密处理(使用HTTP协议)
5.获取请求参数值
1.获取请求参数的方式
1.单个参数值
1.使用API:String request.getParameter(String paramName)
2.语法说明:
- 如果请求参数名写错,则获取的是null
- 表单提交时。如果不填写任何数据则获取的时“”值
2.多个参数值
1.使用API:String[] request。getparameterValues(String parameName);
2.语法说明:
- 当多个请求参数名相同时,使用此方法
- 对于多选框和单选框,如果一个都不选,则获取的时是null值
2.获取情求参数值案例
1.需求
1.项目需求分析
服务端获取⽤户提交的⽤户名、兴趣爱好、BMI参数等数据信息
服务端计算⼀个⼈的BMI指数。算法:bmi = 体重(公⽄) / 身⾼(⽶) / 身⾼(⽶)
2.项目结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yrWigrwD-1687244926186)(Servlet/image-20230519194158753.png)]
3.代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>⽤户信息</title>
</head>
<body>
<form action="param" method="post">
<fieldset>
<legend>⽤户信息</legend>
<br/>
姓名:
<input name="username"/><br/><br/>
爱好:
<input type="checkbox" name="interests" value="Java"/> Java
<input type="checkbox" name="interests" value="Python"/> Python
<input type="checkbox" name="interests" value="Web"/> Web
<br/><br><hr><br/>
身⾼(M):
<input name="height"/><br/><br/>
体重(G):
<input name="weight"/><br/><br/>
性别:
<input type="radio" name="gender" value="男" checked="checked"/>
男
<input type="radio" name="gender" value="女"/> ⼥
<br><br><hr><br/>
<input type="submit" value="提交">
</fieldset>
</form>
</body>
</html>
package com.fjp.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author fjp
* @version 1.0
* @description: TODO
* @date 2023/5/19 11:23
*/
public class ParamServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
//getParameterValues复选框的值
String[] hobby = req.getParameterValues("interests");
for (int i = 0; i < hobby.length; i++) {
System.out.println(hobby[i]);
}
String height = req.getParameter("height");
String weight = req.getParameter("weight");
String gender = req.getParameter("gender");
double BMI = Double.parseDouble(weight)/Double.parseDouble(height)/Double.parseDouble(height);
String status ="正常";
if ("男".equals(gender)){
if (BMI <20) {
status = "偏瘦";
}
if (BMI>25) {
status = "偏胖";
}
}else {
if (BMI <18) {
status = "偏瘦";
}
if (BMI>25) {
status = "偏胖";
}
}
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.println("姓名:"+username+",性别:"+gender +",身高:"+height+",体重:"+weight+",BMI:"+BMI);
writer.close();
}
}
<servlet>
<servlet-name>paramServlet</servlet-name>
<servlet-class>com.fjp.servlet.ParamServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>paramServlet</servlet-name>
<url-pattern>/param</url-pattern>
</servlet-mapping>
3.处理中文乱码
1.URL编解码
1.在com.cy.controller包下创建URLCoderTest类,测试URL编码器和URL解码器的使⽤。
package com.cy.controller;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class URLCoderTest {
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "圆⼼";
String str1 = URLEncoder.encode(str, "UTF-8");
System.out.println("str1:" + str1);
String str2 = URLDecoder.decode(str1, "UTF-8");
System.out.println("str2:" + str2);
}
}
2.总结:此⽅式虽然可以⽤来解决中⽂编码的问题,但是不适合与Servlet结合使⽤来处理中⽂乱码问题。
2.请求数据乱码处理
1.请求数据乱码原因
当表单提交数据时,浏览器会按照打开该表单所在的⻚⾯时的字符集来对中⽂参数值进⾏编码。⽐如,使⽤UTF-8来编码,⽽服务器端默认会使⽤ISO-8859-1来解码。所以会产⽣中⽂乱码。
2.GET请求乱码处理
在idea中GET请求不会产⽣中⽂乱码。在eclipse中GET请求会产⽣中⽂乱码。eclipse中如果GET请求产⽣中⽂乱码,处理的⽅法是:修改服务器端的配置。 Servers项⽬ - server.xml⽂件 - 65⾏代码 - Connector标签添加:URIEncoding="UTF-8"属性。
<Connector URIEncoding="UTF-8" connectionTimeout="20000" port="8080"
protocol="HTTP/1.1" redirectPort="8443"/>
3.post请求乱码处理
/*
* 设置请求参数值使⽤哪种字符集来解码。注意:
* 1.此⽅式只针对post请求有效
* 2.这⾏代码要添加到所有的getParameter()和getParameterValues()⽅法的最前⾯
*/
request.setCharacterEncoding("UTF-8");
3.响应乱码处理
1.响应数据乱码原因
默认情况下response.getWriter().ptrintln(“”)⽅法会使⽤"ISO-8859-1"来编码响应给前端的数据,⽽ISO-8895-1不⽀持中⽂,所以会产⽣中⽂乱码。
2.响应数据乱码处理
在响应数据之前设置响应类型,⽤于设置content-type消息头的值,并在其中添加字符的编码为UTF-8类型。
response.setContentType("text/html;charset=UTF-8");
4.获取请求参数值练习
1.项目需求
1.⽤户在客户端添加⽤户名、密码和邮箱信息,提交给服务器,服务器接收处理并响应给前端浏览器。
2.项⽬的结构设计
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JY2pkOPf-1687244926187)(Servlet/image-20230519195343172.png)]
package com.fjp.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author fjp
* @version 1.0
* @description: TODO
* @date 2023/5/19 20:01
*/
public class login extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
resp.setContentType("text/html");
resp.getWriter().println("用户名:"+username);
resp.getWriter().println("密码:"+password);
resp.getWriter().println("邮箱:"+email);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action = "login" method="post">
<div>
<span>个人信息</span>
<br/>
<input type="text" name="username" placeholder="用户名">
<br/>
<input type="password" name="password" placeholder="密码">
<br/>
<input type="text" name="email" placeholder="邮箱">
<br/>
<input type="submit" value="提交">
</div>
</form>
</body>
</html>
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.fjp.servlet.login</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
6.Servlet生命周期
1.Servlet生命周期介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMnPTrn0-1687244926188)(Servlet/image-20230519201732658.png)]
2.Servlet生命周期阶段
1.实例化
1.什么是实例化?容器调用Servlet的构造器,创建Servlet对象
2.什么时候实例化?
1)情形1:容器收到请求之后才创建相应的Servlet实例
2)情形2:容器启动后,立即创建相应的Servlet实例(需要额外配置)
<!-- 配置启动加载(容器启动之后,会⽴即创建Servlet对象)。参数值是⼀个⼤于等于零的整数,值越⼩优先级越⾼
(值⼩的先创建) -->
<load-on-startup>1</load-on-startup>
3.对于某个Servlet,容器只会创建⼀个实例
2.初始化
1.什么是初始化?
容器调用servlet对象的init(ServletConfig config)方法,注意,init(ServletConfig config)只调用一次。
2.GenericServlet已经提供了init⽅法的实现
会将容器传递过来的ServletConfig对象保存下来,并且提供了⼀个⽅法getServletConfig()来获得该对象。如果要实现⾃已的初始化处理逻辑,只需要override GenericServlet的init()⽅法。
3.初始化参数
1)配置初始化参数
<!-- 配置初始化参数 -->
<init-param>
<param-name>company</param-name>
<param-value>阶段</param-value>
</init-param>
2)调用ServletConfig提供的方法来读取初始化参数
// 通过继承⾃GenericServlet提供的⽅法来获得ServletConfig对象。注:GenericServlet是HttpServlet的⽗类
public void init() throws ServletException {
ServletConfig servletConfig = getServletConfig();
// 读取初始化参数
String company = servletConfig.getInitParameter("company");
System.out.println(company);
}
3.就绪
1.什么是就绪?
容器调用Servlet对象的service方法来处理请求
2.HttpServlet已经提供了service()方法实现。该方法会依据请求类型(get请求/post请求)调用对应的doxxx方法。(⽐如,get请求会调⽤doGet⽅法,post请求会调⽤doPost⽅法)。doXXX⽅法只是抛出了⼀个异常。开发⼈员有两个选择:
- override HttpServlet的doXXX()⽅法
- override HttpServlet的service()⽅法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TZGR5ORL-1687244926189)(Servlet/image-20230519204511511.png)]
4.销毁
1.什么是销毁?
容器在删除Servlet对象之前,会调⽤该对象的destroy()⽅法。注意,该⽅法只会被执⾏⼀次!
2.GenericServlet已经提供了destroy()⽅法的实现。该⽅法实际上什么都没有做,我们可以override该⽅法,来实现⾃⼰的销毁处理逻辑
package com.cy.controller;
import java.io.*;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class LifeCycleServlet extends HttpServlet {
/** Servlet容器会调⽤Servlet的构造器,创建Servlet对象 */
public LifeCycleServlet() {
System.out.println("LifeCycleServlet的构造器");
}
@Override
public void init() throws ServletException {
System.out.println("LifeCycleServlet的init()⽅法");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("LifeCycleServlet的doPost()⽅法");
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("LifeCycleServlet的doGet()⽅法");
// 通过继承⾃GenericServlet提供的⽅法来获得ServletConfig对象。注:GenericServlet是
HttpServlet的⽗类
ServletConfig servletConfig = getServletConfig();
// 读取初始化参数
String company = servletConfig.getInitParameter("company");
System.out.println("company=" + company);
}
/*
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("LifeCycleServlet的service()⽅法");
// 通过继承⾃GenericServlet提供的⽅法来获得ServletConfig对象。注:GenericServlet是
HttpServlet的⽗类
ServletConfig servletConfig = getServletConfig();
// 读取初始化参数
String company = servletConfig.getInitParameter("company");
System.out.println("company=" + company);
}
*/
@Override
public void destroy() {
System.out.println("LifeCycleServlet的destroy()⽅法");
}
}
<servlet>
<servlet-name>lifeCycleServlet</servlet-name>
<servlet-class>com.cy.controller.LifeCycleServlet</servlet-class>
<!-- 配置初始化参数 -->
<init-param>
<param-name>company</param-name>
<param-value>圆⼼</param-value>
</init-param>
<!-- 配置启动加载(容器启动之后,会⽴即创建Servlet对象)。参数值是⼀个⼤于等于零的整数,值越⼩优先
级越⾼(值⼩的先创建) -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>lifeCycleServlet</servlet-name>
<url-pattern>/lifeCycle</url-pattern>
</servlet-mapping>