01EB初识Tomcat&Servlet

#Tomcat简介

使用Tomcat-8.5.31

Tomcat目录:

bin:脚本目录

启动脚本:startup.bat

停止脚本:shutdown.bat

conf:配置文件目录 (config /configuration)

核心配置文件:server.xml

用户权限配置文件:tomcat-users.xml

所有web项目默认配置文件:web.xml

lib:依赖库,tomcat和web项目中需要使用的jar包

logs:日志文件.

localhost_access_log..txt tomcat记录用户访问信息,..表示时间。

例如:localhost_access_log.2017-04-28.txt

temp:临时文件目录,文件夹内内容可以任意删除。

webapps:默认情况下发布WEB项目所存放的目录。

work:tomcat处理JSP的工作目录。

启动与结束:

双击tomcat解压目录/bin/startup.bat 文件即可启动tomcat。[http://ip:8080即可访问tomcat服务器](http://ip:8080即可访问tomcat服务器)

启动报错原因:

(1) JAVA_HOME 配置

 如果没有配置JAVA_HOME环境变量,在双击“startup.bat”文件运行tomcat时,将一闪立即关闭。

 解决方式:需要正确配置JAVA_HOME环境变量,指向JDK的bin目录的上一级目录;

(2)端口号冲突

一台拥有ip地址的主机上可以提供多个服务(启动多个软件),每一个服务都有一个唯一的端口号。所以端口号不能     重复。

使用IDEA创建一个WEB项目:

#Servlet入门

\1. 客户端(浏览器)发送的数据会被提交到服务器;

\2. 服务器必须具备:1.接收请求数据;2.处理请求数据(业务逻辑的处理);3.给浏览器生成响应信息的能力;

\3. tomcat服务器遵从了JavaEE规范,Sun公司制定的动态资源规范Servlet实现这个功能;(运行在服务器端,能够接收用户请求,处理请求数据和给浏览器生成响应信息)

\4. 在java的世界里制定规范往往是接口来实现的,所以Servlet是一个接口,里边只是定义了一些方法;

\5. 如果,我们对这个接口进行具体的实现,就能够完成对用户数据的接收,处理和生成响应信息;

1、什么是Servlet

Servlet 运行在服务端的Java小程序,是sun公司提供一套规范,用来处理客户端请求、响应给浏览器的动态web资源。

Servlet是JavaWeb三大组件之一(Servlet、Filter、Listener),是最重要的组件。

Servlet 的作用

\1. 接收请求 :接收客户端发送的请求数据;

\2. 业务逻辑的处理;

\3. 响应结果 :将处理结果响应给客户端(浏览器);

2、Servlet入门开发步骤

\1. 创建一个普通java类实现Servlet接口;

\2. 重写Servelt接口中的所有抽象方法;

\3. 注册这个类;

  1. 在web.xml中配置这个类的信息;

  2. 配置这个类的映射路径;

\4. 在service方法中处理请求和响应的业务逻辑;

\5. 发布项目,启动项目之后,通过映射路径访问这个类;

3、Servlet入门例子

【例】Servlet入门例

需求分析 :

\1. 编写一个Servlet,接收客户端(浏览器)的请求;

\2. 接收到请求之后给浏览器响应数据”Hello Servlet!”;

技术分析 :

servletResponse.getWriter().print("Hello World!");

实现步骤:

\1. 创建一个普通java类HelloServlet实现Servlet接口,并重写Servlet接口的所有抽象方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class HelloServlet 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 {

}

@Override
public String getServletInfo() {
return null;
}

@Override
public void destroy() {

}
}

\2. 在web.xml中配置这个HelloServlet的类路径和映射路径,配置信息如下;

1
2
3
4
5
6
7
8
9
10
【注意】 url-pattern 映射路径前面必须添加"/"

<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.qiezi.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

\3. 在HelloServlet的service方法中书写响应的代码逻辑;

1
2
3
4
5
6
7
8
9
        @Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("请求进来了................");
servletResponse.getWriter().print("Hello Servlet!");
}
启动tomcat,根据web.xml中的配置访问HelloServlet。
访问路径为:http://ip:8080/hello
【注意事项】
使用idea创建的web项目,可以通过配置,访问路径中不需要添加项目名,具体配置如下:

4、Servlet编程优化

