1. Tomcat

1.1 Hello Tomcat

Tomcat是常见的免费的web服务器。独立达到提供web服务的效果。

不使用tomcat打开html页面,可以看到输入框中是html文件的绝对路径,而是用tomcat后,可以127.0.0.1: 8080/test.html像访问一个网站似的,访问一个html文件了。这是因为tomcat本身是一个web服务器,test.html部署在了这个web服务器上,所以可以这样访问。

安装和启动Tomcat参考我的另一篇博客安装Tomcat

部署一个简单的html测试一下效果,把html文件复制到Tomcat目录下的webapps\ROOT文件夹下。就可以通过localhost:8080/test.html进行访问了。

1.2 改端口

Tomcat默认的端口号是8080,可以通过配置把端口号改成web服务默认的端口号80.

打开conf\server.xml文件,查找8080,将其改为80。

此时就可以通过127.0.0.1/test.html来访问网页了。

1.3 部署

如何正确部署一个J2EE应用。

首先准备一个J2EE应用,解压到某个目录下。

打开server.xml,在host节点中写入下列语句:

1
<Context path="/WebProject" docBase="D:/WebProject" reloadable="true" />

其中:

  • path:浏览器访问时的路径名
  • docBase:web项目的webRoot所在的路径,注意时webRoot的路径,不是项目的路径,其实也就是编译后的项目。
  • reloadable:设定项目有改动时,tomcat是否重新加载该项目。

重启Tomcat,在输入框中输入127.0.0.1/WebProject/hello访问项目。

2. Servlet

2.1 Servlet 简介

Java Servlet是运行在Web服务器或应用服务器上的程序,它是作为来自Web浏览器或其它HTTP客户端的请求和HTTP服务器上的数据库或应用程序之间的中间层。

使用Servlet,可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。

主要任务:

  • 读取客户端(浏览器)发送的显式数据。包括网页上的HTML表单。
  • 读取客户端(浏览器)发送的隐式的HTTP请求数据。包括cookies、媒体类型和浏览器能理解的压缩格式等。
  • 处理数据并生成结果。这个过程可能要访问数据库。
  • 发送显式的数据到客户端(浏览器)。数据格式多种多样,包括文本文件(HTML或XML)、二进制文件、Excel等。
  • 发送隐式的HTTP响应到客户端(浏览器)。

2.2 Hello Servlet和获取参数

首先一个web项目的目录结构如下:

- j2ee
    - src
    - web
        - WEB-INF
            - classes

  • src目录下放java写的Servlet类
  • WEB-INF目录下放置xml配置文件
  • classes目录下放置编译好的类
  • web目录下放置html文件

HelloServlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

public class HelloServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response) {
try {
response.getWriter().println("<h1>Hello Servlet!</h1>");
response.getWriter().println(new Date().toString());
} catch (IOException e) {
e.printStackTrace();
}
}

}

LoginServlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginServlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
String password = request.getParameter("password");

System.out.println("name:" + name);
System.out.println("password:" + password);
}

}

在WEB-INF目录下新建web.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8" ?>
<web-app>
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>LoginServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
</web-app>

使用1.3的方法,将项目部署在Tomcat上面。

在地址框中输入127.0.0.1/web/hello,可以看到如下网页,且每次刷新网页,显示的是当前时间。

输入127.0.0.1/web/login.html,显示如下界面

输入账号、密码,点击登录,可以在Tomcat服务端获取到数据。

2.3 响应

验证是否登录成功,本来应该访问数据库的,这里为了简便直接在内存中进行验证,如果用户名是admin,密码是123即为成功,否则为失败。

使用response.getWriter().println()打印在html上。

注意:为了防止未进行输入就点击登录,获取到null,导致空指针异常,这里把常量写在前面!

LoginServlet

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
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginServlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
String password = request.getParameter("password");

String html = null;

System.out.println("name:" + name);
System.out.println("password:" + password);

if ("admin".equals(name) && "123".equals(password)) {
html = "<div style='color: green'>success</div>";
} else {
html = "<div style='color: red'>fail</div>";
}
response.getWriter().println(html);
}

}

2.4 调用流程

① 访问127.0.0.1/web/login.html,打开一个静态的html页面,在这个页面通过form,以post的形式提交数据。
② 用form,以post形式把账号、密码提交到/login路径
③ Tomcat接收到一个新的请求:127.0.0.1/login,接着和配置文件web.xml进行匹配,发现/login对应的Servlet类是LoginServlet。找寻LoginServlet
④ Tomcat定位到LoginServlet后,发现并没有LoginServlet的实例存在,于是调用LoginServlet的public无参的构造方法实例化一个LoginServlet对象。
⑤ Tomcat拿到LoginServlet实例之后,根据post形式调用对应的doPost方法。
⑥ 进入doPost方法,通过request取出账号密码信息。
⑦ 接着,判断账号密码是否正确,创建不同的html字符串,然后把html字符串通过response.getWriter().println(html)设置在response对象上。
⑧ 在Servlet完成工作后,Tomcat拿到被Servlet修改过的response,根据这个response生成html字符串,然后通过HTTP协议,回发给浏览器。浏览器在根据HTTP协议获取这个html字符串,并渲染在界面上,看到最终效果。

