6、Servlet

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

  1. www代表万维网,baidu.com是一个域名,我们都知道每一个域名都对应的是一个唯一的IP地址,假如是192.168.1.101吧,通过这个IP地址我们可以找到万维网中唯一的一台计算机。

  2. 但是一台计算机有很多个端口,每一个端口号对应着一个应用程序,我们怎么知道去找哪个应用程序呢?这里没有给端口号,但是HTTP协议户默认访问80端口,(假如)Tomcat监听着80端口。

  3. 至此,这条请求消息被送到了Tomcat。可是Tomcat下面有很多的项目,怎么知道把这条消息送到哪个项目中去呢?此时就需要拿着project1去Tomcat的/conf/Catalina/localhost/目录下找到demo.xml配置文件,再通过解析这个配置文件得知对应项目的路径,找到对应项目。【这里只讨论热部署的方式】

  4. 知道了这条消息要被送到哪个项目,可是一个项目中有那么多文件,该去执行哪个文件呢?所以这时得拿着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>
    
  5. 知道了全类名,Tomcat就可以通过反射将对应的字节码文件加载进内存,创建Servlet实现类的对象。

  6. 然后Tomcat会将这条请求消息解析、封装,以键值对的形式丢进新创建的ServletRequest对象中。

  7. 最后Tomcat会通过反射调用Servlet实现类的service()方法,调用的同时将ServletRequest对象丢进去。

  8. 后面就是针对这条请求消息的处理了,最后再响应一下。

二、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的生命周期

  1. 被创建:当Servlet实现类第一次被请求时,Servlet容器就会调用其init()方法来初始化一个Servlet对象出来,但是这个方法在后续请求中不会在被Servlet容器调用,就像人只能“出生”一次一样。利用这个原理,我们可以在init()方法中完成一些相关的初始化工作。

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        //Servlet初始化方法,只会在该Servlet第一次被访问时执行一次
    }
    
  2. 提供相关服务:每当该Servlet被访问时,Servlet容器就会调用其service()方法执行相关操作。需要注意的是只有当这个Servlet实现类第一次被请求时才会在调用service()方法之前调用init()方法。

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    	//每当该Servlet被访问时,就会执行该方法
    }
    
  3. 被销毁:当要销毁(释放)该Servlet时,Servlet容器就会调用其destory()方法。比方说在卸载应用程序或者关闭Tomcat时就会执行该方法释放内存资源。一般在这个方法中会写一些清除代码。

    //
    

2.4、Servlet的其它两个方法

  1. getServletInfo():返回Servlet的一段描述,可以返回一段字符串。

    @Override
    public String getServletInfo() {
    	//获取Servlet的一些信息:版本、作者等等
    	return null;
    }
    
  2. 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

  1. File——>>Setting …
    在这里插入图片描述
  2. 搜索code temp…——>>根据需求设置
    在这里插入图片描述
  3. 在IDEA中创建Servlet:包右键——>>new——>>Servlet
    在这里插入图片描述
  4. 修改Servlet名称,创建!
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值