1.Servlet接口的作用
Servlet是运行在Web服务器上的应用程序。Servlet本身是一个Java接口,它定义了浏览器访问服务器程序的规则,我们写服务器程序只需要按照需求复写Servlet方法即可
2.Servlet的体系结构
<1>直接继承实现类结构
<2>其他相关类视图
<1>实现Servlet接口
重写Servlet生命周期的所有方法
public class ServletDemo1 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
//第一次访问的时候创建Servlet调用
System.out.println("init初始化执行1次");
}
@Override
public ServletConfig getServletConfig() {
//每一个Servlet对象创建时,会自动创建一个ServletCong对象
return null;
}
//所有的客户端请求都会经过service方法
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("servlet实现响应");
}
//获取Servlet信息
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("销毁servlet执行1次");
}
}
<2>继承GenericServlet抽象类
每次写一个Servlet都要复写生命周期的所有方法,比较麻烦。 Servlet提供了一个实现类GenericServlet,它把其他方法已经复写了,我们继承GenericServlet只需要复写service就可以了
public class ServletDemo2 extends GenericServlet {
//不知道是get请求还是post请求
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("GenericServlet实现响应");
}
}
<3>继承GenericServlet抽象类【最简便也最常用】
如果每次都是继承GenericServlet类,在servlce方法中,不能区分直接是Get请求还是Post请求。需要我们自己求请求方法进行处理,也比较麻烦,所以又提供了一个子类HttpServlet类。
HttpServlet类中会根据不同的请求方式,提供不同的方法,我们只需要复写对应的方法即可
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HttpServlet实现响应");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
3.Servlet的映射配置
<1>针对上面三种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">
<!-- 修改默认主页 /代表根目录Web-->
<welcome-file-list>
<welcome-file>/html/index.html</welcome-file>
</welcome-file-list>
<!-- servlet声明-->
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet>
<servlet-name>ServletDemo2</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo2</servlet-class>
</servlet>
<servlet>
<servlet-name>ServletDemo3</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo3</servlet-class>
</servlet>
<!--servlet映射 -->
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/Servlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletDemo2</servlet-name>
<url-pattern>/GenericServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletDemo3</servlet-name>
<url-pattern>/HttpServlet</url-pattern>
</servlet-mapping>
</web-app>
<2>同一个Servlet多映射处理
针对用户种类对进行多种映射配置
<!-- 方式1 -->
<!--具体名称的方式。访问的资源路径必须和映射配置完全相同-->
<servlet-mapping>
<servlet-name>ServletDemo5</servlet-name>
<url-pattern>/ServletDemo5</url-pattern>
</servlet-mapping>
<!-- 方式2 -->
<!--/开头+通配符的方式 不用考虑结尾是什么 -->
<servlet-mapping>
<servlet-name>ServletDemo5</servlet-name>
<url-pattern>/ServletDemo5/*</url-pattern>
</servlet-mapping>
<!-- 方式3 -->
<!--通配符+固定格式结尾的方式。只要符合固定结尾格式即可,不用考虑前面的路径-->
<servlet-mapping>
<servlet-name>ServletDemo5</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
【注:优先级问题。越是具体的优先级越高,越是模糊通用的优先级越低。第一种->第二种->第三种–>】
<3>从浏览器地址栏,到访问Servlet的过程
Servlet是Web项目中的动态资源(Servlet字节码),访问过程如下
第一步:通过浏览器的ip地址找服务端主机
第二步:通过8080端口号找到tomcat服务器软件
第三步:通过虚拟机路径找到tomcat服务器上发布的web项目包
第四步:通过web.xml
中 <url-pattern>
找到Servlet对应的字节码
第五步:Tomcat会根据Servlet的字节码,自动的执行servcie方法
4.Servlet的加载时机
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo1</servlet-class>
<!--Servlet的加载时机配置
每个Servlet默认是在第一次访问的时候被创建,默认为-1
但是也可以通过web.xml配置让其在服务器启动的时候创建。
数字越小,优先级越高-->
<load-on-startup>1</load-on-startup>
</servlet>
<1>第一次访问的时候被创建
优势:减少对服务器内存的浪费。提高了服务器启动的效率。
弊端︰如果有一些要在应用加载时就做的初始化操作,无法完成。
<2>服务器加载时创建
优势∶提前创建好对象,提高了首次执行的效率。可 以完成一些应用加载时要做的初始化操作
弊端∶对服务器内存占用较多,影响了服务器启动的效率
5.Servlet的线程安全问题
<1>问题出现:一个浏览器代表一个线程,多个浏览器代表多个线程。按理说我们期望的应该是每个浏览器查看的都应该是自己的用户名。而现在的结果是浏览器中数据混乱。因此,我们可以认为Servlet是线程不安全的!
<2>分析:多线程使用同一个共享数据
<3>解决:定义类成员要谨慎。如果是共用的,并且只会在初始化时赋值,其他时间都是获取的话,那么是没问题的。如果不是共用的,或者每次使用都有可能对其赋值,那就要考虑线程安全问题了,可以将其定义到doGet 或 doPost方法内或者使用同步功能即可。
a.同步代码块
b…将可能需要重新赋值的值定义在自己的方法体内[推荐]
<4>代码实现
//servlet线程安全问题
public class ServletDemo4 extends HttpServlet {
private String username;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//2.将可能需要重新赋值的值定义在自己的方法体内[推荐]
//String username =null;
//1.同步代码块
synchronized (this){
//获取用户名
username = req.getParameter("username");
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
//将用户名响应给浏览器
PrintWriter pw = resp.getWriter();
pw.println("welcome:" + username);
pw.close();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
6.默认Servlet
<1>含义;默认Servlet是由服务器提供的一个Servlet。它配置在Tomcat的conf目录中的web.xml中;
它的映射路径是/,我们在发送请求时,首先会在我们项目中的web.xml中查找映射配置,找到则执行。但是当找不到对应的Servlet路径时,就去找默认的Servlet,由默认Servlet处理。所以,一切都是Servlet。
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<3>如一些报错信息,如404都是默认Servlet给我们的提示。