Servlet

什么是Servlet?Servlet是一种实现动态页面的技术,是Tomcat提供给程序员的一组api。Servlet的工作就是让程序员自己写一些类,加载到Tomcat上,后续Tomcat收到来自浏览器的请求,就会执行上述代码,通过代码完成业务逻辑

一.第一个servlet程序

我们先来写一个hello world程序。主要有七个步骤

1.创建项目

这里我们要创建的是一个新的项目形式,Maven项目。

maven是Java中知名的构建工具,是帮助编译打包代码的工具。但是javac不就是编译代码吗,idea不是可以自动调用javac吗?若是简单的代码,怎么搞都行,但对于涉及到复杂项目的,可能会有一系列依赖关系,maven就可以把依赖管理好。而且maven的打包功能也很强大,也方便集成到自动化环境中。而idea是六边形战士,专论编译打包,就不如maven。

如何使用maven?不用安装,idea已经集成了maven。

新建项目,构建系统勾选maven,选择jdk1.8,不适用模板

创建好以后,有很多目录结构:

其中,在src中放置业务代码,main下面的java中放Java代码,resourse中式代码运行中所需要的资源(图片,logo等)。pom.xml放的是maven项目的入口配置文件

2.引入依赖

我们在代码编辑过程中会用到Servlet,Servlet api是Tomcat提供的,而不是jdk提供的,所以需要手动安装引入。如果是手动下载导入,会很麻烦,但有maven,它可以帮我们引入。我们只需要在pom.xml中引入Servlet api依赖的的jar包即可

打开maven中央仓库mvnrepository.com,输入servlet,一般第一个就是,点进去,选择和Tomcat对应的版本(比如:Tomcat8系列,对应Servlet的3.1.0

把里面的xml复制到项目对应的pom.xml中,也就是下图的这一段:

复制到dependencies标签之间:

点击右上角的刷新按钮

这样,jar包就下载好了,它被下载到了maven的本地仓库中,maven的本地仓库如何找?点击files->settings->Build,E……->bulid tools->maven,在右面下面的local repository就是本地仓库。点开此路径,找到javax,一路点进去,点javax-servlet,javax-servlet-api,就是jar包的保存位置

3.创建目录

maven项目是普适的,不仅仅局限于Tomcat,但是Tom对于项目目录由很高的要求

在main下创建webapp目录,在webapp下创建WEB-INF,在WEB-INF下创建web.xml文件,这就是tomcat要求的结构,web.xml中的内容是固定的,找模板复制就行

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Archetype Created Web Application</display-name>
</web-app>

把这一段复制上,报错也没事。

webapp 目录就是未来部署到 Tomcat 中的一个重要的目录 . 当前我们可以往 webapp 中放一些
静态资源 , 比如 html , css
在这个目录中还有一个重要的文件 web.xml. Tomcat 找到这个文件才能正确处理 webapp 中的
动态资源 .

4.编写代码

在java目录中船舰一个HelloServlet类,继承自HttpServlet。HttpServlet不再标准库中定义,它来自于Servlet的jar包。类上面还需要有一个注解,表示Tomcat收到的请求中,路径为注解中的内容的才会交给这个类来处理

重写doGet方法,这个方法就会在收到get请求时触发

这个方法由两个参数,分别表示收到的http请求和即将构造的http响应。doget这个方法要做的工作大致分为三步:

1.读取请求并解析:http请求是字符串,对他进行解析就是将它构造成HttpServletResponse对象,而这个工作是Tomcat自动完成的,不需要咱们进行干预

2.根据请求计算响应,这才是doGet核心代码,是doget真正要做的工作

3.把响应对象返回成字符串,这也是Tomcat自动完成的

第一个打印时显示在服务器控制台上面的,在Tomcat上也显示。而下面就是在将它作为响应的body部分,最终会显示到浏览器页面上。resp.getWriter()会获取到一个流对象,通过流对象吸入的数据就会存到响应的body部分,然后tomcat会将响应转成字符串,通过Socket协会给浏览器

5.打包程序

我们要将写好的程序打包成war包:

首先修改pom.xml,设置打包的类型和包名字,名字自己定义即可

war包和jar包的区别:jar时java中更通用的发布方式,任何Java程序都可以打包成jar包,吉安人里面主要就是抱恨.class文件。而war包时Tomcat专属的压缩包,war里面不仅有.class文件,还有tom要求的配置文件web.xml,还有一些前端代码HTML,CSS等

完成上面的操作后,就在右侧的maven面板中,点击项目,点击lifecycle,找到package并且双击

打包成功就会在target目录下出现一个war包:

6.部署程序

把war包拷贝到Tomcat的webapps目录下即可

通过target目录打开刚刚大巴票好的war包,然后进行拷贝

点击explorer就能找到这个包。然后ctrl+c复制即可

然后启动Tomcat,tomcat就会自动将这个包解压缩

7.验证程序

通过浏览器访问

127.0.0.1:8080/java110/hello

其中,路径一java110在这里时war包的名字,/hello是我们在注解中填写的路径

此时就在浏览器中看到了我们所写的hello。

更方便的部署方式

但要是我们想改一下响应的body部分,就要重新修改代码,然后重新进行打包,重新进行程序部署。这非常麻烦,所以程序员就会借助idea上的插件,把Tomcat集成到idea上,然后idea就可以一键进行打包和部署。

所以我们现在要给idea安装SmartTomcat插件:Settings->Plugins->选择MarketPlugin->搜索SmartTomcat进行安装。

首次使用SmartTomcat很麻烦,点击右上角currentFile里面的Edit Configurations,点加号,SmartTomcat:name随便,TomcatServer设置Tomcat在哪个目录,contentPath决定了浏览器访问程序时的第一级路径,之前我们没用SmartTomcat时使用的是war包名字,现在我们可以自己手动配置,不配置就是默认是项目名字)(url中的第二级路径,叫做servletPath)