我们发现在上述的HelloServlet中,实现Servlet接口后能够接收浏览器发送的请求并能够响应数据给浏览器。但是Servlet是一个接口,我们实现接口后必须重写接口中所有的方法。但是,我们在接口请求和响应数据的时候只需要一个service方法就足够了。所以,我们需要有选择地实现父类的方法。

子类继承父类时,可以根据子类的需求选择性地重写父类的方法。

4.1 方案一:GenericServlet优化方案

1) GenericServlet简介

GenericServlet类是一个抽象类,它实现了多个接口,其中有一个是Servlet,所以它重写了Servlet接口中的所有方法。我们只需要继承GenericServlet类,重写其中的service方法即可。

2)为什么要继承GenericServlet

\1. GenericServlet实现了Servlet接口,我们继承GenericServlet之后相当于间接实现了Servlet接口;

\2. GenericServlet中给我们提供了很多通用方法,这些方法已经帮我们实现了很多功能,继承GenericServlet之后可以直接调用这些方法,所以说,GenericServlet相对于Servlet,进行了功能的扩展和衍生;

\3. GenericServlet是一个抽象类,里边只有一个抽象方法service(),我们继承GenericServlet之后只需要重写service()方法处理具体的业务逻辑即可,减少了直接实现Servlet接口带来的代码冗余;

3) 案例:使用GenericServlet优化Servlet接口编程

需求分析 :使用GenericServlet重写入门案例

技术分析 :

1. 创建一个普通的java类继承GenericServlet;

2. 在web.xml中配置这个GenericServlet的子类;

3. 重写service方法,在service方法中完成向浏览器响应数据的功能;

实现步骤

\1. 编写一个普通的java类,ServletDemo1,继承GenericServlet;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.qiezi.servlet;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class ServletDemo1 extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {

}
}

\2. 在web.xml配置ServletDemo1;

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>com.qiezi.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>

\3. 重写service方法,并在service方法中书写以下代码,完成向页面响应数据;

1
2
3
4
  @Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
res.getWriter().print("Hello GenericServlet!!!!");
}

\4. 启动tomcat并访问:

4.2 方案二:HttpServlet优化方案

问题:

在实际的生活中,客户端(浏览器)给我们发送的请求往往是HTTP协议下的请求,所以我们只需要处理HTTP的请求和响应即可。也就是说我们需要的知识一个与HTTP协议相关的Servlet。

解决方案:

Sun公司为我们提供了HttpServlet,对GenericServlet再次进行扩展和功能加强。



实际开发中:我们编写Servlet就可以采用 继承HttpServlet来完成servlet的开发,这样的Servlet我们可以获取更多的业务功能。

1)HttpServlet简介:

HttpServlet是GenericServlet的一个子类。这个类是专门帮我们处理与HTTP协议相关的请求与响应的一个Servlet类,它里边的方法如下:

因为,在我们的日常生活中最常用的HTTP请求只有get请求和post请求。所以我们在继承了HttpServlet之后只需要重写里边的doGet()方法和doPost()方法即可满足我们的需求:

2)为什么要继承HttpServlet

\1. HttpServlet继承了GenericServlet,功能比GenericServlet更强大;

\2. GenericServlet中处理不同的请求都使用service()方法,HttpServlet根据不同的请求方式提供了不同的方法进行处理。等于是细化了service()方法;

\3. HttpServlet类中的方法参数为HttpServletRequest对象和HttpServletResponse对象。使用这两个对象的好处在于:

  1. HttpServletRequest继承了ServletRequest,HttpServletResponse继承了ServletResponse。功能更强大;

  2. HttpServletRequest和HttpServletResponse为我们提供了很多处理HTTP协议相关的API,HTTP协议正是我们日常生活中使用得比较多的一种协议;

(3)HttpServlet编程

1
2
3
4
5
6
7
8
9
10
11
12
【Servlet优化二】HttpServlet编程
public class FormServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("处理get请求提交的数据.................");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("处理post请求提交的数据..................");
}
}

(4)请求是怎么到达doGet()和doPost()的

在我们继承HttpServlet之后,重写了doGet()和doPost()方法。浏览器发送的get请求会被doGet()接收并处理,浏览器发送的post请求会被doPost()请求接收并处理。服务器端是怎么辨别我的请求是get还是post请求的呢?下面,我们看一下HttpServlet的源码中的service方法:

\1. 实际处理请求的还是HttpServlet的service()方法;