2.5 service()

在根据post或get形式分别调用doPost或doGet方法之前,都会先执行service()方法,根据方法继续调用doPost或doGet方法。

所以有时可以直接重写service()方法,在其中提供相应的服务,就不用区分到底是post还是get了。

2.6 中文问题

request中文设置:在获取账户密码时,对request的编码进行设置 request.setCharacterEncoding("utf-8");,放在request.getParameter()之前。

response中文设置:在response中设置编码方式response.setContentType("text/html; charset=utf-8"),放在response.getWriter()之前。

此外,login.html也需要utf-8编码。

2.7 生命周期

1
2
3
4
5
6
st=>start: 实例化
op1=>operation: 初始化
op2=>operation: 提供服务
op3=>operation: 销毁
en=>end: 被回收
st->op1->op2->op3->en

这里的Servlet类是单例的。

2.8 跳转

在web目录下新建success.html和fail.html,分别用于显示登录成功和登录失败。

  • 服务端跳转(转发)
    在判断密码正确的条件语句下修改为
    1
    request.getRequestDispatcher("success.html").forward(request, response);
    跳转后可以看到浏览器的地址仍然是/login路径
  • 客户端跳转(重定向)
    在判断密码错误的条件语句下修改为
    1
    response.sendRedirect("fail.html");
    跳转后了浏览器地址变为fail.html

2.9 自启动

有时候会有这样的业务需求:Tomcat一启动,就需要执行一些初始化的代码,比如校验数据库的完整性等。但是Servlet的生命周期是用户访问浏览器对应的路径开始的,如果没有用户的第一次访问,就无法执行相关代码。

此时就需要Servlet实现自启动,伴随着Tomcat的启动,自动初始化,在初始化方法init()中,就可以进行一些业务代码的工作了。

init方法

1
2
3
public void init(ServletConfig config) {
System.out.println("init of Hello Servlet");
}

在web.xml中的servlet标签下添加

1
<load-on-startup>10</load-on-startup>

其中取值范围是1-99,数字越小,启动的优先级越高。

2.10 request常见方法

方法 作用
request.getRequestURL() 浏览器发出请求时的完整URL,包括协议 主机名 端口(如果有)
request.getRequestURI() 浏览器发出请求的资源名部分,去掉了协议和主机名
request.getRemoteAddr() 浏览器所处于的客户机的IP地址
request.getRemoteHost() 浏览器所处于的客户机的主机名
request.getRemotePort() 浏览器所处于的客户机使用的网络端口
request.getLocalAddr() 服务器的IP地址
request.getLocalName() 服务器的主机名
request.getMethod() 得到客户机请求方式——GET或POST

获取参数

  • request.getParameter() 用于获取单值的参数
  • request.getParameterValues() 用于获取多个相同值的参数,返回list
  • request.getParameterMap() 用于遍历所有参数,并返回Map类型

获取头信息

  • request.getHeader() 获取浏览器传递过来的头信息
  • request.getHeaderNames() 获取浏览器所有的头信息名称,根据头信息名称就能遍历出所有的头信息

2.11 response常见方法

设置响应内容

  • response.getWriter() 获取一个PrintWriter对象,使用println(), append(), write(), format()等等方法设置返回给浏览器的html内容

设置响应格式

  • response.setContentType() 括号中如果填写了”text/html”,那么浏览器可以识别这种格式,如果换一个其他格式,比如”text/lol”,浏览器不能识别,那么打开此servlet就会弹出一个下载的对话框,从而可以用这种方法实现下载功能。

设置响应编码

  • response.setContentType()
  • response.setCharacterEncoding()
    这两种方式都需要在response.getWriter调用之前执行才能生效。
    二者区别:前者不仅发送到浏览器的内容按设置编码,且告诉浏览器使用该种编码方式进行显示;后者只是发送到浏览器的内容按设置编码。

重定向

  • response.sendRedirect()

设置不使用缓存

  • response.setDateHeader(“Expires”, 0);
    response.setHeader(“Cache-Control”, “no-cache”);
    response.setHeader(“pragma”, “no-cache”);
    使用缓存可以加快页面的加载,降低服务端的负担。但是也可能看到过时的信息,可以通过上面通知浏览器不要使用缓存。

3. 参考