但是当我们点击运行时,程序退出了,表示Tomcat退出了。这是为什么?

看一下报错信息:说是当前要绑定的端口号已经被占用。我们知道Tomcat要绑定的端口号由两个一个是8080(业务端口),另一个是8005(代理端口)。被谁占用了?就是刚刚我们打开了Tomcat没有关闭。关了就成功了。

然后在浏览器中的地址栏输入127.0.0.1:8080/hello_world/hello,就可以在浏览器显示hello,并在服务器控制台出现hello

二.访问出错怎么办

1.出现404

访问不存在,可能是url写错了,也可能是webapp没有正常加载(也就是web.xml没有写对)

2.出现405

方法没有被实现

get请求没有写doget,或者doget中没有删除super.duGet()

3.出现500

服务器内部出错(代码执行异常)在日志或响应中会有异常信息

如上编写代码,就会出现

4.出现空白页面

检查是否返回了带有响应正文的响应报文,也就是是否调用了getWriter().write()。

5.无法访问某些网站

tomcat没有正常运行

比如注解没有加斜杠

三.Servlet详细工作流程

1。接受请求

用户在浏览器地址栏输入url,就会发出一个http请求,这个http请求会进更层层封装,形成二进制字节流,最终通过物理层的阴间设备转换成观点信号传输出去。这些观点信号通过互联网上的一系列网络设备,最终到达目标主机。服务器对这些观点信号进行层层分用,层层解析,最终还原成http请求,并交给Tomcat进程进行处理(根据端口号确定进程)。tomcat通过socket读取请求(一个字符串),并按照http格式进行解析,根据请求中的contentPath确定一个webapp,根据ServletPath确定一个类,再根据当前请求的方法决定调用哪个方法,是doGet还是doPost……

2.根据请求计算响应

这就是重写方法中执行到的,这就是我们程序员编写的

3.返回响应

我们自己写的方法执行完毕后,tomcat就会将这个响应对象转换成一个http协议的字符串,通过Socket将其转发出去。这个过程和接收请求的过程类似

Tomcat的工作

我们发现再Servlet中没有写main方法就能够运行。这是为什么?因为咱们写的程序不是直接运行的,而是放到Tomcat中运行的,tomcat中就有main,而我们的webapp就是放到里面运行的。继承HttpServlet重写里面的方法就是为了把自己定义过代码插入到Tomcat中执行

四.Servlet API

1.HttpServlet

这是我们自定义的类要继承的父类

init方法:用于完成初始化操作,会在HttpServlet被实力胡后调用一次

destory方法:servlet被销毁前用来释放资源,再实例不再使用时调用

service方法:每次收到一个http请求就会自动被调用

doGet,doPost.doDelet……

destroy方法很大概率是执行不到的,因为一个Servlet不用了,就说明Tomcat要关闭了,Tomcat关闭有俩种路径:1.Tomcat进程干掉了(直接在任务管理器中结束进程/直接点X)此时根本来不及调用destroy  2.通过8005管理端口给Tomcat发布停机指令,这是才能执行destroy方法