\2. service方法中,接收请求之后获取了请求方式,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

//获取数据的请求方式
String method = req.getMethod();
//根据请求方式的不同,调用不同的方法
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
//调用doGet()方法
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)) {
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()方法
} 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 {
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);
}
}

4.3 Servlet编程优化小结

\1. Servlet编程的作用:接收浏览器的请求,处理请求数据,给浏览器响应数据;

\2. Servlet接口:

  1. Sun公司规定如果想要在服务端接收和响应客户端的数据,必须实现Servlet接口;

  2. 由于Servlet是一个接口,实现Servlet接口后必须重写其中所有的方法;

\3. GenericServlet:

  1. GenericServlet:是一个抽象类,它实现了Servlet接口,与协议无关;

  2. 继承GenericServlet,只需要重写service方法即可接收和响应数据;

\4. HttpServlet:

  1. HttpServlet:是一个抽象类,它继承了GenericServlet,为HTTP请求定制的;

  2. 继承HttpServlet,重写doGet和doPost方法,能够更加方便地处理不同请求的数据;

5、Servlet生命周期

生命周期指的是一个对象从创建到销毁的过程。我们的Servlet是由谁创建的,何时创建的,创建之后做了哪些工作,工作完成之后又是何时销毁的呢?下面我们就来看一下Serlvet的生命周期。
  1. 谁创建:tomcat创建;

  2. 何时创建:第一次访问这个Servlet的时候,只创建一次,创建完成后会调用init()方法;

  3. 如何工作:每次请求到这个Servlet时,由service方法处理请求和响应信息;

  4. 何时销毁:服务器正常关闭,销毁前会调用destory()方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
【测试代码】

public interface Servlet {

/*
* Servlet对象创建的时候会调用init()方法
*/
public void init(ServletConfig config) throws ServletException;

/*
* Servlet对象创建后,每次的请求都由service方法处理
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;

/*
* Servlet对象销毁前会调用destory方法。主要是用于释放一些资源。
*/
public void destroy();
}

【实现类】

package com.qiezi.user.web;

import javax.servlet.*;
import java.io.IOException;

public class LifeCycleTestServlet implements Servlet {

public LifeCycleTestServlet() {
System.out.println("LifeCycleTestServlet对象被创建了...........");
}

@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init方法被调用了.............");
}

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service方法被调用了............");
}

@Override
public void destroy() {
System.out.println("destory方法被调用了........");
}


@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
}

【小结】

  1. Servlet对象的创建:

  2. 创建者:tomcat;创建时机:在Servlet第一次被访问的时候;

  3. 特点:

  4. 只会创建一次(单例);

  5. 对象创建完成之后,会调用init()方法;

  6. Servlet运行:

  7. service()方法接收用户的请求,和处理响应;

  8. 特点:

  9. 每次对这个Servlet的访问都会由service()方法来处理;

  10. service()方法的两个参数:request和response对象专门用来处理请求和响应;

  11. Servlet销毁:

  12. Servlet对象在服务器正常关闭的时候销毁;

  13. 特点:

  14. 销毁的时候会调用destory()方法;

【注意】Servlet对象是单例的,会不会产生线程安全问题?

答:线程安全问题的产生是因为要争夺共同的资源,所以只要不在Servlet类中设置成员变量就可以避免。

6、 Servlet映射路径配置规范

url-pattern中的映射路径配置存在以下几种方式:

1. 完全匹配           /user/hello          资源路径为/user/hello时可以访问

