【Java寒假打卡】JavaWeb-Servlet基础
- 介绍
- servlet快速入门
- servlet的执行过程
- servlet关系视图
- Servlet实现方式-继承HTTPSERVLET
- Servlet的生命周期
- 线程安全问题
- servlet的映射方式
- 案例-多路径映射问题-指定名称的方式
- Servlet的创建时机
- 默认Servlet
介绍
- servlet是运行在Java服务器端的程序,用于接受和响应来自客户端基于HTTP协议的请求
- 如果想实现Servlet的功能,可以通过实现javax.servlet.Servlet接口或者继承它的实现类,比如GenericServlet
- 核心方法:service,任何客户端的请求都会经过该方法
servlet快速入门
-
继承GenericServlet
-
重写service方法
-
在WEB.XML配置Servlet
- 部署并启动项目
servlet的执行过程
http://localhost:8080/demo1/servlet1
* demo1是虚拟路径
* servlet1:是资源路径
* localhost是本机地址
* 8080是端口号
servlet关系视图
Servlet实现方式-继承HTTPSERVLET
-
创建一个类继承HTTPSERVLET
-
重写doGet和doPost方法
-
在web.xml中配置Servlet
-
部署并启动项目
- 通过浏览器测试
http://localhost:8080/demo1/servlet2
Servlet的生命周期
结论:Servlet对象只会创建一次,销毁一次,所以Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就称之为单例模式。
线程安全问题
-
由于Servlet采用的是单例模式,也就是整个应用中只有一个实例对象。所以需要分析一下该实例对象的类成员是否线程安全
-
模拟用户登录功能来查看Servlet线程是否安全
package com.hfut.serv.ServletDemo1;
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;
public class servlet3 extends HttpServlet {
private String username;// 定义一个用户名的成员变量
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
username = req.getParameter("username");// 获取提交的用户名 根据username获取用户名称
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 将用户名响应给;浏览器
PrintWriter pw = resp.getWriter();
pw.println("welcome:" + username);// 打印
pw.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
}
}
- 结论:一个浏览器代表一个线程,多个浏览器代表多个线程,按理来说,应该是每一个浏览器查看的都是自己的用户名。而现在的结果是浏览器中数据混乱,因此,可以认为Servlet不是线程安全的
- 解决办法一:定义为局部变量
package com.hfut.serv.ServletDemo1;
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;
public class servlet3 extends HttpServlet {
// private String username;// 定义一个用户名的成员变量
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = null;// 定义成局部变量 解决线程安全问题
// super.doGet(req, resp);
username = req.getParameter("username");// 获取提交的用户名 根据username获取用户名称
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 将用户名响应给;浏览器
PrintWriter pw = resp.getWriter();
pw.println("welcome:" + username);// 打印
pw.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
}
}
- 解决办法二:使用同步代码块
package com.hfut.serv.ServletDemo1;
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;
public class servlet3 extends HttpServlet {
private String username;// 定义一个用户名的成员变量
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// String username = null;// 定义成局部变量 解决线程安全问题
// 解决办法二:使用同步代码块
synchronized (this){
username = req.getParameter("username");// 获取提交的用户名 根据username获取用户名称
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 将用户名响应给;浏览器
PrintWriter pw = resp.getWriter();
pw.println("welcome:" + username);// 打印
pw.close();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
}
}
servlet的映射方式
- 具体名称的方式。访问的资源路径必须和映射配置完全相同
- /开头 + 通配符的方式。只要符合目录结构即可,不用考虑结尾是什么
- 通配符 + 固定格式结尾的方式。只要符合固定结尾格式即可,不用考虑前面的路径
- 具体名称的方式,随便起一个名字 这里不一定是servlet4
<!-- 第一种映射方式servlet4 指定名称的方式-->
<!-- 配置servlet4-->
<servlet>
<servlet-name>servlet4</servlet-name>
<servlet-class>com.hfut.serv.ServletDemo1.servlet4</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet4</servlet-name>
<!-- 资源访问路径-->
<url-pattern>/servlet4</url-pattern>
</servlet-mapping>
- /开头+ 通配符
<!-- 第二种映射方式/开头 + 通配符-->
<servlet>
<servlet-name>servlet4</servlet-name>
<servlet-class>com.hfut.serv.ServletDemo1.servlet4</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet4</servlet-name>
<!-- 只要前面是/servlet 后面不管填写什么 都可以找到servlet4资源-->
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
- 通配符+ 固定格式结尾
<!-- 第三种方式:通配符+ 固定格式结尾 -->
<servlet>
<servlet-name>servlet4</servlet-name>
<servlet-class>com.hfut.serv.ServletDemo1.servlet4</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet4</servlet-name>
<!-- /*.do 指定格式 访问servlet4资源路径 前面都是虚拟目录-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
优先级问题:越是具体的优先级越高,越是模糊通用的优先级与越低。第一种优先级越高
案例-多路径映射问题-指定名称的方式
这里的案例使用指定的开头加上通配符进行的
<servlet>
<servlet-name>servlet5</servlet-name>
<!-- 具体的资源路径 通过servlet的名字找到-->
<servlet-class>com.hfut.serv.ServletDemo1.Servlet5</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet5</servlet-name>
<!-- 指定开头加上通配符-->
<url-pattern>/hfut/*</url-pattern>
</servlet-mapping>
package com.hfut.serv.ServletDemo1;
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 Servlet5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 定义一个商品金额
int money = 1000;
// 获取访问资源路径
String path = req.getRequestURI();
// 将最后一个/之后的字符串进行截取 先定位最后一个/的索引 然后进行字符串的截取
path = path.substring(path.lastIndexOf("/"));
// 条件判断
if("/vip".equals(path)){
System.out.println("打九折");
}else if("/vvip".equals(path)){
System.out.println("打五折");
}else{
System.out.println("不打折");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
}
}
Servlet的创建时机
一个xml文件中有多个servlet配置文件,所以加载servlet的时候有个优先级的问题
<?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_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>servlet5</servlet-name>
<!-- 具体的资源路径 通过servlet5的名字找到-->
<servlet-class>com.hfut.serv.ServletDemo1.Servlet5</servlet-class>
<!-- 默认创建时机是第一次访问时 创建servlet5资源-->
<!-- 添加这句话 正整数表示服务器启动的时候进行访问 数字大小代表启动的优先级-->
<!-- 修改servlet的优先级-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servlet5</servlet-name>
<!-- 指定开头加上通配符-->
<url-pattern>/hfut/*</url-pattern>
</servlet-mapping>
</web-app>