Tomcat服务器和Web开发介绍
一、开启Web开发
什么是web开发
WEB,即网页的意思,它用于表示Internet主机上供外界访问的资源。
Internet上供外界访问的Web资源分为:
静态web资源(如html 页面):指web页面中供人们浏览的数据始终是不变。
动态web资源:指web页面中供人们浏览的数据是由程序产生的,不同时间点访问web页面看到的内容各不相同。问题:静态web资源开发技术和动态web资源开发技术分别有哪些?
在Java中,动态web资源开发技术统称为Javaweb,我们课程的重点也是教大家如何使用Java技术开发动态的web资源,即动态web页面。
二、Web容器_Tomcat
学习web开发,需要先安装一台web服务器,然后再在web服务器中开发相应的web资源,供用户使用浏览器访问。
目前有一款免费且应用广泛的web容器Tomcat,是我们学习的首选该服务器支持JSP以及Servlet规范。启动成功后,通过浏览器访问效果如下:
JSP:html+java
Servlet:技术
三、常见的启动问题及tomcat目录介绍
常见启动问题
使用startup.bat文件启动Tomcat
Catalina_home环境变量
Java_home环境变量TOMCAT的启动需要一个环境变量JAVA_HOME
端口占用问题(更改默认端口)
其他程序占用了这个端口,修改这个端口
解决办法:
Conf/server.xml 搜8080 替换Tomcat的目录层次
Bin:存放二进制文件,启动和关闭tomcat
Conf:配置文件,配置端口,配置数据库连接池,web.xml
Lib:jar文件,支撑tomcat运行
Logs:存放tomcat运行日志信息
Temp:临时目录,给tomcat自己用的
Webapps:存放我们要部署的项目
Work:工作目录,区别(jsp)
四、JavaWeb应用程序
WEB应用程序指供浏览器访问的程序,通常也简称为web应用。
一个web应用由多个静态web资源和动态web资源组成,如:
html、css、js文件
Jsp文件、java程序、支持jar包、相关的配置文件等等Web应用开发好后,若想供外界访问,需要把web应用所在目录交给web服务器管理,这个过程称之为虚似目录的映射。
五、Web应用的组成结构
开发web应用时,不同类型的文件有严格的存放规则,否则不仅可能会使web应用无法访问,还会导致web服务器启动报错。下面我们使用eclipse来开发我们的第一个web应用程序
六、Tomcat的体系结构
Server代表整个Servlet容器组件,是最顶层元素,包含多个Service元素。
Service包含一个Engine元素以及多个Connector元素
Connector元素代表和客户端程序交互的组件,负责接收客户端请求和向客户端返回响应。
Engine中可以包含多个Host,Host表示一个虚拟主机,他可以包含多个web应用
Context代表运行在虚拟主机上的单个web应用
七、什么是Servlet
Servlet是sun公司提供的一门用于开发动态web资源的技术,可以实现和客户端的交互,接收客户端请求和给客户端返回响应。
Sun公司在其API中提供了一个servlet接口,用户若想开发一个动态web资源需要完成以下2个步骤:
编写一个Java类,实现servlet接口。
把开发好的Java类部署到web服务器中。
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 1.创建MyServlet类,继承HttpServlet(前提:先导入servlet_api.jar)
* 2.重写service()
* 3.在web.xml配置Servlet信息
*/
public class MyServlet01 extends HttpServlet {
/**
* 客户端发送请求到本Servlet中,Tomcat服务器就会调用service方法,并传进来请求对象和响应对象
*/
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码格式
req.setCharacterEncoding("UTF-8");//设置请求编码格式
resp.setContentType("text/html;charset=UTF-8");//设置响应编码格式
//获取请求中的数据
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("接收到来自客户端的请求了... " + username + " -- " + password);
//通过响应返回数据
resp.getWriter().println("<h1>把微笑带回家</h1>");
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢迎页面</h1>
<a href="aaa?username=ggc&password=123123">向MyServlet发送请求</a>
<br>
<input type="button" value="向MyServlet02发送请求" onclick="fun01()"/>
<br>
<a href="register.html">跳转到注册页面</a>
<script type="text/javascript">
function fun01() {
location ="bbb?username=ggc&password=111222";
}
</script>
</body>
</html>
优化代码
/**
* 1.创建MyServlet类,继承HttpServlet(前提:先导入servlet_api.jar)
* 2.重写doGet()和doPost()
* 3.在web.xml配置Servlet信息
*/
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 MyServlet02 extends HttpServlet {
/**
* 客户端发送Get请求到本Servlet中,Tomcat服务器就会调用doGet方法,并传进来请求对象和响应对象
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
/**
* 客户端发送Post请求到本Servlet中,Tomcat服务器就会调用doPost方法,并传进来请求对象和响应对象
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码格式
req.setCharacterEncoding("UTF-8");//设置请求编码格式
resp.setContentType("text/html;charset=UTF-8");//设置响应编码格式
//获取请求中的数据
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("MyServlet02接收到来自客户端的请求了... "+ username + " -- " + password);
//通过响应返回数据
resp.getWriter().println("<h1>把微笑带回家</h1>");
}
}
八、Servlet的实现类
Servlet接口SUN公司定义了两个默认实现类,分别为:GenericServlet、HttpServlet。
HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。
HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法第一步:创建一个类,继承HttpServlet,重写service方法
第二步:注册
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>注册页面</h1>
<form action="ccc" method="post">
账号:<input type="text" name="username"/><br>
密码:<input type="password" name="password"/><br>
性别:
<input type="radio" name="sex" value="man" checked="checked"/>男
<input type="radio" name="sex" value="woman"/>女
<br>
籍贯:
<select id="province" name="province">
<option value="sc">四川</option>
<option value="hn">湖南</option>
<option value="hb">湖北</option>
</select>省
<select id="city" name="city">
<option value="cd">成都</option>
<option value="nc">南充</option>
<option value="yb">宜宾</option>
</select>市
<br>
爱好:
<input type="checkbox" name="hobbys" value="football"/>足球
<input type="checkbox" name="hobbys" value="basketball"/>篮球
<input type="checkbox" name="hobbys" value="shop"/>购物
<br>
<input type="submit" value="注册"/>
</form>
<script type="text/javascript">
var province = document.getElementById("province");
var city = document.getElementById("city");
//改变事件
province.onchange = function () {
var v = this.value;
if(v == "sc"){
addCity(["成都","南充","宜宾"],["cd","nc","yb"]);
}else if(v == "hn"){
addCity(["长沙","娄底","怀化","益阳","永州"],["cs","ld","hh","yy","yz"]);
}else if(v == "hb"){
addCity(["武汉","仙桃","黄冈","孝感","十堰","咸宁","宜昌","恩施"],["wh","xt","hg","xg","sy","xn","yc","es"]);
}
}
function addCity(cityArr,valueArr){
//清空city下拉列表中的数据
city.length = 0;
for(var i = 0;i<cityArr.length;i++){
var cityName = cityArr[i];
var option = document.createElement("option");
option.innerText = cityName;
option.value = valueArr[i];
city.appendChild(option);
}
}
</script>
</body>
</html>
/**
* 1.创建MyServlet类,继承HttpServlet(前提:先导入servlet_api.jar)
* 2.重写doGet()和doPost()
* 3.在web.xml配置Servlet信息
*/
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class MyServlet03 extends HttpServlet {
/**
* 客户端发送Get请求到本Servlet中,Tomcat服务器就会调用doGet方法,并传进来请求对象和响应对象
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
/**
* 客户端发送Post请求到本Servlet中,Tomcat服务器就会调用doPost方法,并传进来请求对象和响应对象
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码格式
req.setCharacterEncoding("UTF-8");//设置请求编码格式
resp.setContentType("text/html;charset=UTF-8");//设置响应编码格式
//获取请求中的数据集合
// Map<String, String[]> parameterMap = req.getParameterMap();
// Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
// for(Map.Entry<String, String[]> entry : entries){
// System.out.println(entry.getKey() + " -- " + Arrays.toString(entry.getValue()));
// }
//获取请求中的数据
String username = req.getParameter("username");
String password = req.getParameter("password");
String sex = req.getParameter("sex");
String province = req.getParameter("province");
String city = req.getParameter("city");
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("MyServlet03接收到来自客户端的请求了... " + username + " -- " + password + " -- " + sex + " -- " + province + " -- " + city + " -- " + Arrays.toString(hobbys));
//通过响应返回数据
resp.getWriter().println("<h1>把微笑带回家</h1>");
}
}
九、解析过程
Tomcat在加载Web应用时,就会把相应的web.xml文件中的数据读入到内存中。因此当Tomcat在解析web请求的时候,需要参考web.xml文件时,实际上只需要从内存中读取相关数据就可以了,不需要再到文件系统中读取web.xml。
十、Servlet的运行过程
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
1,Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第4步,否则,执行第2步。
2,装载并创建该Servlet的一个实例对象。
3,调用Servlet实例对象的init()方法。
4,创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
5,WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
十一、Servlet的生命周期
Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。
在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。
如果在元素中配置了一个元素,那么WEB应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。
用途:为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的公共数据。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="Servlet01">向Servlet01发送请求</a>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>welcome.html</welcome-file>
</welcome-file-list>
<!--<servlet>-->
<!--<servlet-name>Servlet01</servlet-name>-->
<!--<servlet-class>com.qf.servlet.Servlet01</servlet-class>-->
<!--<init-param>-->
<!--<param-name>code</param-name>-->
<!--<param-value>UTF-8</param-value>-->
<!--</init-param>-->
<!--<load-on-startup>3</load-on-startup>-->
<!--</servlet>-->
<!--<servlet-mapping>-->
<!--<servlet-name>Servlet01</servlet-name>-->
<!--<url-pattern>/Servlet01</url-pattern>-->
<!--</servlet-mapping>-->
</web-app>
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Servlet作用:
* 1.接收来自客户端的请求,并且可以获取请求信息
* 2.执行具体的业务(登录、注册、展示商品、加入购物车....)
* 3.通过响应返回数据给客户端
*
* Servlet生命周期:
* 1.第一次发送给该Servlet请求后,会创建该Servlet的对象,调用无参构造、init()
* 2.服务器根据请求方式选择调用doGet()或doPost()
* 3.关闭服务器时销毁Servlet对象,调用destroy()
* 小结:在一般情况下,Servlet是单例的
*
* Servlet何时被创建?
* 1.第一次发送请求时
* 2.在web.xml -> servlet -> <load-on-startup>1</load-on-startup>,该Servlet在项目启动时就会创建对象
* 3.在Servlet添加了@WebServlet(loadOnStartup=1),该Servlet在项目启动时就会创建对象
*/
@WebServlet(
value = "/Servlet01",
initParams={@WebInitParam(name="code",value="UTF-8")},
loadOnStartup=1
)
public class Servlet01 extends HttpServlet {
/**
* Servlet构造方法
*/
public Servlet01() {
System.out.println("Servlet01 -- Servlet01()");
}
/**
* Servlet初始化方法
* @param config Servlet的配置文件对象
*/
@Override
public void init(ServletConfig config) throws ServletException {
String code = config.getInitParameter("code");
System.out.println("Servlet01 -- init() :" + code);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet01接收到客户端的请求了...");
}
/**
* Servlet销毁方法
*/
@Override
public void destroy() {
System.out.println("Servlet01 -- destroy()");
}
}
十二、ServletConfig对象
在Servlet的配置文件中,可以使用一个或多个标签为servlet配置一些初始化参数。
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
阅读ServletConfig API,并举例说明该对象的作用:
获得字符集编码
获得数据库连接信息实际的Servlet开发中,可以直接通过getServletConfig()的到ServletConfig对象。
十三、Servlet线程安全问题
多个客户端访问同一个Servlet中的资源时,有可能会出现线程安全问题
解决方案
1.将Servlet实现SingleThreadModel(已过时),因为当线程阻塞,就会创建新的Servlet对象
2.利用线程锁机制, synchronized或lock
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="MyServlet">向MyServlet发送请求</a>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>welcome.html</welcome-file>
</welcome-file-list>
</web-app>
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;
/**
* Servlet是否是单例的?
* 一般情况下,Servlet是单例的
* 如果Servlet实现类实现了SingleThreadModel接口,Tomcat服务器发现Servlet线程阻塞了,就会新创建一个Servlet对象
*/
@WebServlet("/MyServlet")
//public class MyServlet extends HttpServlet implements SingleThreadModel {
public class MyServlet extends HttpServlet {
private int num = 0;
public MyServlet() {
System.out.println("MyServlet被创建了");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
synchronized (this){
num++;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
response.getWriter().println(num);
}
}
}