Javaweb基础——Servlet

目录:
1.Servlet入门
2.我的第一个Servlet
3.继承HttpServlet类的方法来实现Servlet
4.使用IDE直接创建Servlet程序
5.Servlet的继承体系
6.ServletConfig类
7.ServletCotext类
8.Http协议
9. GenericServlet抽象类
10.HttpServletRequest
11.HttpServletResponse
12.请求重定向

1.Servlet入门

在这里插入图片描述

如何实现Servlet:Servlet技术的核心是Servlet,它是所有Servlet类必须直接或者间接实现的一个接口。在编写实现Servlet的Servlet类时,直接实现它。在扩展实现这个这个接口的类时,间接实现它。

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Servlet接口定义了Servlet与servlet容器之间的契约。这个契约是:Servlet容器将Servlet类载入内存,并产生Servlet实例和调用它具体的方法。但是要注意的是,在一个应用程序中,每种Servlet类型只能有一个实例。

用户请求致使Servlet容器调用Servlet的Service()方法,并传入一个ServletRequest对象和一个ServletResponse对象。ServletRequest对象和ServletResponse对象都是由Servlet容器(例如TomCat)封装好的,并不需要程序员去实现,程序员可以直接使用这两个对象。

ServletRequest中封装了当前的Http请求,因此,开发人员不必解析和操作原始的Http数据。ServletResponse表示当前用户的Http响应,程序员只需直接操作ServletResponse对象就能把响应轻松的发回给用户。

对于每一个应用程序,Servlet容器还会创建一个ServletContext对象。这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个ServletContext。每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象。

2.我的第一个Servlet

servlet工作流程:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3.继承HttpServlet类的方法来实现Servlet

HttpServlet要比GenericServlet强大,其实也是有道理的。HttpServlet是由GenericServlet抽象类扩展而来的,HttpServlet抽象类的声明如下所示:

public abstract class HttpServlet extends GenericServlet implements Serializable 

HttpServlet之所以运用广泛的另一个原因是现在大部分的应用程序都要与HTTP结合起来使用。这意味着我们可以利用HTTP的特性完成更多更强大的任务。Javax。servlet.http包是Servlet API中的第二个包,其中包含了用于编写Servlet应用程序的类和接口。Javax.servlet.http中的许多类型都覆盖了Javax.servlet中的类型。

在这里插入图片描述

HttpServlet抽象类是继承于GenericServlet抽象类而来的。使用HttpServlet抽象类时,还需要借助分别代表Servlet请求和Servlet响应的HttpServletRequest和HttpServletResponse对象。

HttpServletRequest接口扩展于javax.servlet.ServletRequest接口,HttpServletResponse接口扩展于javax.servlet.servletResponse接口。

public interface HttpServletRequest extends ServletRequest
public interface HttpServletResponse extends ServletResponse

HttpServlet抽象类覆盖了GenericServlet抽象类中的Service( )方法,并且添加了一个自己独有的Service(HttpServletRequest request,HttpServletResponse方法。

让我们来具体的看一看HttpServlet抽象类是如何实现自己的service方法吧:

首先来看GenericServlet抽象类中是如何定义service方法的:

public abstract void service(ServletRequest var1, ServletResponse var2) 

throws ServletException, IOException;

我们看到是一个抽象方法,也就是HttpServlet要自己去实现这个service方法,我们在看看HttpServlet是怎么覆盖这个service方法的:

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    HttpServletRequest request;
    HttpServletResponse response;
    try {
        request = (HttpServletRequest)req;
        response = (HttpServletResponse)res;
    } catch (ClassCastException var6) {
        throw new ServletException("non-HTTP request or response");
    }
 
    this.service(request, response);
}

我们发现,HttpServlet中的service方法把接收到的ServletRequsest类型的对象转换成了HttpServletRequest类型的对象,把ServletResponse类型的对象转换成了HttpServletResponse类型的对象。之所以能够这样强制的转换,是因为在调用Servlet的Service方法时,Servlet容器总会传入一个HttpServletRequest对象和HttpServletResponse对象,预备使用HTTP。因此,转换类型当然不会出错了。