2.HttpServletRequest

当tomcat接收到http请求后,就会将http字符串转化成HttpServletRequest对象

核心方法

String getProtocol() 返回请求协议的名称和版本号

String getMethod()返回请求方法

String getRequestURI()注意,是uri,不是url,uri表示唯一资源标识符,相当于身份证号。返回的是该请求中url的一部分

String getContentPath()返回第一级路径

String getQueryString()

接下来三个方法是和query String的key和value相关的

Enumeration getParameterNames()返回一个String对象的枚举,包含在该请求中的url中的参数的名字集合

String getParameter(String name)通过传入的参数名字,返回对应参数的值

String[] getParameterValues(String name)通过传入一个参数名,返回请求中该参数的所有值

接下来两个方法是对请求的Header中的键值对进行操作

Enumeration getHeaderNames()

String getHeader(String name)返回指定请求头的值

下面三个方法是对header进行了简单的封装

String getCharacterEncoding()返回请求的body中使用的字符编码方式

String getContentType()返回请求的body的数据组织形式

int getContentLength()返回请求的body的长度

InputStream getInputStream()得到一个流对象。读取这个流对象就会得到整个请求的body部分。很多时候,再代码中表示一个不定长的数据时,常使用流对象。使用它就允许咱们不必一次性处理完所有内容,可以一次读取一部分(要是用String去存储,就会太长,不方便操作)。而且使用它能够更好的兼容二进制数据(而String不能读取二进制数据)

代码示例

常规方法

我们来写一下这个测试类,通过调用上述api,获取到各种结果,并将结果拼接成字符串,这里的拼接就用到了StringBuilder类

其中<br>是StringBuilder中的一个特殊标签,代表了换行符。因为在html中,\n不能起到换行的作用。

注意getHeaderNames返回值是一个枚举数组,所以要用while循环

然后我们分别用浏览器和postman发送请求

这是浏览器获取到的

这是postMan中获取到的

参数获取方法

然后来看一下获取参数及其值的方法:

queryString传参

首先我们约定请求中的queryString是username=……,password=……,然后进行代码编写

最后在postman中发如下请求后得到如下响应的正文:

为什么username是??呢?注意,当queryString中是中文等字符时,就要进行urlencoded,urlencoded之后,就能看到正确的响应:

除了使用query String方式来传递参数,还可以使用请求的body来传递参数,那么我们就应该发post请求,如下

直接通过form表单形式发送请求

服务器端代码如下:

postman中选取body为x-www-form-urlencoded,如下:

直接使用json

上面说的几种方式都是Servlet天然支持的,但是json不是Servlet支持的,所以要额外引入第三方库,这里我们使用jackson库。我们只需要通过maven即可下载导入jackson到项目中。还是同样的操作,在maven中央仓库中搜索jackson,找到下面的,点进去,选择2.15.0版本,复制粘贴并打包即可

使用jackson的关键的一个类就是ObjectMapper(object表示对象,mapper表示映射,所以可以简单理解为对象映射器),它的作用就是将json字符串映射成java对象(使用read方法)/把Java对象映射成json字符串(用writeAsString方法)。

在网络传输过程中,使用的是json字符串,而在java代码中使用的是java对象。站在服务器角度,它显式收到了json字符串,所以要先映射成java对象再处理业务,返回时就又要映射成json字符串方便传输。代码如下:

首先我们来约定前后端交互接口:

前端请求:

POST /json

Content-Type:application/json

{

     username:'张三'

     password:'123'

}

响应(约定也是用json数组):

HTTP/1.1 200 OK

Content-Type:application/json

{

    OK:true

}

后端服务器代码如下:

class Request{
    public String username;
    public String password;
}
class Response{
    public boolean OK;
}
@WebServlet("/json")
public class JsonParameterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.把请求的body按json格式解析成java对象
        ObjectMapper objectMapper=new ObjectMapper();
        Request request=objectMapper.readValue(req.getInputStream(),Request.class);
        System.out.println("username:"+request.username);
        System.out.println("password:"+request.password);
        Response response=new Response();
        response.OK=true;
        String respJson=objectMapper.writeValueAsString(response);
        resp.setContentType("application/json;charset=utf8");
        resp.getWriter().write(respJson);
    }
}

这里我们需要准备一个request类和response类,首先用ObjectMapper中的readValue方法将请求中的json字符串转化成java对象。工作过程如下:

