JAVAEE:超级详细servlet总结

什么是servlet?

其实就是一个java程序,运行在我们的web服务器上,用于接收和响应 客户端的http请求。
更多的是配合动态资源来做。 当然静态资源也需要使用到servlet,只不过是Tomcat里面已经定义好了一个 DefaultServlet

Servlet的创建及其相关配置

1_创建HelloWeb:
在这里插入图片描述在这里插入图片描述
在这里我们需要在Dynamic web module version 选择2.5,一会在说为什么?
然后就有了
在这里插入图片描述
现在我们开始创建我们的第一个servlet了,准备好了吗?小老弟们。
2_新建一个类, 实现Servlet接口:
然后就有了如下东西:

package com.items.servlet;

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

public class ServletTest implements Servlet{

	@Override
	public void init(ServletConfig config) throws ServletException {		
	}

	@Override
	public ServletConfig getServletConfig() {
		return null;
	}

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		
	}

	@Override
	public String getServletInfo() {
		return null;
	}

	@Override
	public void destroy() {	
	}
}

2_怎么样才能让这个东西运行,并出现结果呢?
这个时候我们需要配置相关路径,在配置前呢?我们在上面方法中添加一些内容如下:

	@Override
	public void init(ServletConfig config) throws ServletException {	
		System.out.println("-----------------我被初始化了-----------------");
	}

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		System.out.println("-----------------我被调用了-----------------");
	}

	@Override
	public void destroy() {	
		System.out.println("-----------------我被摧毁了-----------------");
	}

然后我们开始配置:
首先我们需要点开,web.xml这个文件。
在这里插入图片描述
如果没有这个文件也没有关系,根据如下操作就可以解决这个问题。
在这里插入图片描述
当我们有了web.xml这个文件,我们双击这个文件,将出现如下内容。
在这里插入图片描述
如果不是这种展现方式,而是如下所示:
在这里插入图片描述
我们只需要点击图片中用黑色方框圈出来的地方,然后点击Source就可以了

好了我们开始配置servlet,结果如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>HelloWeb</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  	<!-- 向tomcat报告, 我这个应用里面有这个servlet, 名字叫做ServletTest , 具体的路径是com.items.servlet.ServletTest -->
	<servlet>
		<servlet-name>ServletTest</servlet-name>
	  	<servlet-class>com.items.servlet.ServletTest</servlet-class>
	</servlet>
	  
	 <!-- 注册servlet的映射。  servletName : 找到上面注册的具体servlet,  url-pattern: 在地址栏上的path 一定要以/打头 -->
	 <servlet-mapping>
	  	<servlet-name>ServletTest</servlet-name>
	  	<url-pattern>/a</url-pattern>
	 </servlet-mapping>

</web-app>

在这里我需要强调一件事:
我们在配置 < servlet-mapping > 下面的 < url-pattern > 一定要以 / 打头,不要问为什么,因为你不这样做,servlet就跑不起来。

在这里我需要强调一件事:
我们在配置 < servlet-mapping > 下面的 < url-pattern > 一定要以 / 打头,不要问为什么,因为你不这样做,servlet就跑不起来。

在这里我需要强调一件事:
我们在配置 < servlet-mapping > 下面的 < url-pattern > 一定要以 / 打头,不要问为什么,因为你不这样做,servlet就跑不起来。

重要的事情说三篇。不要忘了要有 / 打头

然后呢,我说一说如何快速得到servlet的具体路径,相关操作如下:
在这里插入图片描述
将相关servlet变成如上所示,然后我们在用鼠标右击ServletTest(用黄色框圈出来的),然后如图所示。
点击Copy Qualified Name就可以了
在这里插入图片描述
然后我们就要启动tomcat了,启动好tomcat后我们在游览器中输入

http://localhost:8080/HelloWeb/a

