1. Servlet简介
Servlet是服务器端的重要组件,直译为服务端的小程序,它属于动态资源,用来处理请求,服务器接收到请求后会调用Servlet来处理请求。
Servlet的主要作用 :
接收请求 ,
处理请求 ,
完成响应 。
例如:
当我们要完成一个登录功能时,用户会将输入的用户名和密码以POST请求的形式发送到服务器,但是服务器本身并不具有能力来读取用户发送的用户名和密码,也就不可能对用户名和密码进行验证,所以当服务器收到这类请求后需要将请求转个一个Servlet处理。
Servlet实现类由我们编写,而由Web服务器(Servlet容器)调用,每个Servlet都必须实现javax.servlet.Servlet
2.HelloServlet
(1) 创建一个类并实现Servlet接口
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* service方法是Servlet最重要的方法,Servlet每次接收请求时
* 都会调用service方法
* request:代表的是请求报文,是浏览器发送给服务器的
* response:代表的是响应报文,是服务器发送给浏览器的
*/
@Override
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
System.out.println("这是我的第一个Servlet!!");
//向浏览器返回一个内容
//获取一个输出流
PrintWriter out = response.getWriter();
//通过流向浏览器输出一个内容
out.print(new Date());
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
(2) 在WEB-INF目录下的web.xml文件中注册映射Servlet.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
<!--
注册servlet
servlet-name:Servlet的别名,在服务器中Servlet所有的配置都是通过别名,这个名字是给程序员用的,程序员通过别名对Servlet进行配置。
servlet-class:Servlet的全类名,用来创建Servlet实例, 这个名字是给服务器使用的,服务器通过全类名来创建Servlet实例。
url-pattern:为Servlet映射的请求地址,通过该地址可以访问我们创建的Servlet,这个名字是给用户使用的,用户通过该地址来访问Servlet。
-->
3.Servlet的生命周期:Servlet对象从被创建到被销毁的过程。
当我们第一次访问Servlet时,有三个方法被调用了: 构造器 ,init ,service 。
第一次以后每次访问,只会调用: service
当服务器停止时: destroy
(1) 构造器
当Servlet第一次接收请求的时候调用,用来创建Servlet对象,只调用了一次。
Servlet是一个单例的,在服务器中同一个Servlet只有一个实例。
它是以多线程的形式调用service方法
Servlet不是线程安全,所以在service方法尽量不要操作全局变量
(2) init()
在构造器调用之后马上被调用,用来初始化Servlet,只调用了一次。
(3) service()
每次处理请求时,service都会调用,用来处理请求,会调用多次。
(4) destroy()
当停止服务器时(项目卸载、Servlet对象销毁前),用来做一些收尾工作,只会调用一次。
4. ServletConfig
(1) 代表:SerlvetConfig代表的是Servlet的配置信息,具体来说就是Servlet标签中的内容,每一个Servlet都有其唯一的ServletConfig
(2) 获取:该对象由服务器创建,最终会作为参数传递到init()方法,我们可以在init中直接使用
(3) 功能
① 可以获取Servlet的别名
② 可以获取当前Servlet的初始化参数
③ 获取ServletContext对象:
@Override
public void init(ServletConfig config) throws ServletException {
//获取Servlet的别名
String servletName = config.getServletName();
System.out.println(servletName);
//获取当前Servlet的初始化参数
String user = config.getInitParameter("user");
String pwd = config.getInitParameter("password");
System.out.println(user+"--"+pwd);
//获取ServletContext对象
ServletContext servletContext = config.getServletContext();
System.out.println(servletContext);
}
5.ServletContext
(1) 代表:ServletContext代表的是整个的WEB应用,每一个WEB应用都有其唯一的一个ServletContext
(2) 获取:通过ServletConfig的getServletContext()方法获取
(3) 功能:
① 获取整个WEB应用的初始化参数
② 获取资源的真实路径(主要用来做文件的上传和下载)
String realPath = servletContext.getRealPath("index.html");
真实路径(物理路径):C:\Users\lilichao\Desktop\day07\note\笔记.txt
虚拟路径:http://localhost:8080/08_WEB_Servlet/BServlet
③ 作为一个域对象在不同的WEB资源之间共享数据
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
//获取ServletContext
ServletContext servletContext = config.getServletContext();
//获取整个web应用的初始化参数
String phone = servletContext.getInitParameter("phone");
System.out.println("phone = "+phone);
//获取index.html的真实路径
String realPath = servletContext.getRealPath("hello.html");
System.out.println(realPath);
}
6.GenericServlet
在显示开发中我们如果直接实现servlet接口功能也是可以正常实现的,但是所面临的问题是:如果我直接实现Servlet接口,那接口中的所有方法都必须要实现,但是这些方法有的我们用不到,而有的方法实现起来很麻烦而且没给servlet实现的代码都差不多,于是我们就需要一个抽象类来帮助我们实现servlet接口,实现一些通用的方法,而我们只需要继承GenericServlet就可以了,这样的好处是我们不在需要重写全部方法,而只需要重写必须的和我们需要的方法就可以。
7. HttpServlet
HttpServlet是GenericServlet的子类,他是Tomcat中专门处理HttpServlet的Servlet(实际上Tomcat只处理Http类型的请求)所以在一般情况下我们都会通过继承HttpServlet来实现servlet。
HttpServlet和GenericServlet一样都是一个抽象类,也就是不能对它们进行实例化,与GenericServlet不同HttpServlet中没有任何抽象方法,所以需要我们根据实际需求来重写它的方法。
在GenericServlet中service(ServletRequest req, ServletResponse res)方法是一个抽象方法,必须要由子类实现,而在HttpServlet中已经被实现,而在HttpServlet的实现的方法中会被强制转换成HttpServletRequest和HttpServletResponse(不用担心会出现类型转换异常,因为传过来的对象本身就是该类型)。转换类型之后会调用HttpServlet中的一个重载的service(HttpServletRequest req, HttpServletResponse resp)方法。也就是说如果我们要重写service()方法不必再去重写GenericServlet的service()方法了,而可以直接重写HttpServlet重载的service()方法就可以了。
在service(HttpServletRequest req, HttpServletResponse resp)方法中,会对它的请求类型进行判断,然后根据请求类型的不同再去调用不同的方法。如:post类型的请求回去调用doPost(),get请求回去调用doGet(),delete请求回去调用doDelete(),以此类推。但是在实际应用中我们只会使用到doPost()和doGet(),所以一般情况下我们不会去重写service()方法,而是去重写更简单的doGet()或者doPost()。
8.HttpServletRequest 和
HttpServletResponse
(1)
HttpServletRequest
代表:代表的是浏览器发送给服务器的请求报文
获取:该对象由服务器创建最终作为参数传递doGet()或者doPost()方法中,我们可以在这两个方法中直接使用。
功能:
① 获取用户发送的请求参数
② 获取项目的名字(主要用来设置绝对路径)
③ 可以作为一个域对象在不同的web资源之间共享数据。
④ 可以做请求的转发:
转发就是Servlet收到请求以后,自己不去处理请求,而是调用服务器内部的其他资源去处理请求
(2)
HttpServletResponse
代表:代表是服务器发送给浏览器响应报文。
获取:该对象由服务器创建最终作为参数传递doGet()或者doPost()方法中,我们可以在这两个方法中直接使用。
功能:
① 向浏览器输出一个页面片段,这个方法实际上是在设置响应报文的响应体。
② 做请求的重定向
所谓的重定向,指Servlet收到浏览器发送的请求以后,返回给浏览器一个特殊的响应,这个特殊的响应告诉浏览器,再去向另一个地址发送请求。
③ 重定向的特点:
【1】重定向时浏览器发送了 2 次请求。
【2】重定向是在浏览器端发生。
【3】重定向时浏览器的地址栏会发生变化。
【4】浏览器可以知道重定向的发生。
(3) 代码说明
public class FServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/**
* HttpServletRequest
* 代表:代表的是浏览器发送给服务器的请求报文
* 获取:该对象由服务器创建最终作为参数传递doGet()或者doPost()方法中,我们可以在这两个方法中直接使用。
* 功能:
* [1]获取用户发送的请求参数
* [2]获取项目的名字(主要用来设置绝对路径)
* [3]可以作为一个域对象在不同的web资源之间共享数据。
* [4]可以做请求的转发
*/
//获取用户发送的请求参数
String username = request.getParameter("um");
String password = request.getParameter("password");
//将用户名输出到页面中
response.getWriter().print("username = "+username +"<br />");
response.getWriter().print("password = "+password);
//获取项目名
String contextPath = request.getContextPath();
System.out.println(contextPath);
//发起请求的转发
//1.获取一个请求的派发器
RequestDispatcher dispatcher = request.getRequestDispatcher("target.html");
//2.使用派发器发起转发
dispatcher.forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/**
* HttpServletResponse
* 代表:代表是服务器发送给浏览器响应报文。
* 获取:该对象由服务器创建最终作为参数传递doGet()或者doPost()方法中,我们可以在这两个方法中直接使用。
* 功能:
* [1]向浏览器输出一个页面或者是页面片段
* [2]做请求的重定向
*/
//向浏览器输出一个页面片段
//这个方法实际上是在设置响应报文的响应体
//1.获取一个输出流
PrintWriter out = response.getWriter();
//2.通过这个流向浏览器输出一段信息
out.print("<h1>Hello Servlet!</h1>");
//做请求的重定向
response.sendRedirect("target.html");
}
}
(4) 转发和重定向的对比:
在一般情况下,用户名验证码等都正确就重定向到成功页面,如果失败就转发到失败页面。例如:
重定向:response.sendRedirect(request.getContextPath()+ "/pages/user/regist_success.html" );
转 发 :request.getRequestDispatcher( "/pages/user/regist_error.html" ).forward(request,response);
9.字符编码
计算机在传输字符,需要将字符转换为二进制数据来传输,读取内容时又需要将二进制数据转换为字符,编码和解码所使用的规则,我们称为字符集,常见的字符集: ASCII、ISO-8859-1、GBK、GB2312、UTF-8。
产生乱码问题的根本原因:编码和解码所采用的字符集不同。
解决方案:统一编码和解码所采用的字符集为utf-8。
(1) 请求:浏览器 --> 服务器 :
浏览器编码,服务器解码
① post请求
post请求是在Servlet中解码的,
Servlet中默认使用iso-8859-1,而它不支持中文,所以肯定乱码,
所以post请求我们只需要去指定request使用的字符集即可。
解决方案:在request.getParameter()方法第一次调用之前调用一下代码:
request.setCharacterEncoding("utf-8");
② get请求
get请求是通过url地址来传递请求参数的,
它会由Tomcat服务器自动解码,而服务器默认使用的是iso-8859-1。
解决方案:修改服务器默认的解码字符集,在server.xml配置文件中,在Connector标签中,添加一个 URIEncoding="utf-8"。
最终为:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
(2) 响应:服务器 --> 浏览器 :
服务器编码,浏览器解码。
解决方案:在向浏览器返回响应之前,调用如下代码:
response.setContentType("text/html;charset=utf-8");
10.路径问题
URL地址的格式:http://主机地址:端口号/项目名/资源路径/资源名
(1) 相对路径:
相对路径相对于:http://主机地址:端口号/项目名/资源路径/
由于转发的出现,相对路径非常容易出错,所以在开发中我们不使用相对路径,而是使用绝对路径。
(2) 绝对路径
绝对路径使用 "/" 开头;
由浏览器去解析的绝对路径,这个"/"代表的是服务器的根目录,http://主机地址:端口号/
注意:由浏览器解析的绝对路径,我们需要添加上项目名。
由服务器解析的绝对路径,这个"/"代表的是项目的根目录
即:http://主机地址:端口号/项目名/所以由服务器解析路径不需要加上项目名。
(3) 常见的路径:
① url-pattern:
② 转发的路径:
url-pattern和转发的路径都是由服务器所解析的,是以项目的根目录为根目录,
即: "/"代表的是项目的根目录:http://主机地址:端口号/项目名/
③
重定向的路径:
④
页面中的路径:
重定向和页面中的路径都是由浏览器解析的,是以服务器的根目录为根目录,即:
"/"代表的是服务器的根目录:http://主机地址:端口号/
(5) base标签(HTML页面的处理方式):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- base标签中的href属性,将会作为页面中所有相对路径的前缀 -->
<!-- base标签的作用就是让我们可以以使用相对路径的方式使用绝对路径 -->
<!-- base标签只对相对路径起作用 -->
<base href="http://localhost:8080/10_WEB_Path/" />
</head>
<body>
<h1>我是网站的首页</h1>
<!-- 使用绝对路径 -->
<a href="/10_WEB_Path/hello/1.html">去1.html</a>
<hr />
<!-- 使用base标签 -->
<a href="hello/1.html">去1.html</a>
</body>
</html>