1.此处的readValue方法第一个参数就是把请求的流对象传入,这样,readValue就会读取到InputStream中的所有数据(也就是http请求的body部分)

2.将读取的数据按json的格式进行解析,把json字符串解析成Map(key+value)

3.把map转换成java对象。要转换成哪一个类?就是根据第二个参数来判断。Request.class告诉了readValue内部,要将字符串换转成此java对象。readValue内部就可以通过反射api,创建出Request的实例,并根据request提供的属性名字查询Map,把得到的结果沪指给对应的属性

.class类对象复习:

java代码,被javac编译成.class文件,它是一个二进制文件,包含了java源代码中的核心信息,比如类名、类属性、方法名、方法权限、父类、接口等

java启动进程,就会读取.class文件,将二进制内容读到内存中并解析(这叫做类加载)。

加载完后就会再内存中得到类对象,其中包含了.class的所有信息。

类对象就约等于是类的图纸,后续构造类的实例就是基于类对象展开的。

synchronized修饰static方法就是相当于给类对象加锁

当我们要构造一个body为json格式的响应时,也需要将一个java对象转换成json格式,所以要有一个Response类。通过writeAsString方法将java对象转换成json字符串。工作过程如下:

1.通过传入的reqponse对象获取到Response的类对象,通过反射拿到都有哪些属性

2.根据属性名字,拿到属性的值

3,把上述属性的名字和值构造成json字符串(即String),就是方法的返回值

这样,服务器端代码就写完了,现在我们在postman中发送一个post请求,body使用json,如下:

3.HttpServletResponse

核心方法

void setStatus()为响应设置状态码

void setHeader(String name,String value)设置带有给定名称和值的header,如果name已经存在,则覆盖旧值

void addHeader(String name,String value)添加带有给定名称和值的header,如果name已经存在,不覆盖旧值,并列添加新的键值对

void setCharacterEncoding(String charset)设置响应的字符编码集

void setContentType(String type)设置被发送到客户端的响应的body类型。同时也可以设置字符集,如下:setContentType("application/json;charset=utf8")

void sendRedirect(String location)这个方法可直接构造重定向响应,并发送给客户端

PrintWriter getWriter()用于往body中写入文本格式的数据

OutputStream getOutputStream()用于往body中写入二进制格式的数据

最后这两个方法,一个是字符流,一个是字节流。因为行营都body也是通过流对象体现的,二此处的写入body站在cou角度就是输出

代码示例

设置状态码

抓包查看响应:

发现就是404,但是用浏览器打开后,是一个空白页面,不显示404。所以我们应该手动设定错误信息,如下:

最终结果如下:

设置Header

例如:设置refresh=1,让浏览器每一秒刷新一次

然后在浏览器地址栏中输入url就看容易看到每一秒刷新一次的效果

构造重定向

重定向的状态码是3xx,并且响应的header要搭配一个location属性,来描述要跳转到哪个页面

然后再浏览器地址栏输入对应的url,再用fiddler抓包,结果如下:

相关推荐

  1. <span style='color:red;'>Servlet</span>

    Servlet

    2024-04-05 21:14:03      31 阅读
  2. <span style='color:red;'>servlet</span>

    servlet

    2024-04-05 21:14:03      42 阅读
  3. <span style='color:red;'>Servlet</span>

    Servlet

    2024-04-05 21:14:03      15 阅读
  4. <span style='color:red;'>Servlet</span>

    Servlet

    2024-04-05 21:14:03      19 阅读
  5. servlet

    2024-04-05 21:14:03       10 阅读
  6. <span style='color:red;'>Servlet</span>

    Servlet

    2024-04-05 21:14:03      7 阅读
  7. SpringMVC-Servlet

    2024-04-05 21:14:03       42 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-05 21:14:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-05 21:14:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-05 21:14:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-05 21:14:03       20 阅读

热门阅读

  1. react 组件:Suspense

    2024-04-05 21:14:03       16 阅读
  2. 物理安全和逻辑安全在信息安全中的重要作用

    2024-04-05 21:14:03       15 阅读
  3. vue3+uniapp 动态渲染组件,兼容h5、app端

    2024-04-05 21:14:03       15 阅读
  4. 顺序表的动态实现

    2024-04-05 21:14:03       13 阅读
  5. 003 CSS介绍

    2024-04-05 21:14:03       17 阅读
  6. sass中的导入与部分导入

    2024-04-05 21:14:03       16 阅读
  7. MATLAB /Simulink 快速开发STM32 --学习收获

    2024-04-05 21:14:03       15 阅读