在这里插入图片描述
然后我将在控制台上面看到一些东西,直接上图。
在这里插入图片描述
我们发现在这里有两行输出内容,说明在我们访问这个servlet的时候tomcat调用了servlet中的两个方法init(),service(),然后呢,我们在游览器里面多次刷新我们刚刚输入的url,然后我们在观察控制台的输出内容,变成了这样。我们发现调用了多次service()
在这里插入图片描述
然后,我们在正常关闭tomcat,按照如下所示,点击用黄色框圈出来的那个按钮
在这里插入图片描述
然后将在控制台输出什么呢?我们直接上图。
在这里插入图片描述
我们发现当我们正常关闭tomca的时候,servlet调用了destroy()

以上的现象了我们一会在来进行详细说明。

Servlet执行过程

当我们在游览器的url中输入http://localhost:8080/HelloWeb/a时
执行过程如下:

1_找到Tomcat应用

2_找到项目

3_找web.xml,然后在里面找到url-pattern,有没有哪一个pattern的内容是/a

4_找到servlet-mapping中的那个servlet-name【ServletTest】

5_找到上面定义的servlet元素中的servlet-name中的【ServletTest】

6_找到下面定义的servlet-class然后开始创建该类的实例

7_继而执行servlet中的service方法

Servlet的通用写法

Servlet (接口)
	|
	|
GenericServlet
	|
	|
HttpServlet (用于处理http的请求)

我们一般在用servlet的时候,一般我们都是通过继承HttpServlet (用于处理http的请求)

以下我们来继承HttpServlet 试试,看看效果怎么样,直接上代码

package com.items.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HttpServletTest extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("我是doGet。。。。。。。。。。");
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("我是doPost。。。。。。。。。。");
	}
}

如果不知道get和post的这两种提交方式的化,可以看看这篇博客
HTTP 8种请求方式介绍

然后我们在来配置servlet,如下:(用黑色框圈出来的)
在这里插入图片描述
然后我们启动tomcat后,在游览器里面输入:

http://localhost:8080/HelloWeb/HttpServletTest

然后我们发现在控制台将会输出:(如下结果图)
在这里插入图片描述
我们发现这个时候我们调用了,doGet方法,那么这个时候,我们会想init() ,destory(),service()这个三个方法调用了吗?
答案是调用了init() ,service(),这是我们没有写输出语句而已,而只有当我们正常关闭服务器时才会调用destory()方法。

然后呢,我们再想上面操作那样,刷新游览器中的url栏,在看看控制台是什么效果。
在这里插入图片描述
最后呢,我们就记住Servlet的通用写法,是继承HttpServlet这个类,一会我们在说为什么?

Servlet的生命周期

  • 生命周期

从创建到销毁的一段时间

  • 生命周期方法

从创建到销毁,所调用的那些方法。

  • init方法

在创建该servlet的实例时,就执行该方法。 一个servlet只会初始化一次, init方法只会执行一次 默认情况下是: 初次访问该servlet,才会创建实例。

  • service方法

只要客户端来了一个请求,那么就执行这个方法了。
该方法可以被执行很多次。 一次请求,对应一次service方法的调用

  • destroy方法

servlet销毁的时候,就会执行该方法

  1. 该项目从tomcat的里面移除。
  2. 正常关闭tomcat就会执行 shutdown.bat

以上便是Servlet的生命周期。
这个时候,可能有人会说,为什么没有doGet 和 doPost这两个方法,我们继承HttpServlet这个类的时候不就调用了这个方法吗?

下面,我们做一个小小的实验来说明:
我们创建如下这个类:(重写相关方法来进行说明)

package com.items.servlet;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HttpTest extends HttpServlet{

	@Override
	public void init(ServletConfig config) throws ServletException {	
		System.out.println("-----------------我被初始化了-----------------");
	}

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		System.out.println("-----------------我被调用了-----------------");
	}

	@Override
	public void destroy() {	
		System.out.println("-----------------我被摧毁了-----------------");
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("我是doGet。。。。。。。。。。");
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("我是doPost。。。。。。。。。。");
	}
}

