Tomcat简介
如何实现动态网页技术,HTML包括JavaScript都是静态网页技术,就是不能与数据库交互,不是实现登录、注册、提交购物车、搜索。动态是根据不同的客户端、不同的时间、不同的偏好可以提供不同的内容;淘宝网千人千面,可以有针对性的推荐商品,提高成交率。
动态网页的实现:
- JAVA技术: Servlet JSP 主流 淘宝、京东、当当网、美团、滴滴主流的互联网公司都是使用java平台
- .NET技术: C# 一般网页的扩展名 .asp或者是 .aspx
- PHP技术: 开源的、免费的;淘宝网最早是PHP修改的,论坛、政府网站、办公系统
- CGI技术: 通用网关技术, 腾讯的邮件系统你注意一下扩展名
这些动态网页技术都要运行在服务器上,服务器接收用户的请求,找到请求的资源,再返回给客户端浏览器。我们网络上访问的网站taobao,jd 都是运行在服务器上的;主流服务器JAVA是Tomcat服务器,asp和aspx技术运行的服务器是IIS,在windows系统都自带该服务器;PHP apache服务器。
Tomcat是Apache开源社区维护的一个java的web服务器,并且得到Sun公司的大力支持,Oracle公司。
Tomcat官网
下载的Tomcat8.xx版本: 下载地址
- tar.gz linux环境使用的, 可以直接解压 : tar -zxvf apache-tomcat-8.5.55.tar.gz
- zip版本: windows系统,直接解压
- exe版本: 直接安装的 apache-tomcat-8.5.55.exe
Tomcat目录结构
下载对应的版本之后,直接解压即可,
- bin 主要是Tomcat启动和停止的脚本,以sh结尾的脚本是linux和mac使用,windows系统使用.bat脚本,startup,shutdow
- conf: configuration 配置信息,server.xml文件配置端口号、线程池;tomcat-users.xml管理员账户信息
- lib: library 库, jsp-api.jar servlet-api.jar tomcat-dbcp.jar连接池,所有的带i18n是国际化的语言信息,websocket是开发网页消息的发送和接收,可以开发一个网页版的聊天工具。特别网站的客服会用到。
- logs: log4j.jar 记录服务器访问的日志信息,当系统出现问题通过日志查询
- temp: 存储的临时文件,譬如: 文件上传先存到临时文件夹,上传结束再放大具体位置
- webapps: web applications web应用,我们开发的web项目部署到该文件夹
- work: 工作目录,存储jsp转换成servlet的java文件,包括编译后的字节码文件
启动、停止服务器
启动: startup.sh startup.bat
停止:shutdown.sh shutdown.bat, Ctrl + C 也可以停止DOS窗口
前台启动: 一直占用Dos窗口 Ctrl + C 结束运行
后台启动: Tomcat 以后台进程的方式运行,不占用Dos窗口
mac体系:
~/Downloads/apache-tomcat-8.5.51/bin » ./startup.sh
Using CATALINA_BASE: /Users/kongfanyu/Downloads/apache-tomcat-8.5.51
Using CATALINA_HOME: /Users/kongfanyu/Downloads/apache-tomcat-8.5.51
Using CATALINA_TMPDIR: /Users/kongfanyu/Downloads/apache-tomcat-8.5.51/temp
Using JRE_HOME: /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home
Using CLASSPATH: /Users/kongfanyu/Downloads/apache-tomcat-8.5.51/bin/bootstrap.jar:/Users/kongfanyu/Downloads/apache-tomcat-8.5.51/bin/tomcat-juli.jar
Tomcat started.
------------------------------------------------------------
~/Downloads/apache-tomcat-8.5.51/bin »
Tomcat服务器默认对外提供服务的端口: 8080; 访问地址:
本机: http://localhost:8080 http://127.0.0.1:8080 http://192.168.11.163:8080(局域网)
云服务器: http://119.34.5.89:8080
Server Status 查看服务器状态,此时提示登录;
修改tomcat-users.xml文件:
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<role rolename="tomcat"/>
<role rolename="manager-gui"/>
<user username="tomcat" password="123456" roles="manager-gui"/>
<user username="both" password="123456" roles="tomcat,role1"/>
<user username="role1" password="123456" roles="role1"/>
</tomcat-users>
查看服务器的状态:
项目的启动和停止:
eclipse创建web项目
Web项目简介:
- src 文件夹: source code 源代码文件夹: 实体类,JdbcUtil,Dao接口,Dao实现类
- WebContent web内容
- META-INF 元信息 information
- WEB-INF web 信息
- lib 各种jar包存储的位置
- web.xml web项目的核心配置文件
- HTML页面写在WebContent目录下面,包括images、js、css
- JRE System Library java运行环境 系统库
- Apache Tomcat v8.5 服务器运行jar包
部署web项目
部署项目到Tomcat的webapps目录下, 需要做的修改:
上图的位置是wtpwebapps,还不是tomcat的默认的webapps目录, 做如下修改:
Servlet技术入门
Servlet概念
Servlet是一个java类,部署在服务器端的程序,用来接收用户请求,并做出响应的一个web组件。
Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
如何编写Servlet
编写Servlet的规范:
- 继承HttpServlet
- 重写doGet方法和doPost方法
package com.ujiuye.web;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = -6600072285720004089L;
/**
* 处理Get请求
* request表示的是请求对象
* response表示的响应对象
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out= response.getWriter();
out.println("<h1>一个人幸运的前提是他有改变自己的能力。</h1>");
out.close();
}
/**
* 处理Post请求
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
配置Servlet
注册Servlet, 注册该web应用的配置文件中; 相当于新入职一个员工,到人事注册,提交身份证信息等等。
配置的位置: web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>chapter11</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<!--配置Servlet的实现类信息 -->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.ujiuye.web.HelloServlet</servlet-class>
</servlet>
<!--配置映射信息: 访问的路径 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
配置完成,要重启Tomcat服务器的。
访问Servlet
http://localhost:8080/chapter11/hello
url的配置:
/* 匹配任何字符: http://localhost:8080/chapter11/asdasd
/hello/abc : http://localhost:8080/chapter11/hello/abc
*.action 类似 *.do http://localhost:8080/chapter11/xxx.action http://localhost:8080/chapter11/xxx.do
Tomcat执行Servlet的过程:
Servlet生命周期
Servlet生命周期: 一个Servlet组件从被Tomcat加载、产生对象、服务(处理请求)、销毁的过程。
package com.ujiuye.web;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LifeCycleServlet extends HttpServlet{
static {
System.out.println("LifeCycleServlet被加载到服务器了....");
}
public LifeCycleServlet() {
System.out.println("LifeCycleServlet构造方法被执行了...创建对象");
}
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("LifeCycleServlet初始化方法被调用....");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("LifeCycleServlet执行服务的方法.....doGet....");
resp.getWriter().println("<h1>欢迎访问我的web</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
public void destroy() {
System.out.println("LifeCycleServlet销毁方法执行.....");
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>chapter11</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<!--配置Servlet的实现类信息 -->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.ujiuye.web.HelloServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>life</servlet-name>
<servlet-class>com.ujiuye.web.LifeCycleServlet</servlet-class>
</servlet>
<!--配置映射信息: 访问的路径 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>life</servlet-name>
<url-pattern>/life</url-pattern>
</servlet-mapping>
</web-app>
执行过程:
信息: Server startup in 646 ms
五月 30, 2020 3:52:36 下午 org.apache.tomcat.util.http.parser.Cookie logInvalidHeader
信息: A cookie header was received [1585184331] that contained an invalid cookie. That cookie will be ignored.
Note: further occurrences of this error will be logged at DEBUG level.
LifeCycleServlet被加载到服务器了....
LifeCycleServlet构造方法被执行了...创建对象
LifeCycleServlet初始化方法被调用....
LifeCycleServlet执行服务的方法.....doGet....
LifeCycleServlet执行服务的方法.....doGet....
LifeCycleServlet执行服务的方法.....doGet....
LifeCycleServlet执行服务的方法.....doGet....
五月 30, 2020 3:53:54 下午 org.apache.catalina.core.StandardServer await
信息: A valid shutdown command was received via the shutdown port. Stopping the Server instance.
五月 30, 2020 3:53:54 下午 org.apache.coyote.AbstractProtocol pause
信息: Pausing ProtocolHandler ["http-nio-8080"]
五月 30, 2020 3:53:54 下午 org.apache.catalina.core.StandardService stopInternal
信息: 正在停止服务[Catalina]
LifeCycleServlet销毁方法执行.....
五月 30, 2020 3:53:54 下午 org.apache.catalina.core.ApplicationContext log
信息: SessionListener: contextDestroyed()
五月 30, 2020 3:53:54 下午 org.apache.catalina.core.ApplicationContext log
信息: ContextListener: contextDestroyed()
五月 30, 2020 3:53:54 下午 org.apache.coyote.AbstractProtocol stop
信息: 正在停止ProtocolHandler ["http-nio-8080"]
五月 30, 2020 3:53:54 下午 org.apache.coyote.AbstractProtocol destroy
信息: 正在摧毁协议处理器 ["http-nio-8080"]
从执行的结果可以看出; Servlet是单实例、多线程;不是线程安全的对象。
总结:
- 加载
- 实例化
- 初始化
- 服务
- 销毁
加载是由Tocmat实现的的类加载器完成的,类似我们在学习java基础的时候JVM的类加载器。
Servlet处理登录请求
登录页面: login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录系统</title>
<style type="text/css">
#username,#pwd{
border:1px solid #000;
width: 150px;
}
</style>
<script type="text/javascript">
function subForm(){
var name = document.getElementById("username").value;
if(name == "" || name.length==0){
alert("请输入用户账号.");
return false;
}
return true;
}
</script>
</head>
<body>
<h1>用户登录</h1>
<form action="login" method="post" onsubmit="return subForm()">
用户账号: <input type="text" name="username" id="username"/><br/>
用户密码: <input type="password" name="password" id="pwd"/><br/>
<input type="submit" value="登录"/>
<input type="reset" value="重置"/>
</form>
</body>
</html>
登录处理的Servlet: LoginServlet.java
package com.ujiuye.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取登录的用户名称
String username = request.getParameter("username");
//获取登录的用户密码
String password = request.getParameter("password");
//假设正确的用户名和密码 admin 123456
if( "admin".equals(username) && "123456".equals(password) ) {
response.sendRedirect("a.jsp");//重定向的一个页面
}else {
response.sendRedirect("login.html");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
配置Servlet: 注解配置
@WebServlet("/login")
get请求和post请求的参数传递:
Servlet处理过程:
GET请求和POST请求区别
项目 | GET | POST |
---|---|---|
后退按钮/刷新 | 无害 | 数据会被重复提交(浏览器应该告诉用户数据会被重新提交) |
书签 | 可收藏 | 不可收藏 |
缓存 | 可缓存 | 不可缓存 |
编码 | application/x-www-form-urlencoded | application/x-www-form-urlencoded或multipart/form-data为二级制数据使用多重编码 |
历史 | 参数保留在浏览器历史中 | 不会保存 |
数据长度 | 发送数据GET方法向URL添加数据: 最大2048个字符 | 无限制 |
数据类型 | 只允许ASCII字符 | 无限制,也允许二进制数据 |
安全性 | 差,数据是URL的一部分,可以看到 | 更安全 |
可见性 | 数据在URL中对所有人可见的 | 数据不会显示在URL中 |
Servlet源码分析
查看父类HTTPServlet源码:
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod(); //获取请求提交的方法 get或post或delete
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
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);
} 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 {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
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);
}
}
可以看出父类根据请求提交的方法转而调用子类的相关实现方法。
面试题
servlet生命周期
见上面讲义
Servlet是单例吗?
不一定是,在一个ServeltName情况下是的。在多个ServletName匹配到一个Servlet类时,该Servlet不是单例。
Servlet是线程安全的吗?
不是,一个servlet实现类只会有一个实例对象,多个线程是可能会访问同一个servlet实例对象的,线程安全问题都是由全局变量及静态变量引起的。解决线程安全的方法如下:
1、实现 SingleThreadModel 接口
Servlet2.4 已经提出不提倡使用。实现此接口,Servlet容器为每个新的请求创建一个单独的Servlet实例。这会有严重性能问题。
2、同步锁
使用synchronized关键字,虽然可以保证只有一个线程可以访问被保护区段,已达到保证线程安全。但是系统性能及并发量大大降低。不可取~
3、避免使用实例变量,即Servlet中全局变量。使用局部变量 (推荐)
方法中的局部变量分配在栈空间,每个线程有私有的栈空间。因此访问是线程安全的。