Servlet
- 相关概念
- Servlet使用
- Servlet配置
- Servlet体系
一、相关概念
1.1、什么是Servlet?
Servlet(Server applet):一个Java接口,定义了Java类被浏览器访问到的规则。主要功能在于交互式地浏览和修改数据,生成动态Web内容。
1.2、什么是Servlet容器?
Servlet容器:就是服务器软件(Tomcat等),主要用于端口监听、协议处理等、反射调用。
1.3、一条请求消息的一生
请求信息:http://www.baidu.com/project1/demo1?name=zhangsan
-
www代表万维网,baidu.com是一个域名,我们都知道每一个域名都对应的是一个唯一的IP地址,假如是192.168.1.101吧,通过这个IP地址我们可以找到万维网中唯一的一台计算机。
-
但是一台计算机有很多个端口,每一个端口号对应着一个应用程序,我们怎么知道去找哪个应用程序呢?这里没有给端口号,但是HTTP协议户默认访问80端口,(假如)Tomcat监听着80端口。
-
至此,这条请求消息被送到了Tomcat。可是Tomcat下面有很多的项目,怎么知道把这条消息送到哪个项目中去呢?此时就需要拿着project1去Tomcat的/conf/Catalina/localhost/目录下找到demo.xml配置文件,再通过解析这个配置文件得知对应项目的路径,找到对应项目。【这里只讨论热部署的方式】
-
知道了这条消息要被送到哪个项目,可是一个项目中有那么多文件,该去执行哪个文件呢?所以这时得拿着demo1去对应项目的根目录中的webapp.xml配置文件中匹配。匹配规则:虚拟路径>>Servlet名称>>Servlet实现类的全类名。
哪怕使用的是注解配置,仍会有一个保存映射关系的配置文件,由Tomcat管理、维护。<servlet> <servlet-name>demo1</servlet-name><!--Servlet名称--> <servlet-class>Test.Test1</servlet-class><!--Servlet实现类的全类名--> </servlet> <servlet-mapping> <servlet-name>demo1</servlet-name><!--Servlet名称--> <url-pattern>/demo1</url-pattern><!--虚拟路径--> </servlet-mapping>
-
知道了全类名,Tomcat就可以通过反射将对应的字节码文件加载进内存,创建Servlet实现类的对象。
-
然后Tomcat会将这条请求消息解析、封装,以键值对的形式丢进新创建的ServletRequest对象中。
-
最后Tomcat会通过反射调用Servlet实现类的service()方法,调用的同时将ServletRequest对象丢进去。
-
后面就是针对这条请求消息的处理了,最后再响应一下。
二、Servlet使用
2.1、Servlet的原理
Servlet接口定义了Servlet和Servlet容器之间的契约。这个契约是:Servlet容器将Servlet实现类载入内存,并产生Servlet实例和调用它具体的方法。需要注意的是一个应用程序中,每种Servlet只能有一个实例。
用户请求导致Servlet容器调用Servlet的service()
方法,并传入一个ServletRequest对象和一个ServletResponse对象。这两个对象都是由Servlet容器将用户的请求信息封装好的两个对象,直接拿着用就行了。
ServletRequest中封装了当前的Http请求,因此,开发人员不必解析和操作原始的Http数据。ServletResponse表示当前用户的Http响应,程序员只需直接操作ServletResponse对象就能把响应轻松的发回给用户。
对于每一个应用程序,Servlet容器还会创建一个ServletContext对象。这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个ServletContext。每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象。
2.2、Servlet的配置
<web-app>
<servlet>
<servlet-name>demo1</servlet-name><!--Servlet名称-->
<servlet-class>com.Servlet_01</servlet-class><!--Servlet实现类的全类名-->
<load-on-startup>0</load-on-startup><!--指定Servlet的创建时机,默认0-->
<!--数值为负数:第一次被访问时创建
数值为0或正数:服务器启动时创建-->
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern><!--给该servlet声明一个访问名称-->
</servlet-mapping>
</web-app>
2.3、Servlet的生命周期
-
被创建:当Servlet实现类第一次被请求时,Servlet容器就会调用其
init()
方法来初始化一个Servlet对象出来,但是这个方法在后续请求中不会在被Servlet容器调用,就像人只能“出生”一次一样。利用这个原理,我们可以在init()
方法中完成一些相关的初始化工作。@Override public void init(ServletConfig servletConfig) throws ServletException { //Servlet初始化方法,只会在该Servlet第一次被访问时执行一次 }
-
提供相关服务:每当该Servlet被访问时,Servlet容器就会调用其
service()
方法执行相关操作。需要注意的是只有当这个Servlet实现类第一次被请求时才会在调用service()
方法之前调用init()
方法。@Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { //每当该Servlet被访问时,就会执行该方法 }
-
被销毁:当要销毁(释放)该Servlet时,Servlet容器就会调用其
destory()
方法。比方说在卸载应用程序或者关闭Tomcat时就会执行该方法释放内存资源。一般在这个方法中会写一些清除代码。//
2.4、Servlet的其它两个方法
-
getServletInfo():返回Servlet的一段描述,可以返回一段字符串。
@Override public String getServletInfo() { //获取Servlet的一些信息:版本、作者等等 return null; }
-
getServletConfig( ):返回由Servlet容器传给
init()
方法的ServletConfig对象。@Override public ServletConfig getServletConfig() { //获取ServlitConfig对象————>>Servlet的配置对象。 return null; }
2.5、使用
import javax.servlet.*;
import java.io.IOException;
public class Test1 implements Servlet {
@Override//创建对象
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("第一次被访问时调用,创建本类对象");
}
@Override//获取ServlitConfig对象
public ServletConfig getServletConfig() {
return null;
}
@Override//处理请求
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("我被访问到了,处理请求");
}
@Override//获取Servlet的相关信息
public String getServletInfo() {
return null;
}
@Override//销毁对象
public void destroy() {
System.out.println("本类对象销毁前的动作");
}
}
三、Servlet配置
3.1、webapp.xml配置
虽然这种方式现在基本不用了,但还是需要了解一下。
<web-app>
<servlet>
<servlet-name>demo1</servlet-name><!--Servlet名称-->
<servlet-class>com.Servlet_01</servlet-class><!--Servlet实现类的全类名-->
<load-on-startup>0</load-on-startup><!--指定Servlet的创建时机,默认0-->
<!--数值为负数:第一次被访问时创建
数值为0或正数:服务器启动时创建-->
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern><!--给该servlet声明一个访问名称-->
</servlet-mapping>
</web-app>
3.2、注解配置
从JDK1.6(Web Application 3.0)开始,Servlet支持使用注解进行配置。有了注解就不用频繁的去配置XML了,简直快乐。
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/child1")//就是这么快乐。/child1代表该servlet的访问路径
public class Test1 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("child Servlet");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
四、Servlet体系
4.1、GenerciServlet抽象类
GenericServlet 继承了Servlet接口,所以我们可以通过继承GenericServlet
来编写自己的Servlet
。源码如下:
public abstract class GenericServlet implements Servlet, ServletConfig,java.io.Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
public GenericServlet() {}//只提供了一个无参构造
@Override
public void destroy() {}//实现了destory()方法
@Override
public String getInitParameter(String name) {//继承的getInitParameter()方法
return getServletConfig().getInitParameter(name);
}
@Override
public Enumeration<String> getInitParameterNames() {
return getServletConfig().getInitParameterNames();
}
@Override
public ServletConfig getServletConfig() {//实现了getServletConfig()方法
return config;
}
@Override
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}
@Override
public String getServletInfo() {
return "";
}
@Override
public void init (ServletConfig config) throws ServletException {
//所以子类不能覆写父类带参的init()方法了,否则config对象会丢失
this.config = config;
this.init();
}
public void init () throws ServletException {
//提供给子类的init()方法
}
public void log(String msg) {
getServletContext().log(getServletName() + ": " + msg);
}
public void log(String message, Throwable t) {
getServletContext().log(getServletName() + ": " + message, t);
}
@Override//唯一的一个抽象方法
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;//需要由实现类实现的service()方法
@Override
public String getServletName() {
return config.getServletName();
}
}
需要注意的是
GenericServlet
抽象类不仅仅继承了Servlet
接口,同时还继承了ServletConfig
接口。所以可以直接调用getInitParameter()、getServletContext()等ServletConfig的方法。
4.2、HttpServlet抽象类
HttpServlet类是GenericServlet的子类,它提供了对HTTP请求的特殊支持,所以通常我们都会通过继承HttpServlet来完成自定义的Servlet。HttpServlet虽然是一个抽象类,但是却没有抽象方法。源码如下:
package javax.servlet.http;
import ...//N个包
public abstract class HttpServlet extends GenericServlet {
private static final ...//N个常量
public HttpServlet() {
//只提供了一个无参构造
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//doGet()方法
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//doPost()方法
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//service()方法
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {//get协议的处理过程
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);//正常情况下,最终会去调用doGet()方法
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);//直接调用doPost()方法
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {//http总共有7中请求协议,我这里只关注get和post
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {//重载的service()方法
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException(lStrings.getString("http.non_http"));
}
this.service(request, response);
}
...//其它方法
}
HttpServlet的service(HttpServletRequest,HttpServletResponse)
方法会去判断当前请求是GET还是POST,如果是GET请求,那么会去调用本类的doGet()方法,如果是POST请求会去调用doPost()方法,这说明我们在子类中去覆盖doGet()或doPost()方法即可。比如:
import ...
@WebServlet("/demo4")
public class Servlet_04 extends HttpServlet{
//子类重写方法时抛出的异常范围不能比父类中抛出的异常范围大(父类是IOException,子类不能是Exception)
public void doGet(HttpServletRequest request, HttpServletResponse response){
System.out.println("hello doGet()...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response){
System.out.println("hello doPost()...");
}
}
五、IDEA中快速创建Servlet
- File——>>Setting …
- 搜索code temp…——>>根据需求设置
- 在IDEA中创建Servlet:包右键——>>new——>>Servlet
- 修改Servlet名称,创建!