配置文件如下(用黑色框圈出来的):
在这里插入图片描述
然后我们在重启tomcat,然后在游览器的url上输入:

http://localhost:8080/HelloWeb/HttpTest

结果如下:
在这里插入图片描述
然后多次刷新后:
在这里插入图片描述
然后正常关闭tomcat(上面说过怎么正常关闭tomcat)
在这里插入图片描述
我们发现,在这个实验工程中,我们发现从头到尾都没有调用过doGet 和 doPost这个两个方法,这是为什么?
原因如下:(我们需要查看源码)
然后我们查看了HttpServlet方法中的service方法

 protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
		//获取到这个方法是什么方法名
        String method = req.getMethod();
		//判断是什么方法
		//如果是doGet方法,然后我们就执行doGet(req, resp);	
		//如果不是的化,我们就执行  doPost(req, resp);	
        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);
		//我们就执行  doPost(req, resp);	
        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);

        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);

        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);

        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);

            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

所以当我们重写了HttpServlet里面的service方法后,就不会在判断是方法的类型了,就不会调用doGet 和 doPost方法了,所以 生命周期方法只有:

  • init方法

  • service方法

  • destroy方法

通过查看HttpServlet里面的service方法,我们可以知道一个东西,那就是请求的方式不止一种

最后我们明白:
doGet 和 doPost不算生命周期方法,所谓的生命周期方法是指,从对象的创建到销毁一定会执行的方法, 但是这两个方法,不一定会执行。

最后呢,我们可以说说为什么servlet的通用写法是继承HttpServlet,下面我说说我的看法(也可能不对):

看了上面的几个实验,我们发现无论我们是继承servlet接口还是继承HttpServlet这个类,我们发现呢,通过配置web.xml我们都可以做到通过游览器来访问我们的servlet,但是呢,我们发现,继承servlet接口,我们需要重写的是

  • init()
  • getServletConfig()
  • service()
  • getServletInfo()
  • destroy()
    这五个方法,而我们呢?有不是所有方法都要使用,如果直接继承servlet接口的化,会比较麻烦,不会像直接继承HttpServlet那样我们只需要重写doGet 和 doPost这个两个方法就可以了。

让Servlet创建实例的时机提前

1_默认情况下,只有在初次访问servlet的时候,才会执行init方法。 有的时候,我们可能需要在这个方法里面执行一些初始化工作,甚至是做一些比较耗时的逻辑。
2_那么这个时候,初次访问,可能会在init方法中逗留太久的时间。 那么有没有方法可以让这个初始化的时机提前一点。
3_在配置的时候, 使用load-on-startup元素来指定, 给定的数字越小,启动的时机就越早。 一般不写负数, 从2开始即可。
下面我在来做个实验,代码如下:

package com.items.servlet;

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Test implements Servlet{

	@Override
	public void init(ServletConfig config) throws ServletException {
		System.out.println("我是init。。。。。我被调用了");	
	}

	@Override
	public ServletConfig getServletConfig() {
		return null;
	}

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		System.out.println("我是service方法。。。。。。。。。");
	}

	@Override
	public String getServletInfo() {
		return null;
	}

	@Override
	public void destroy() {}

}

然后我们在进行如下配置:
与以往不同我们在servlet里面加入了一行< load-on-startup>2< /load-on-startup>
在这里插入图片描述
然后我们启动tomcat,注意这个时候,我们不再游览器的url输入相关地址,直接查看控制台:
发现,相关的servlet被初始化了
在这里插入图片描述

ServletConfig

Servlet的配置,通过这个对象,可以获取servlet在配置的时候一些信息
直接代码:(注意在这里我们直接实现servlet接口的方式来处理)