2. 目录匹配            /user/*                 资源路径中含有/user目录均可访问

3. 后缀名匹配        *.do                 资源路径中以.do结尾的均可访问

4. 缺省路径            /                     访问的路径找不到,就会去找缺省路径

tomcat获得匹配路径时,优先级顺序:1 >2 > 3 > 4

开发中一般使用完全匹配,即一个Servlet对应一个映射路径。

\1. 配置servlet的实现类信息:

1
2
3
4
5
<!--1.配置HelloServlet的类路径-->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.heima.servlet.HelloServlet</servlet-class>
</servlet>

\2. 配置这个实现类的映射路径:

1
2
3
4
5
<!--2.配置映射路径,映射到HelloServlet-->
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/user/hello</url-pattern>
</servlet-mapping>

\3. url-pattern配置规则

1
2
3
4
5
6
7
8
9
10
11
<!--1.配置HelloServlet的类路径-->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.qiezi.servlet.HelloServlet</servlet-class>
</servlet>

<!--2.配置映射路径,映射到HelloServlet-->
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

【关于缺省路径】web.xml

URL输入http://localhost:8080/ 如何读取文件?

1)到当前WEB应用下的web.xml文件查找是否有匹配的url-pattern。

2)如果没有匹配的url-pattern,则交给tomcat的内置的DefaultServlet处理。

3)DefaultServlet程序到WEB应用的根目录下查找是否存在一个名称为index.html的静态文件。

4)如果找到该文件,则读取该文件内容,返回给浏览器。

5)如果找不到该文件,则返回404错误页面。

7.服务器启动,立刻加载Servlet对象

问题:普通的Servlet对象在我们第一次访问的时候创建, 开发中,如果我们需要在服务器启动的时候,初始化Servlet对象应该如何编写呢?

需求:服务器tomcat启动,立刻加载配置文件,获取配置文件信息,为项目后续代码提供数据支持。

解决:此时的业务场景,我们需要在web.xml文件中给指定的Servlet添加一个标签

实现步骤:在web.xml中在标签内部中配置:

2 — 传入正整数,整数越小,被创建的优先级就越高。12345

#Servlet3.0注解开发

在我们上面创建的Servlet中,Servlet的配置信息都是在web.xml中完成的。如果,我们创建的Servlet非常多,就会导致web.xml中的配置非常臃肿,不便于管理。Servlet3.0为我们提供了注解支持,创建Servlet的时候。在这个Servlet类上面添加注解就可以完成Servlet的配置。Servlet3.0除了提供注解支持,还提供了以下新特性:

1、Servlet3.0新特性

- 注解支持

该版本新增了若干注解,用于简化 Servlet、过滤器(Filter)和监听器(Listener)的声明,这使得 web.xml 部署描述文件从该版本开始不再是必选的了。

- 支持web模块(servlet filter Listener)

- 支持Servlet异步处理

- 文件上传API简化

【注意】详细信息请参考Servlet3.0新特性全解

2、为什么要使用注解

\1. web.xml中配置过多servlet不便于管理,容易出错;

\2. 注解开发使得开发更敏捷,效率更高;

\3. 注解开发是一种趋势;

3、使用IDEA创建3.0版本的Servlet

新版的IDEA(2017版本)创建的Servlet默认是3.0版本的,所以我们只需要新建一个Servlet,然后用注解配置即可。具体步骤如下:

第一步:新建一个Servlet;

在包上面点击鼠标右键New-->Servlet

给这个Servlet取个名,然后点击OK

第二步:配置Servlet

创建完成后,Servlet类上面会默认添加一个注解@WebServlet(name="Servlet3Demo") 。这个@WebServlet注解就是用来配置当前这个Servlet的。这个注解中常用的有两个属性:

\1. name属性: 相当于web.xml的

\2. urlPatterns属性: 编写访问servlet的路径 类似于

所以,我们需要手动在@WebServlet 注解中添加urlPatterns属性,来设置映射路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.qiezi.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/*
* 1. name属性: 相当于web.xml的 <servlet-name>;
* 2. urlPatterns属性: 编写访问servlet的路径 类似于 <url-pattern>
*/
@WebServlet(name = "Servlet3Demo",urlPatterns = "/servlet3")
public class Servlet3Demo extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}
}

4、优化

4.1 优化一

我们继承HttpServlet之后,重写doGet()方法处理get请求,重写doPost()方法处理post请求。但是,浏览器发送的请求到底是get请求还是post请求,我们在书写Servlet代码的时候并不知道。所以,需要在doPost()方法中调用doGet()方法,或者是在doGet()方法中调用doPost()方法。这样我们只需要在一个方法中处理请求了。

4.2 优化二

基于IDEA的模板,快速创建Servlet。

【模板配置代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")
@javax.servlet.annotation.WebServlet(urlPatterns="/${Entity_Name}")
public class ${Class_Name} extends javax.servlet.http.HttpServlet {

@Override
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
doPost(request, response);
}

@Override
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {

}
}

5、Servlet开发最终版本

1
2
3
4
5
6
7
8
9
10
@WebServlet("/lastDemo")
public class LastDemoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}
}

Powered by Hexo and Hexo-theme-hiker

Copyright © 2016 - 2018 Francis的个人博客 All Rights Reserved.

UV : | PV :