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销毁的时候,就会执行该方法
- 该项目从tomcat的里面移除。
- 正常关闭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
- 未来我们自己开发的一些应用,使用到了一些技术,或者一些代码,我们不会。 但是有人写出来了。它的代码放置在了自己的servlet类里面。
- 刚好这个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的时候我们需要考虑线程安全问题。