导学
在本节课程中需要了解什么是b/s架构模式,浏览器与服务器。掌握servlet初步开发技巧,掌握servlet开发原理。
软件结构发展史
- 单机时代(桌面应用)
特点:没有互联网(92)和局域网(83),所用数据的保存是放在本地的硬盘中
优点:易于使用、结构简单、安装方便
缺点:数据难以共享、安全性差、更新不及时
比如:Word/Eclipse - 联机时代(
Client-Server
模式)
特点:Client/Server
结构(C/S架构
)是指客户端和服务器结构,最大的特点是需要安装客户端,大部分的数据保存在远程服务器上,在本地客户端只保留有少量的不太重要的数据。
优点:数据方便共享、安全性高
缺点:必须安装客户端,升级和维护困难
比如:QQ/微信/支付宝 - 互联网模式(
Brower-Server
模式)
特点:Brower-Server
(B/S架构
)是指浏览器和服务器结构,使用浏览器代替客户端,程序运行在浏览器上,动态生成一个个的网页,在本地不会留有任何的数据。
优点:开发简单,无需安装客户端,数据易于共享
缺点:执行速度慢一点、用户体验差一点
比如:手机淘宝(像是APP,本质上嵌套了一个浏览器,用的是HTML进行的开发)B/S
模式的执行流程什么是服务器
服务器:向终端(客户端)提供服务的计算机。简单来说就是安装了WEB服务器程序的电脑
服务器一般的特点:
- 7*24 全天候都在运行的计算机,当然也会出现维护的时候;
- 服务器的性能要出色,包括CPU/内存/磁盘读写;
- 服务器是要连接在一个稳定的网络中的,没有连接网络的服务器可以说没有任何意义。
服务器的种类:
- 刀片式服务器:刀片式服务器应用于大型的数据中心或者需要大规模度计算的领域。
- 塔式服务器:塔式服务器适合常见的入门级和工作组级服务器应用,性能能满足大部分中小企业用户的要求问。
- 机架式服务器:机架式服务器多用于服务器数量较多的大型企业使用。
广域网与局域网
广域网:可以理解为全世界互联的一个网络。
局域网:是一个由有限的计算机组成的网络,一般比如说公司的内网,学校的内网都属于局域网。IP地址:计算机在网络中的唯一标识,进入
cmd
,ipconfig
显示自身电脑的ip地址
但是IP V4地址是一串10进制数字组成,IP V6的地址是一串16进制的数字组成,非常难以记忆。于是,便有了DNS(域名服务协议),设置域名服务器用于将域名和IP进行转换。
比如,百度的域名:https://www.baidu.com:80
- https:协议
- baidu.com:域名
- www:在此域名下解析的主机服务器
- 80:解析到的服务器的端口(服务器设置不同的端口用于提供不同的服务)
其实对于这样的地址,我们不仅仅可以采用域名进行访问,还可以使用ip地址进行访问。比如在cmd中,使用如下代码:nslookup www.baidu.com
去查看对应域名的ip地址,再使用ping
语句,检查是否能够连接对应地址。
运行流程
静态Web服务: 访问的就是最简单的文件系统,在文件系统中只存储了html/css/javascript/img这些静态资源 动态Web服务: 网页可以和数据库进行交互
在这张图片中,是一次请求与响应的动态加载过程。那么什么是请求与响应呢?
从浏览器发出送给服务区的数据包称为请求(
Request
) 从服务器返回给浏览器的结果称为响应(Response
) 二者通常是成对出现的。
Tomcat与Servlet简介
再来回忆一下什么是J2EE
,它是Java开发的平台企业版。在它内部和标准版最大的不同就是它内置了很多企业开发的组件和功能。其中开发B/S(WEB)
应用程序就是J2EE
最核心的功能。这些功能模块有:
现在Java已经为我们的WEB开发提供了如此多的支持,那么还需要什么呢?这个时候就需要介绍大名鼎鼎的Apache Tomcat。
Apache Tomcat是一款WEB应用服务器程序,它能够使我们的WEB应用程序和静态资源能够被公开的使用。它是Apache软件基金会旗下一款免费的开放源代码的WEB应用服务器程序。
J2EE与Tomcat的关系:
- J2EE是一组技术规范与指南,具体实现由软件厂商来决定。
- Tomcat是J2EE Web (Servlet与JSP)标准的实现者。
- J2SE是J2EE的运行基石,运行Tomcat离不开J2SE
此时虽然有了Tomcat,可以使电脑变成服务器,但是在服务器内部程序是如何处理的,Tomcat自己也不清楚。所以,此时需要J2EE的核心组件Servlet来进行编码,Tomcat与Servlet是相辅相成的。Tomcat提供运行的基础,Servlet提供软件的实现,共同组建形成软件。
Servlet(Server Applet)服务器小程序,主要功能用于生成动态Web内容
安装配置Tomcat
安装流程
- 安装JDK,Tomcat依托于JDK
- 选择与JDK大版本号相同的Tomcat版本
- 访问https://tomcat.apache.org/download-80.cgi下载 8.X.X 版本。
- 解压到本地,即完成安装
启动
Tomcat 目录说明
进入 「bin」目录 → startup.bat+---bin Tomcat的命令行工具,主要有启动和关闭命令
+---conf Tomcat相关配置的属性,如修改端口号,设置部署目录等
+---lib Tomcat用到的一些CLASSPATH,如果程序里有用到第三方的jar也可以放在其中,但是不建议
+---logs 相关日志文件
+---temp
+---webapps 项放置所有的web应用程序
+---work 所有的jsp被编译后的文件,临时生成的文件
关闭:可以直接关闭启动窗口(比较暴力),或者进入 「bin」目录 → shutdown.bat
ROOT 目录
webapps/ROOT 是一个特殊的应用程序,请求在访问的时候默认进去的应用。
关于 hosts 文件
位置:C:\Windows\System32\drivers\etc
hosts文件中存储了本机的域名解析服务,浏览器在输入域名的时候,第一步是在host中找到解析的ip地址,如果找不到则到外网的域名解析服务器中查询。
修改端口号
在conf/server.xml,搜索8080字符,将其替换为80,并重启服务
部署项目
- 直接部署在webapps下面,此时应用的上下文路径就是webapps下面文件夹名称
- 服务器项目部署
- 直接复制法:比较开发工具中Tomcat的项目发布路径的内容与工作空间的内容,可以将开发工具中Tomcat中的项目(或者修改工作空间中项目源码输出位置)直接复制到外部Tomcat中
- 导出war包法:可以通过eclipse等开发工具导出war包,部署到webapps文件夹下
- 使用下文的虚拟路径的方式部署
- 服务器项目部署
- 通过虚拟目录的方式进行配置
在 conf/server.xml 文件中的</Host>
内部的最后配置一下内容<Context path="/abc" docBase="D:\work\Trade\inv\ntqn" />
其中path
为应用的上下文路径,docBase
为应用在服务器中的路径
字符编码的统一
在开发环境中(Eclipse)中,相关的类、文件都使用 UTF-8 编码。
创建数据库的时候,数据库编码使用 UTF-8。
在 Tomcat 中,设置 URIEncoding=UTF-8,server.xml 中的 Connector 节点。在Tomcat 8之后,默认字符编码为UTF-8。
不要使用中文和带有空格的名称做为文件路径。
Eclipse集成配置Tomcat
- 新建Dynamic Web Project
- 填写项目基本信息
- 创建完的项目目录
说明:
build:默认的编译完成的class文件和相关的配置文件(xml/properties/json..)
src:源文件
WebContent:web项目路径
WebContent 下的 WEB-INF:放置服务器端文件的目录,例如 lib/classes,当然也可以放置 jsp 文件,但是在此处放置的 jsp 文件,是不能通过路径直接访问的。
部署项目
- 直接部署在webapps下面,此时应用的上下文路径就是webapps下面文件夹名称
- 服务器项目部署
- 直接复制法:比较开发工具中Tomcat的项目发布路径的内容与工作空间的内容,可以将开发工具中Tomcat中的项目(或者修改工作空间中项目源码输出位置)直接复制到外部Tomcat中
- 导出war包法:可以通过eclipse等开发工具导出war包,部署到webapps文件夹下
- 使用下文的虚拟路径的方式部署
- 服务器项目部署
- 通过虚拟目录的方式进行配置
在 conf/server.xml 文件中的</Host>
内部的最后配置一下内容<Context path="/abc" docBase="D:\work\Trade\inv\firstservlet" />
其中path
为应用的上下文路径,docBase
为应用在服务器中的路径
Servlet
``` package com.dodoke.servlet; 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; /**- 此时Tomcat还不认识这个servlet,需要在项目的配置文件中配置servlet
- @author LiXinRong
/
public class MyFirstServlet extends HttpServlet {//HttpServlet是所有服务器小程序的父类
@Override//重写service方法,为servlet的请求与响应提供支持。
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}//接收请求的参数,暂时先记住
String name = request.getParameter("name");
String html = "<h1 style='color:red'>hi," + name + "!</h1><hr/>";
System.out.println("返回给浏览器的响应数据为:" + html);
//创建服务器对浏览器的输出流,可以将数据从服务器发送到浏览器
PrintWriter out = response.getWriter();
out.println(html);
}
```
<?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>FirstServlet</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<!-- 创建servlet的别名,就是给真正创建的servlet起了个名字 -->
<servlet-name>first</servlet-name>
<!-- 使用servlet-class指向一个具体的servlet的实现类 -->
<servlet-class>com.dodoke.servlet.MyFirstServlet</servlet-class>
</servlet>
<!-- 构建一个servlet的映射,servlet可以有其具体的地址,这个地址可以被浏览器访问,此处就来设置对应的地址 -->
<servlet-mapping>
<!-- 需要和上方的first同名,这样地址映射才能和具体实现类对应 -->
<servlet-name>first</servlet-name>
<url-pattern>/hi</url-pattern>
</servlet-mapping>
</web-app>
Servlet开发与基本配置
Servlet开发步骤:
- 创建Servlet类,继承HttpServlet
- 重写service方法,编写程序代码
- 配置web.xml,绑定URL
请求参数的发送与接收
请求参数:
- 请求参数是指浏览器通过请求向Tomcat请求的数据
- 请求参数通常是用户输入的数据,待servlet进行处理
- 请求参数的形式:参数名=值1&参数名2=值2&参数名n=
示例:
``` package com.dodoke.servlet; 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 SampleServlet extends HttpServlet{ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>学员信息登记表</title>
</head>
<body>
<h1>学员信息登记表</h1>
<form action="/FirstServlet/sample">
姓名:<input name="name"/>
<br/>
电话:<input name="telphone"/>
<br/>
性别:
<select name="sex" style="width:100px;padding:5px">
<option value="man">男</option>
<option value="women">女</option>
</select>
<br/>
特长:
<input type="checkbox" name="spec" value="English"/>英语
<input type="checkbox" name="spec" value="Program"/>编程
<input type="checkbox" name="spec" value="Speech"/>演讲
<input type="checkbox" name="spec" value="Swimming"/>游泳
<br/>
<input type="submit" value="提交"/>
<br/>
</form>
</body>
</html>
}PrintWriter out = response.getWriter();
String html = "<h1>hello,world</h1>";
out.println(html);
}
在servlet中,有两个方法去接收参数:
- `request.getParameter() 接收单个参数`
- `request.getParameterVa``lue``s() 接收多个同名参数`<br />修改后:
```java
public class SampleServlet extends HttpServlet{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
String telphone = request.getParameter("telphone");
String sex = request.getParameter("sex");
String[] specs = request.getParameterValues("spec");
PrintWriter out = response.getWriter();
out.println("<h1>姓名:"+ name +"</h1>");
out.println("<h1>手机:"+ telphone +"</h1>");
out.println("<h1>性别:"+ sex +"</h1>");
for(String str : specs) {
out.println("<h2>特长:"+ str +"</h2>");
}
}
}
GET和POST请求
get和post请求:
get是将数据通过url附加数据显性的向服务器发送数据
post 是将数据放置在请求体
中隐性的向服务器发生数据
请求体:name=zhangsan,放置在请求流中,专门用于存放数据
比如:在action 属性添加 method=”post” ,我们会发现在地址栏中的数据已经没有了,那么这样的数据如何去查看呢?我们可以在浏览器的调试工具中查看。
post请求,数据会存放在请求体中,如下
get请求,显示在地址栏,如下
其实,在请求体中的数据,和get请求的数据表现是一样的。
service是请求处理的核心方法,无论是get或者post都会被service()方法处理
//service()是请求处理的核心方法 无论get和post都会被处理,service方法不区别对待
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method = request.getMethod();//获取请求的请求方式
String name = request.getParameter("name");
String telphone = request.getParameter("telphone");
String sex = request.getParameter("sex");
String[] specs = request.getParameterValues("spec");
PrintWriter out = response.getWriter();
out.println("<h1>method:"+ method +"</h1>");
out.println("<h1>name:"+ name +"</h1>");
out.println("<h1>mobile:"+ telphone +"</h1>");
out.println("<h1>sex:"+ sex +"</h1>");
for(String str : specs) {
out.println("<h2>spec:"+ str +"</h2>");
}
}
在实际工作中,对于get和post请求是需要分开进行处理的,在servlet中提供了doGet和doPost方法,方便我们进行处理。
package com.dodoke.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RuquestMethodServlet extends HttpServlet{
//处理get请求
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
response.getWriter().println("<h1 style='color:green'>"+ name +"</h1>");
}
//处理post请求
@Override
protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
response.getWriter().println("<h1 style='color:red'>"+ name +"</h1>");
}
}
doGet() 常用于不包含敏感词信息查询 doPost() 用于安全性较高的功能和服务器的写操作
Service()方法如果不进行重写的话,就相当于一个分发器,是doGet()和doPost()的上级,将不同请求方式的请求交给不同的方法进行处理。
Servlet的生命周期
Servlet从创建到死亡经历了哪些过程呢?我们首先来看看它的底层实现。
- 装载(Tomcat解析XML)此时Tomcat只是解析,并未对其做什么操作
- 创建 第一次访问的时候,创建servlet对象执行构造函数
- 初始化 调用init()方法初始化
- 提供服务 service( )/doGet( )/doPost( )
- 销毁 tomcat关闭或重启的时候执行destory( )
```java public class MyFirstServlet extends HttpServlet {
public MyFirstServlet() {
System.out.println("正在创建Servlet");
}
@Override public void init(ServletConfig config) throws ServletException {
System.out.println("正在初始化servlet对象");
} @Override//重写service方法 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("servlet正在提供服务");
//接收请求的参数,暂时先记住
String name = request.getParameter("name");
String html = "<h1 style='color:red'>hi," + name + "!</h1><hr/>";
System.out.println("返回给浏览器的响应数据为:" + html);
//创建服务器对浏览器的输出流,可以将数据从服务器发送到浏览器
PrintWriter out = response.getWriter();
out.println(html);
} @Override public void destroy() {
System.out.println("正在销毁servlet对象");
}
}
创建与初始化servlet对象,只在第一次访问servlet时进行,随后的访问不会再去执行,所以在整个系统中,servlet对象有且只有一个。
<a name="Ra2gq"></a>
## 使用注解简化配置servlet
之前我们创建了好几个servlet,但是在使用这些servlet的时候,我们发现需要在web.xml中进行很多的配置。这显然来说,对于我们的开发是很不友好的,需要在Java文件和xml文件之间进行大量的跳转,那么有没有一种简便的方法可以使我们的开发变得更容易呢?<br />在jdk 1.5之后,提供了注解,注解可以简化程序的配置。同样的,在Servlet 3.X版本之后也引入了注解Annotation的特性。在Servlet中,有且只有一个核心的注解:
> `@WebServlet`
这个注解可以用于简化Servlet在web.xml中的配置工作。
```java
@WebServlet("/sample")
public class SampleServlet extends HttpServlet{
//service()是请求处理的核心方法 无论get和post都会被处理,service方法不区别对待
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method = request.getMethod();//获取请求的请求方式
String name = request.getParameter("name");
String telphone = request.getParameter("telphone");
String sex = request.getParameter("sex");
String[] specs = request.getParameterValues("spec");
PrintWriter out = response.getWriter();
out.println("<h1>method:"+ method +"</h1>");
out.println("<h1>name:"+ name +"</h1>");
out.println("<h1>mobile:"+ telphone +"</h1>");
out.println("<h1>sex:"+ sex +"</h1>");
for(String str : specs) {
out.println("<h2>spec:"+ str +"</h2>");
}
}
}
启动时加载Servlet
就如同很多程序开机启动一样,servlet可以在Tomcat被启动的使用就进行加载,启动时加载在工作中常用于系统的预处理。我们可以在web.xml中进行设置,配置我们的启动加载服务。
<load-on-startup>
设置该节点可以控制启动时加载
0~9999(常用) (值>=0) 设置启动加载,值越小优先级越高
<servlet>
<servlet-name>first</servlet-name>
<servlet-class>com.dodoke.servlet.MyFirstServlet</servlet-class>
<!-- 设置启动加载,值越小优先级越高 -->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>first</servlet-name>
<url-pattern>/hi</url-pattern>
</servlet-mapping>
当然我们也可以使用注解的形式来指定启动时加载
//可以使用属性指定不同的功能
//在@WebServlet注解中,如果只指定了loadOnStartup属性,而没有指定urlPatterns属性,该注解是不起作用的
//因为@WebServlet注解强制要求必须指定urlPatterns属性
@WebServlet(urlPatterns="/first" , loadOnStartup=0)
public class MyFirstServlet extends HttpServlet {}