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
18import 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
17import 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
<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,导致空指针异常,这里把常量写在前面!
LoginServlet1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26import 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 | st=>start: 实例化 |
这里的Servlet类是单例的。
2.8 跳转
在web目录下新建success.html和fail.html,分别用于显示登录成功和登录失败。
- 服务端跳转(转发)
在判断密码正确的条件语句下修改为跳转后可以看到浏览器的地址仍然是/login路径1
request.getRequestDispatcher("success.html").forward(request, response);
- 客户端跳转(重定向)
在判断密码错误的条件语句下修改为跳转后了浏览器地址变为fail.html1
response.sendRedirect("fail.html");
2.9 自启动
有时候会有这样的业务需求:Tomcat一启动,就需要执行一些初始化的代码,比如校验数据库的完整性等。但是Servlet的生命周期是用户访问浏览器对应的路径开始的,如果没有用户的第一次访问,就无法执行相关代码。
此时就需要Servlet实现自启动,伴随着Tomcat的启动,自动初始化,在初始化方法init()中,就可以进行一些业务代码的工作了。
init方法1
2
3public 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”);
使用缓存可以加快页面的加载,降低服务端的负担。但是也可能看到过时的信息,可以通过上面通知浏览器不要使用缓存。