转换之后,service方法把两个转换后的对象传入了另一个service方法,那么我们再来看看这个方法是如何实现的:

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            this.doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader("If-Modified-Since");
            if (ifModifiedSince < lastModified) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } 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);
    } 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")) {
        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);
    }
 
}

我们发现,这个service方法的参数是HttpServletRequest对象和HttpServletResponse对象,刚好接收了上一个service方法传过来的两个对象。

接下来我们再看看service方法是如何工作的,我们会发现在service方法中还是没有任何的服务逻辑,但是却在解析HttpServletRequest中的方法参数,并调用以下方法之一:doGet,doPost,doHead,doPut,doTrace,doOptions和doDelete。这7种方法中,每一种方法都表示一个Http方法。doGet和doPost是最常用的。所以,如果我们需要实现具体的服务逻辑,不再需要覆盖service方法了,只需要覆盖doGet或者doPost就好了。

总之,HttpServlet有两个特性是GenericServlet所不具备的:

1.不用覆盖service方法,而是覆盖doGet或者doPost方法。在少数情况,还会覆盖其他的5个方法。
2.使用的是HttpServletRequest和HttpServletResponse对象。

在这里插入图片描述
在这里插入图片描述

a.html文件按照第一个Servlet进行配置就行了

4.使用IDE直接创建Servlet程序

在这里插入图片描述在这里插入图片描述

5.Servlet的继承体系

在这里插入图片描述

6.ServletConfig类

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

实现Servlet程序,当我们是以继承了HttpServlet类,我们可以使用getServletConfig()方法来获取当前的Servlet对应的ServleConfigt对象,但是当我们直接实现了Servlet接口,就不能使用这个方法来获取ServleConfigt对象,因为这个方法会返回null

当我们使用继承HttpConfig实现Servlet程序,在重写init()方法的时候我们需要在重写的第一行加上一句super.init(config),因为HttpConfig类是继承了GenericServlet类,而GenericServlet类,而GenericServlet的init()方法又保存了Servlet的ServletConfig的对象(GenericServlet实现了Servlet接口)所以如果不使用super.init(config)就无法保存ServletConfig对象

7.ServletCotext类

在这里插入图片描述

在这里插入图片描述

ServletContext对象表示Servlet应用程序。每个Web应用程序都只有一个ServletContext对象。在将一个应用程序同时部署到多个容器的分布式环境中,每台Java虚拟机上的Web应用都会有一个ServletContext对象。

通过在ServletConfig中调用getServletContext方法,也可以获得ServletContext对象。

那么为什么要存在一个ServletContext对象呢?存在肯定是有它的道理,因为有了ServletContext对象,就可以共享从应用程序中的所有资料处访问到的信息,并且可以动态注册Web对象。前者将对象保存在ServletContext中的一个内部Map中。保存在ServletContext中的对象被称作属性。

ServletContext中的下列方法负责处理属性:

Object getAttribute(String var1);
 
Enumeration<String> getAttributeNames();
 
void setAttribute(String var1, Object var2);
 
void removeAttribute(String var1);

在这里插入图片描述

在这里插入图片描述

ServletContext对象只能获取<context-param>标签里边的内容不能获取<init-param>标签里边的内容,<init-param>只能由ServletConfig对象获取

在这里插入图片描述

在这里插入图片描述

我们通过ServletConfig对象获取ServletContext对象,同时我们还可以通过getServletContext()直接获取

在这里插入图片描述