package com.items.servlet;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class Servlet00  implements Servlet{

	@Override
	public void init(ServletConfig config) throws ServletException {	
		System.out.println("-----------------我被初始化了-----------------");
	}

	@Override
	public ServletConfig getServletConfig() {
		return null;
	}

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		System.out.println("-----------------我被调用了-----------------");
		
		//获取ServletConfig对象
		ServletConfig config = getServletConfig();
		System.out.println(config);
		
		//可以获取具体的某一个参数。 
		String name = config.getInitParameter("name");
		String address = config.getInitParameter("address");
		System.out.println(name+":::"+address);
		
		//获取全部参数的信息
		System.out.println("-------------------------------");
		Enumeration<String> enumeration = config.getInitParameterNames();
		while (enumeration.hasMoreElements()) {
			String key = (String) enumeration.nextElement();
			String value = config.getInitParameter(key);
			System.out.println(name+":::"+address);
		}
	}

	@Override
	public String getServletInfo() {
		return null;
	}

	@Override
	public void destroy() {	
		System.out.println("-----------------我被摧毁了-----------------");
	}

}

配置方式如下:
在这里插入图片描述
然后我们启动tomcat,后在游览器url上面输入:
发现在控制台上出现报错,报错结果是NullPointerException
在这里插入图片描述
为什么会出现报错了?
原因如果所示:

servlet的几种方式以及接口或者类之间的关系图

在这里插入图片描述
通过这个关系同我们发现一个道理,那就是Servlet接口根本就没有实现ServletConfig 这个接口。
所以通过图我们知道,HttpServlet实现了这个接口,(这张图也进一步说明了,为什么我们选择HttpServlet而不选择Servlet的原因)。
然后我们将代码修改如下,将Servlet换成HttpServlet,然后在按照上面的操作在来一次。

package com.items.servlet;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;

public class Servlet00  extends HttpServlet{


	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		System.out.println("-----------------我被调用了-----------------");
		
		//获取ServletConfig对象
		ServletConfig config = getServletConfig();
		System.out.println(config);
		
		//可以获取具体的某一个参数。 
		String name = config.getInitParameter("name");
		String address = config.getInitParameter("address");
		System.out.println(name+":::"+address);
		
		//获取全部参数的信息
		System.out.println("-------------------------------");
		Enumeration<String> enumeration = config.getInitParameterNames();
		while (enumeration.hasMoreElements()) {
			String key = (String) enumeration.nextElement();
			String value = config.getInitParameter(key);
			System.out.println(name+":::"+address);
		}
	}

}

输出结果如下:

在这里插入图片描述
为什么需要有这个ServletConfig

  1. 未来我们自己开发的一些应用,使用到了一些技术,或者一些代码,我们不会。 但是有人写出来了。它的代码放置在了自己的servlet类里面。
  2. 刚好这个servlet 里面需要一个数字或者叫做变量值。 但是这个值不能是固定了。 所以要求使用到这个servlet的公司,在注册servlet的时候,必须要在web.xml里面,声明init-params

servlet的便捷创建和配置

1_便捷创建:
在这里插入图片描述
在这里插入图片描述
2_便捷配置:
使用 @WebServlet( “/+自己定义的路径”)
注意:一定要要以 / 打头
代码如下:

package com.items.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/aa")
public class Test00 extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("哈哈,我来了小老弟");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
	}
}

然后启动tomcat,在游览器的url中输入:

http://localhost:8080/HelloWeb/aa

结果如下:
在这里插入图片描述

servlet是多线程单实例

下面来简单证明一下servlet是多线程单实例
首先我们将上面的代码简单的修改一下:

package com.items.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/aa")
public class Test00 extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	
		//Thread.currentThread().getName()输出线程的名字
		System.out.println(Thread.currentThread().getName()+":::"+this);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
	}
}

然后启动tomcat,在游览器的url中输入:

http://localhost:8080/HelloWeb/aa

结果如下:
在这里插入图片描述
通过输出结果我们可以发现:servlet是单实例多线程。
所以这样就存在隐患,我们在使用servlet的时候我们需要考虑线程安全问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值