目录
什么是servlet
Java官方规定的web开发的api(规范)。
网络编程对于客户端和服务端:
两端使用支持网络编程的语言,就能实现网络编程(网络通信)
但是应用层协议,还是需要应用程序自己来进行封装和分用。
对于hhtp协议:
- 客户端:基于浏览器可以省略程序自己封装分用http数据的步骤(浏览器会帮助我们进行封装和分用);
- 服务端:基于web服务器(部署网站,对应省略http协议处理的逻辑)也可以类似的省略封装和分用的步骤。
对于web服务器,有多种产品(支持多种编程语言),支持Java语言的,对应也有多种产品。对以一个Java开发的网站,可以运行在这些支持Java语言的不同web服务器上,就需要满足一定的规范,这个规范就是servlet。
总结:
- web服务器:针对所有编程语言,能运行网站系统的服务器程序;
- servlet容器:运行Java开发的网站(Java开发的网站就一定使用了servlet技术);
- servlet:Java官方规定的web开发的api规范。
一个完整的servlet程序
(1)创建一个maven项目
(2)引入依赖
项目创建完成后,会自动生成一个pom.xml文件。我们需要在该文件中引入servlet依赖包。需要注意以下事项:
(3)创建目录
项目创建好之后,idea会帮我们自动创建一些目录(我们还需要自己配置一个webapp目录):
- src:表示源代码所在的目录;
- main/java:源代码的根目录;
- main/resources:项目的一些资源文件所在的目录;
- test/java:测试代码的根目录。
此外,我们还需要创建一些新的目录:
在main目录下,和Java目录并列,创建一个webapp目录,webapp目录内部再创建一个WEB-INF目录,在该目录下创建一个web.xml文件。
编写web.xml:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> </web-app>
- webapp目录是未来部署到Tomcat中的一个重要的目录,可以在其中放一些静态的文件(html,css等);
- web.xml文件:Tomcat找到这个文件才能正确处理webapp中的动态资源。
(4)编写servlet代码
开发servlet的步骤(java目录下):
- 类注解@WebServlet:字符串必须以/开头,一个项目可以开发多个servlet,但是其路径必须唯一;
- 继承HttpServlet;
- 重写doXXX方法,XXX表示提供的方法。(常见doGet、doPost)
【1】获取请求信息:通过servlet规定的api,HttpServletRequest方法参数对象;
【2】进行一些逻辑操作:比如数据库的操作等;
【3】获取响应信息:通过servlet规定的api,HttpServletResponse方法参数对象。
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("hello world");
}
}
(5)打包程序
使用maven进行打包,找到项目目录下的Lifecycle的package双击即可,一定要出现success才算打包成功。打包成功后会在target目录下生成一个war包。
(6)部署
将war文件复制到tomcat/webapps目录下。
(7)运行
通过tomcat来运行网站。
补充
可以看到我们打包的war包名称很长,而且其中包含版本号,牵涉到版本更新的时候比较麻烦,所以我们可以使用以下代码配置文件名。
<build>
<!-- 最终打包的文件名,建议配置 -->
<finalName>servlet-demo</finalName>
</build>
部署方式
对于上面的部署方式,手动将war包拷贝到webapps目录下比较麻烦,我们可以使用idea中的Smart Tomcat插件更方便的完成部署。
(1)安装smart插件
按照以下步骤安装Smart Tomcat插件:
(2)配置Smart Tomcat插件
找到右上角Add:
进行如下配置:
访问出错的情况
404
表示用户访问的资源不存在。大部分情况是URL的路径有问题。
注意请求路径的写法:
如下,缺少上下文路径:
405
表示对应的HTTP请求方法没有实现。
如下,将上面的servlet程序的重写的doGet方法改为doPost方法(通过地址栏访问使用的是get方法):
500
往往是servlet代码中抛出异常导致。
如下,修改servlet代码,使其抛出异常,访问时HTTP状态码为500:
Servlet运行原理
Servlet容器(tomcat等运行Java网站的web服务器)和Servlet:容器管理整个Servlet的生命周期。
- Tomcat的代码中内置了main方法。我们启动Tomcat时,就是从Tomcat的main方法开始执行的;
- 类@WebServlet注解修饰的类会在Tomcat启动的时候被获取到,并集中管理;
- Tomcat通过反射来创建被注解修饰的类的实例;
- 这些实例被创建完之后,会调用init()方法进行初始化;
- 这些势力被销毁之前,会调用destory()方法进行收尾工作;
- Tomcat为了能同时相应多个HTTP请求,采取了多线程的方式实现。
处理请求的时候,根据请求路径(url)找到对应的servlet对象。
servlet三大生命周期方法:
- init():初始化方法,实例化对象之后,执行一次;
- service():每次请求,执行一次;
- destory():销毁方法,只执行一次。
Servlet相关API
【1】HttpServlet
方法 | 调用时机 |
init | 对象实例化之后调用一次,初始化 |
service | 收到HTTP请求调用一次 |
destroy | 实例不在使用的是时候调用一次 |
doGet/doPost | 收到GET/POST请求之后调用(由service方法调用) |
doPut/doDelete/… | 收到其他请求的时候调用 |
注:HttpServlet实例只在程序启动时创建一次。
【2】HttpServletRequest
方法 | 描述 |
String getProtocol() | 返回请求协议的名称和版本号 |
String getMethod() | 返回请求的HTTP的方法 |
String getContextPath() | 获取URL的应用上下文路径 |
String Parameter(String name) | 以字符串的形式返回请求参数的值,如果参数不存在返回null。 |
InputStream getInputStream() | 用于获取请求的body内容。返回一个InputStream对象。(body的任何格式都可以获取,但一般用于json) |
void setCharacterEncoding() | 设置被发送到客户端的响应的字符编码格式 |
【3】HttpServletResponse
void setStatus(int sc) | 为该响应设置状态码 |
void setContentType(String type) | 设置被发送到客户端的相应的内容的类型 |
PrintWriter getWriter() | 往body中写入文本格式(写网页、json) |
OutPutStream getOutPutStream() | 往body中写入二进制格式的数据(写文件) |
- 对于Servlet代码,需要注意的是,代码执行执行到重写的doXXX方法时,表示网站可以正常响应,此时响应状态码为200,所以即使重写的方法中抛出异常,状态码也为200。(即使状态码为200,也可能是后端出现异常);
- 对于状态码/响应头的设置要放到getWriter/getOutPutStream之前,否则可能设置失效。
上传文件
HttpServletRequest类方法
方法 | 描述 |
Part getPart(String name) | 获取请求中给定name的文件 |
Collection<Part> getPart() | 获取所有的文件 |
Part类的常用api
方法 | 描述 |
String getSubmittidFileName() | 获取提交的文件名 |
String getContentType() | 获取提交的文件类型 |
long getSize() | 获取文件的大小 |
void write(String path) | 把提交的文件数据写入磁盘文件 |
POST提交from-date(包含上传文件)
在提交from-data格式的数据时,会保存客户端上传的文件到服务端本地路径,那么保存的文件,一般如何进行设计呢?
- 文件可以存放在数据库中:
【1】文件非常小,可以考虑放进去=>将二进制的数据转换为Base64(字符串)保存
【2】文件比较大,就不建议放进去了=>数据库网络带宽非常重要,一般是用单独的服务器来存储文件。- 也可以存放在某个服务器主机硬盘上(此时需要考虑,如何才能访问):
可以自己写代码提供一个服务资源(servlet),请求数据包含一些信息,就可以返回不同的文件响应了。(但是不要将其放在web项目的webapp包下,虽然此时tomcat可以直接访问到,但是打包的时候消耗的资源太多)
请求和响应(总结)
服务器获取数据的格式
- queryString:
String s=request.getParacmeter("请求数据的键");- 表单格式:getParacmeter;
- json格式:
(1)先获取请求中的输入流(getInputStream),输入流包含body请求数据;
(2)再使用第三方json框架来处理:把json字符串转换为Java对象(反序列化);- from-data:
(1)简单格式:getParacmeter
(2)复杂格式:getPart;
服务端返回数据的格式
前后端分离技术:
- ajax请求:返回json字符串(将Java字符串转换为josn对象:序列化);
- 图片,音频等,浏览器会自动的发送请求,servlet返回二进制数据。
序列化和反序列化
- 序列化:站在自己程序角度,将程序中的对象转换为其他格式用于返回响应数据;
- 反序列化:站在自己程序的角度,将其他格式转换为自己程序中的对象,用于接收请求数据。
实现页面跳转
- 使用a标签:
(1)如果直接给html路径,就无法进行校验:
(2)写servlet路径,可以进行校验。- 前端js代码:location.herf=“跳转的路径”(相当于修改地址栏的url)
需要进行验证:前端某个事件中调用ajax函数,发请求,根据返回的响应数据,来决定是否跳转。- 后端进行跳转:重定向。
重定向和转发:
- 重定向:两次请求,路径会发生改变:
(1)发送的请求,返回重定向状态码,响应头Location字段(表示要跳转的路径)
(2)浏览器自动发起第二次请求(地址栏会改变为location路径)- 转发:一次请求,路径不会发生改变(服务端会自动帮助找资源)。
Cookie和Session
Cookie
客户端保存数据的技术。属于客户端的机制,而session是服务端的机制。
如何操作数据?
- 保存数据:js、服务端的响应头(Set-Cookie)=>保存在客户端本地硬盘和浏览器/域名相关的路径上;
- 使用数据:浏览器自动的携带请求头(Cookie)
会话管理(Cookie+Session)
背景:Http协议是一种无状态协议(应用层没有保存连接状态)。多次请求,基于Http本身不知道具体是哪个用户发送。
会话的本质就是一个哈希表,存储一些键值对结构,value就是用户信息。
会话:web开发中,会话是用户登录一个网站后,推出或超时之前,都属于一个会话。
流程:
- 登录时,验证账号密码:
【1】创建Session会话,并保存在服务端;
【2】返回响应头Set-Cookie:sessionid=XXX。- 客户端保存Cookie信息到本地;
- 客户端的每次请求都会携带Cookie请求头(sessionid).
Servlet中使用Session会话管理
HttpServletRequest类中的方法
方法 | 描述 |
HtttpSession getSession() | 在服务器中获取会话(不带参数时默认是true),参数如果为true,会话不存在时新建会话;参数为false,会话不存在时返回null。 |
HttpSession类中的方法
方法 | 描述 |
Object getAttribute(String name) | 该方法返回该session会话中具有指定名称的对象,如果没有指定名称的对象,则返回null |
void setAttribute(String name,Object value) | 该方法使用指定的名称绑定一个对象到该session会话 |
void invalidate() | 使会话失效,注销时使用 |