ServletContext对象是在web工程启动时创建,web工程停止时销毁我们setAttribute()getAttribute()都是建立在服务器销毁销毁前,而且保存的数据可以在不同的Servlet服务器端进行获取,有点全局变量的意思
在这里插入图片描述
以上四个选项:
update resources ---- 更新静态的资源,比如html,js,css等 运行模式和调试模式都是立即生效;
update classes and resources ---- 更新java,jsp和静态资源( 1. java修改后,会被编译成.class,然后覆盖到target/kao文件夹下,IDE调试模式的情况下,立即生效。IDE运行模式下,不立即生效,需要redeployed才可生效。jsp修改后,再次被访问的时候,会自动更新,重新编译成java.class保存在tomcat的work目录下。由于是访问时才检测是否修改,是否需要重新编译,所以 IDE 运行模式 和IDEA调试模式下,都是立即生效。刷新下页面就可);
redeployed ----- 重新部署,发布到tomcat里,不重启tomcat,而是把原来的删掉,然后重新发布(此时的web工程未停止);
restart server ----- 重启tomcat。最彻底!(此时的web工程停止)

8.Http协议

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

谷歌浏览器查看http协议:

在这里插入图片描述

9.GenericServlet抽象类

前面我们编写Servlet一直是通过实现Servlet接口来编写的,但是,使用这种方法,则必须要实现Servlet接口中定义的所有的方法,即使有一些方法中没有任何东西也要去实现,并且还需要自己手动的维护ServletConfig这个对象的引用。因此,这样去实现Servlet是比较麻烦的。

void init(ServletConfig var1) throws ServletException;

幸好,GenericServlet抽象类的出现很好的解决了这个问题。本着尽可能使代码简洁的原则,GenericServlet实现了Servlet和ServletConfig接口,下面是GenericServlet抽象类的具体代码:

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
    private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");
    private transient ServletConfig config;
 
    public GenericServlet() {
    }
 
    public void destroy() {
    }
 
    public String getInitParameter(String name) {
        ServletConfig sc = this.getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        } else {
            return sc.getInitParameter(name);
        }
    }
 
    public Enumeration<String> getInitParameterNames() {
        ServletConfig sc = this.getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        } else {
            return sc.getInitParameterNames();
        }
    }
 
    public ServletConfig getServletConfig() {
        return this.config;
    }
 
    public ServletContext getServletContext() {
        ServletConfig sc = this.getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        } else {
            return sc.getServletContext();
        }
    }
 
    public String getServletInfo() {
        return "";
    }
 
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
 
    public void init() throws ServletException {
    }
 
    public void log(String msg) {
        this.getServletContext().log(this.getServletName() + ": " + msg);
    }
 
    public void log(String message, Throwable t) {
        this.getServletContext().log(this.getServletName() + ": " + message, t);
    }
 
    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
 
    public String getServletName() {
        ServletConfig sc = this.getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        } else {
            return sc.getServletName();
        }
    }
}

其中,GenericServlet抽象类相比于直接实现Servlet接口,有以下几个好处:

1.为Servlet接口中的所有方法提供了默认的实现,则程序员需要什么就直接改什么,不再需要把所有的方法都自己实现了。
2.提供方法,包围ServletConfig对象中的方法。
3.将init( )方法中的ServletConfig参数赋给了一个内部的ServletConfig引用从而来保存ServletConfig对象,不需要程序员自己去维护ServletConfig了。

public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

但是,我们发现在GenericServlet抽象类中还存在着另一个没有任何参数的Init()方法:

public void init() throws ServletException {
}

设计者的初衷到底是为了什么呢?在第一个带参数的init()方法中就已经把ServletConfig对象传入并且通过引用保存好了,完成了Servlet的初始化过程,那么为什么后面还要加上一个不带任何参数的init()方法呢?这不是多此一举吗?

当然不是多此一举了,存在必然有存在它的道理。我们知道,抽象类是无法直接产生实例的,需要另一个类去继承这个抽象类,那么就会发生方法覆盖的问题,如果在类中覆盖了GenericServlet抽象类的init()方法,那么程序员就必须手动的去维护ServletConfig对象了,还得调用super.init(servletConfig)方法去调用父类GenericServlet的初始化方法来保存ServletConfig对象,这样会给程序员带来很大的麻烦。GenericServlet提供的第二个不带参数的init( )方法,就是为了解决上述问题的。

这个不带参数的init()方法,是在ServletConfig对象被赋给ServletConfig引用后,由第一个带参数的init(ServletConfig servletconfig)方法调用的,那么这意味着,当程序员如果需要覆盖这个GenericServlet的初始化方法,则只需要覆盖那个不带参数的init( )方法就好了,此时,servletConfig对象仍然有GenericServlet保存着。

说了这么多,通过扩展GenericServlet抽象类,就不需要覆盖没有计划改变的方法。因此,代码将会变得更加的简洁,程序员的工作也会减少很多。

10.HttpServletRequest

在这里插入图片描述

因为Request代表请求,所以我们可以通过该对象分别获得HTTP请求的请求行,请求头和请求体。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在前面我们讲过,在service中使用的编码解码方式默认为:ISO-8859-1编码,但此编码并不支持中文,因此会出现乱码问题,所以我们需要手动修改编码方式为UTF-8编码,才能解决中文乱码问题,下面是发生乱码的具体细节:

在这里插入图片描述

解决post提交方式的乱码:request.setCharacterEncoding("UTF-8");

 解决get提交的方式的乱码:

parameter = newString(parameter.getbytes("iso8859-1"),"utf-8");

在这里插入图片描述

上图在获取请求参数之前调用的意思是,在设置请求体字符编码之前不调用HttpServletRequest的get之类方法,避免发生乱码

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

上图的?username=wzg168只是一种格式一般都是代表请求参数一般写法是...=...

在这里插入图片描述

在这里插入图片描述

base标签的作用:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

上面两张图可以实现c.html和index.html之间的跳转,但是当我们使用请求转发进行跳转的时候(服务
器默认打开index.html页面),再点击回到a.html的时候就会发现跳转不回去,以上代码执行顺序,服务器先默认打开index.html页面——》点击请求转发a/b/c.html——》c.html然后再点击c.html的的跳回首页就会发生错误,错误原因如下(含base标签作用):
2

我们只需要改进c.html就行,如下:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

11.HttpServletResponse

在这里插入图片描述

响应的数据通过两个流传递给客户端, javax.servlet.ServletResponse接口表示一个Servlet响应,在调用Servlet的Service( )方法前,Servlet容器会先创建一个ServletResponse对象,并把它作为第二个参数传给Service( )方法。ServletResponse隐藏了向浏览器发送响应的复杂过程。

在这里插入图片描述

PrintWriter getWriter()
获得字符流,通过字符流的write(String s)方法可以将字符串设置到response 缓冲区中,随后Tomcat会将response缓冲区中的内容组装成Http响应返回给浏览器端。

ServletOutputStream getOutputStream()
获得字节流,通过该字节流的write(byte[] bytes)可以向response缓冲区中写入字节,再由Tomcat服务器将字节内容组成Http响应返回给浏览器。

其中的getWriter方法,它返回了一个可以向客户端发送文本的的Java.io.PrintWriter对象。默认情况下,PrintWriter对象使用ISO-8859-1编码(该编码在输入中文时会发生乱码)。

在向客户端发送响应时,大多数都是使用该对象向客户端发送HTML。

还有一个方法也可以用来向浏览器发送数据,它就是getOutputStream,从名字就可以看出这是一个二进制流对象,因此这个方法是用来发送二进制数据的。

在发送任何HTML之前,应该先调用setContentType()方法,设置响应的内容类型,并将“text/html”作为一个参数传入,这是在告诉浏览器响应的内容类型为HTML,需要以HTML的方法解释响应内容而不是普通的文本,或者也可以加上“charset=UTF-8”改变响应的编码方式以防止发生中文乱码现象。

在这里插入图片描述

在这里插入图片描述

当我们write的数据是中文就可能出现乱码,那么以下是解决中文乱码的问题的方法

方法1:
在这里插入图片描述

方法2:

在这里插入图片描述

response工作流程
在这里插入图片描述

12.请求重定向

在这里插入图片描述

在这里插入图片描述

请求重定向第一种方法:

上图的setHeader方法的name参数代表新的地址,value代表新的访问地址,可以是工程外的资源比如百度等等

请求重定向第二种方法:

在这里插入图片描述

在这里插入图片描述

©️2020 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页