02_Servlet
一、初识Servlet
1.Servlet简介
Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
1、编写一个Java类,实现servlet接口。
2、把开发好的Java类部署到web服务器中
Java Servlet是和平台无关的服务器端组件,它运行在Servlet容器中。Servlet容器负责Servlet和客户的通信以及调用Servlet的方法 ,Servlet和客户的通信采用“请求/响应”的模式。Servlet可完成如下功能:
- 创建并返回基于客户请求的动态HTML页面。
- 创建可嵌入到现有HTML 页面中的部分HTML 页面(HTML 片段)。
- 与其它服务器资源(如数据库或基于Java的应用程序)进行通信。
Servlet容器响应客户请求的过程
Servlet,filter,listener统称为JavaWeb的三大组件,它属于动态资源。Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:
接收请求数据;
处理请求;
完成响应
实现Servlet有三种方式:
实现javax.servlet.Servlet接口;
继承javax.servlet.GenericServlet类;
继承javax.servlet.http.HttpServlet类;
通常我们会去继承HttpServlet类来完成我们的Servlet,但学习Servlet还要javax.servlet.Servlet接口开始学习。
二、Servlet初探
接下来我们开始准备完成Servlet,完成Servlet需要分为两步:
- 编写Servlet类;
- 在web.xml文件中配置Servlet;
①编写Servlet类:
//开发一个Serlvet 实现Serlvet接口
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init。。。。。。。");
}
@Override
public ServletConfig getServletConfig() {
System.out.println("get Servlet config。。。。");
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service。。。。。。");
}
@Override
public String getServletInfo() {
System.out.println("servlet info。。。。。");
return null;
}
@Override
public void destroy() {
System.out.println("destory。。。。。");
}
}
②配置Serlvet
<?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">
<servlet>
<!-- 给当前的servlet配置一个名称-->
<servlet-name>helloSerlvet</servlet-name>
<!-- 指定要配置的servlet是哪一个具体的serlvet 此处需要使用全类名-->
<servlet-class>org.lanqiao.web.HelloServlet</servlet-class>
</servlet>
<!-- 配置serlvet映射-->
<servlet-mapping>
<!-- 映射的servlet的名称 需要对应上边的serlvet-name-->
<servlet-name>helloSerlvet</servlet-name>
<!-- serlvet请求规则-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
在web.xml中配置Servlet的目的其实只有一个,就是把访问路径与一个Servlet绑定到一起,上面配置是把访问路径:“/hello”与“org.lanqiao.javaweb.HelloServlet”绑定到一起。
- < servlet>:指定HelloServlet这个Servlet的名称为hello;
- < servlet-mapping>:指定/hello访问路径所以访问的Servlet名为hello。
< servlet >和< servlet-mapping >通过< servlet-name >这个元素关联在一起了!
请求的过程: tomcat
localhost:8080/
tomcat中 /默认会请求servlet中的一个默认的defaultServlet
执行到welcome-file-list
1.url-pattern常用的配置方法:
/:表示项目的根路径
/ :是通配符 无论后边是什么都无所谓 都可以匹配的到 * 0-N个字符
多级路径配置:
<!-- 配置serlvet映射-->
<servlet-mapping>
<!-- 映射的servlet的名称 需要对应上边的serlvet-name-->
<servlet-name>helloSerlvet</servlet-name>
<!-- serlvet请求规则-->
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
使用扩展名配置:
<!-- 配置serlvet映射-->
<servlet-mapping>
<!-- 映射的色弱略同的名称 需要对应上边的serlvet-name-->
<servlet-name>helloSerlvet</servlet-name>
<!-- serlvet请求规则-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
常用的 扩展名 .do .action
三、Sevlet的生命周期:
- init :表示servlet的初始化 执行一次
- service:servlet的服务方法 是处理请求的 每一次请求都会执行service方法
- destroy:销毁(死亡) 执行一次 serlvet从serlvet容器中卸载的时候 才会执行
init方法表示servlet的创建:
创建时机可控:
- 默认情况下 是当第一次请求servlet的时候 才会执行该init 方法 创建servlet
< load-on-startup>< /load-on-startup>
- 该参数>=0 则表示该serlvet随着容器的启动而完成初始化
数字越小 初始化时机越早 - 当参数<0 则servelt的初始化时机为第一次请求时
此时初始话的时机 与数字大小无关 与请求顺序有关
每个Serlvet在整个声明周期中 有几个实例对象:
A 只有一个(用构造器可明显看出)
B 每次都会产生一个新的
四、HttpServlet
Servlet ---接口
GenericServlet---抽象类 ---service
HttpServlet --- 抽象类 ---无抽象方法 该类提供了对Http协议的支持
在开发中 sevlet的实现 采用继承HttpSerlvet,并重写doGet和doPost
public class HttpServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//请求处理的核心代码
System.out.println("请求处理。。。。。");
}
}
五、Servlet API
1.ServletConfig
- ServletConfig:init()方法的参数,它表示Servlet配置对象,它对应Servlet的配置信息,对应web.xml文件中的< servlet>元素。
- String getServletName():获取Servlet在web.xml文件中的配置名称,即< servlet-name>指定的名称;
- Enumeration getInitParameterNames():用来获取在web.xml中配置的所有初始化参数名称;
- String getInitParameter(String name):用来获取在web.xml中配置的初始化参数,通过参数名来获取参数值;
- ServletContext getServletContext():用来获取ServletContext对象
xml配置文件:
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>org.lanqiao.web.HelloServlet</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>user</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
servlet类:
@Override
public void init(ServletConfig servletConfig) throws ServletException {
// 获取web.xml中配置的该sevelt的 <servlet-name>helloSerlvet</servlet-name>的值
String servletName = servletConfig.getServletName();
System.out.println("servletName:---"+servletName);
// 获取该sevlet中所有的 初始化参数的名称
Enumeration<String> names = servletConfig.getInitParameterNames();
while(names.hasMoreElements()){
System.out.println("parameterName ="+names.nextElement());
}
//获取初始化参数的对应的值
String username = servletConfig.getInitParameter("username");
System.out.println(username);
String password = servletConfig.getInitParameter("password");
System.out.println(password);
}
2.ServletContext
2.1.ServletContext概述
服务器会为每个应用创建一个ServletContext对象:
- ServletContext对象的创建是在服务器启动时完成的;
- ServletContext对象的销毁是在服务器关闭时完成的。
- ServletContext对象的作用是在整个Web应用的动态资源之间共享数据!例如在AServlet中向
- ServletContext对象中保存一个值,然后在BServlet中就可以获取这个值,这就是共享数据了。
- Servlet引擎为每个WEB应用程序都创建一个对应的ServletContext对象,ServletContext对象被包含在ServletConfig对象中,调用ServletConfig.getServletContext方法可以返回ServletContext对象的引用。
- 由于一个WEB应用程序中的所有Servlet都共享同一个ServletContext对象,所以,ServletContext对象被称之为 application 对象(Web应用程序对象)。
- 功能:
①获取WEB应用程序的初始化参数
②记录日志
③application域范围的属性
④访问资源文件
⑤WEB应用程序之间的访问
2.2.获取ServletContext
ServletConfig#getServletContext();
GenericServlet#getServletContext();
HttpSession#getServletContext()
ServletContextEvent#getServletContext()
ServletContext application = servletConfig.getServletContext();
//获取初始化参数
String name = application.getInitParameter("name");
String age = application.getInitParameter("age");
System.out.println(name+"---"+age);
web.xml配置文件
<!-- ServletContext的初始化参数 可以被所有的servelt 所共享 -->
<context-param>
<param-name>name</param-name>
<param-value>jack</param-value>
</context-param>
<context-param>
<param-name>age</param-name>
<param-value>23</param-value>
</context-param>
2.3.域对象的功能
ServletContext是JavaWeb四大域对象之一:
PageContext;
ServletRequest;
HttpSession;
ServletContext
所有域对象都有存取数据的功能,因为域对象内部有一个Map,用来存储数据,下面是ServletContext对象用来操作数据的方法:
- void setAttribute(String name, Object value): 用来存储一个对象,也可以称之为存储一个域属性,例如:servletContext.setAttribute(“xxx”, “XXX”),在ServletContext中保存了一个域属性,域属性名称为xxx,域属性的值为XXX。请注意,如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的值,这一特性与Map相同;
- Object getAttribute(String name): 用来获取ServletContext中的数据,当前在获取之前需要先去存储才行,例如:String value = (String)servletContext.getAttribute(“xxx”);,获取名为xxx的域属性;
- void removeAttribute(String name): 用来移除ServletContext中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;
- Enumeration getAttributeNames(): 获取所有域属性的名称;
servletContext.setAttribute("schoolname","中北大学");
String schoolname = (String) servletContext.getAttribute("schoolname");
System.out.println("schoolname="+schoolname);
小结:
-
Servlet的生命周期
init(ServletConfig config) 创建并初始化servlet
load-on-startup
参数 >= 0 随着servelt的装载而进行初始化 数字越小 初始化越早
<0 则是在第一次请求时进行初始化
service(ServletRequest req ,ServletResponse resp)
serlvet的接受请求并处理请求 返回响应
destroy(); 销毁 当servlet从容器中卸载时 执行 后续的清理工作 -
Servlet的核心API
ServletConfig 封装了当前servlet的配置信息
ServletContext 代表整个web应用的全部信息 可以实现servelt之间的数据共享 -
四大域对象:
pageContext
ServletRequst
HttpSession
ServletContext
域对象的公有方法:
setAttribute(String key ,Object value);
getAttribute(String key);
removeAttribute(String key);
练习:
servlet类:
public class servletDemo1 extends HttpServlet {
//重写doGet和doPost
//doGet只能接受get请求 doPost只能接受post请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//编写核心业务代码
//获取servletconfig对象 通过servletconfig获取相关的配置信息
ServletConfig config = this.getServletConfig();
String name = config.getServletName();
String username = config.getInitParameter("username");
System.out.println(name +"------"+username);
//获取servletcontext对象
ServletContext context = req.getServletContext();
String app = context.getInitParameter("app");
System.out.println("app:"+app);
context.setAttribute("schoolname","中北大学");
String schoolname = (String) context.getAttribute("schoolname");
System.out.println("schoolname :"+schoolname);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
xml配置:
<context-param>
<param-name>app</param-name>
<param-value>javeEE</param-value>
</context-param>
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>org.lanqiao.web.servletDemo1</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>tom</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
练习:统计当前访问应用的用户数量
//需求:统计当前访问应用的用户数量
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//编写核心业务代码
//获取servletconfig对象 通过servletconfig获取相关的配置信息
ServletContext application = req.getServletContext();
Integer online_num = (Integer) application.getAttribute("online_num");
if (online_num == null){
online_num = 1;
}else {
online_num++;
}
application.setAttribute("online_num",online_num);
System.out.println("你是访问该应用的第"+online_num+"的用户");
}
3.Request
3.1.request概述
request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用Servlet.service()方法时传递给service()方法,这说明在service()方法中可以通过request对象来获取请求数据。
3.2谁可以发送请求:
HttpServletRequest 接受请求的
- 通过浏览器的地址栏发送请求 可以发送get请求
- 超链接发送请求 请求的地址href 只能发送get请求
- form表单 可以通过action来发送请求 不仅可以发送get请求还可以发送post请求 根据method属性来指定请求的方式
get的参数传递是通过路径后?参数名=参数值&参数名=参数值
post的参数通过请求体来传递参数的
发送get请求
http://localhost:8080/demo1?username=admin
3.3.request相关的其它方法
- int getContentLength():获取请求体的字节数,GET请求没有请求体,没有请求体返回-1;
- String getContentType():获取请求类型,如果请求是GET,那么这个方法返回null;如果是POST请求,那么默认为application/x-www-form-urlencoded,表示请求体内容使用了URL编码;
- String getMethod():返回请求方法,例如:GET
- Locale getLocale():返回当前客户端浏览器的Locale。java.util.Locale表示国家和言语,这个东西在国际化中很有用;
- String getCharacterEncoding():获取请求编码,如果没有setCharacterEncoding(),那么返回null,表示使用ISO-8859-1编码;
- void setCharacterEncoding(String code):设置请求编码,只对请求体有效!注意,对于GET而言,没有请求体!!!所以此方法只能对POST请求中的参数有效!
- String getContextPath():返回上下文路径,例如:/hello
- String getQueryString():返回请求URL中的参数,例如:name=zhangSan
- String getRequestURI():返回请求URI路径,例如:/hello/oneServlet
- StringBuffer getRequestURL():返回请求URL路径,例如:http://localhost/hello/oneServlet,即返回除了参数以外的路径信息;
- String getServletPath():返回Servlet路径,例如:/oneServlet
- String getRemoteAddr():返回当前客户端的IP地址;
- String getRemoteHost():返回当前客户端的主机名,但这个方法的实现还是获取IP地址;
- String getScheme():返回请求协议,例如:http;
- String getServerName():返回主机名,例如:localhost
- int getServerPort():返回服务器端口号,例如:8080
System.out.println("request.getContentLength(): " + request.getContentLength());
System.out.println("request.getContentType(): " + request.getContentType());
System.out.println("request.getContextPath(): " + request.getContextPath());
System.out.println("request.getMethod(): " + request.getMethod());
System.out.println("request.getLocale(): " + request.getLocale());
System.out.println("request.getQueryString(): " + request.getQueryString());
System.out.println("request.getRequestURI(): " + request.getRequestURI());
System.out.println("request.getRequestURL(): " + request.getRequestURL());
System.out.println("request.getServletPath(): " + request.getServletPath());
System.out.println("request.getRemoteAddr(): " + request.getRemoteAddr());
System.out.println("request.getRemoteHost(): " + request.getRemoteHost());
System.out.println("request.getRemotePort(): " + request.getRemotePort());
System.out.println("request.getScheme(): " + request.getScheme());
System.out.println("request.getServerName(): " + request.getServerName());
System.out.println("request.getServerPort(): " + request.getServerPort());
3.4.request获取请求参数
最为常见的客户端传递参数方式有三种:
浏览器地址栏直接输入:一定是GET请求;
超链接:一定是GET请求;
表单:可以是GET,也可以是POST,这取决与<form>的method属性值;
GET请求和POST请求的区别:
GET请求和POST请求的区别:
- GET请求:
①请求参数会在浏览器的地址栏中显示,所以不安全;
②请求参数长度限制长度在1K之内;
③GET请求没有请求体,无法通过request.setCharacterEncoding()来设置参数的编码; - POST请求:
①请求参数不会显示浏览器的地址栏,相对安全;
②请求参数长度没有限制;
3.5.中文乱码
解决post请求的中文乱码:
方式一:采用解码编码的方式来解决
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
//对获取到的数据进行节码 得到字节数组
byte[] usernameBytes = username.getBytes("iso-8859-1");
// 使用新的字符集重新编码
String name = new String(usernameBytes,"UTF-8");
System.out.println(name);
String key = req.getParameter("key");
System.out.println(key);
}
方式二:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设定接收请求所采用的字符集
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
System.out.println(username);
String key = req.getParameter("key");
System.out.println(key);
}
Get
通用解决方式:
配置tomcat的字符集 conf/serve.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8"/>
4.response
response是Servlet.service方法的一个参数,类型为javax.servlet.http.HttpServletResponse。在客户端发出每个请求时,服务器都会创建一个response对象,并传入给Servlet.service()方法。response对象是用来对客户端进行响应的,这说明在service()方法中使用response对象可以完成对客户端的响应工作。
response对象的功能分为以下四种:
设置响应头信息;
发送状态码;
设置响应正文;
重定向;
4.1.response响应正文
response是响应对象,向客户端输出响应正文(响应体)可以使用response的响应流,repsonse一共提供了两个响应流对象:
PrintWriter out = response.getWriter():获取字符流;
ServletOutputStream out = response.getOutputStream():获取字节流;
当然,如果响应正文内容为字符,那么使用response.getWriter(),如果响应内容是字节,例如下载时,那么可以使用response.getOutputStream()。
注意,在一个请求中,不能同时使用这两个流!也就是说,要么你使用repsonse.getWriter(),要么使用response.getOutputStream(),但不能同时使用这两个流。不然会抛出IllegalStateException异常。
字符响应流
字符编码
在使用response.getWriter()时需要注意默认字符编码为ISO-8859-1,如果希望设置字符流的字符编码为utf-8,可以使用response.setCharaceterEncoding(“utf-8”)来设置。这样可以保证输出给客户端的字符都是使用UTF-8编码的!
但客户端浏览器并不知道响应数据是什么编码的!如果希望通知客户端使用UTF-8来解读响应数据,那么还是使用response.setContentType(“text/html;charset=utf-8”)方法比较好,因为这个方法不只会调用response.setCharaceterEncoding(“utf-8”),还会设置content-type响应头,客户端浏览器会使用content-type头来解读响应数据。
缓冲区
response.getWriter()是PrintWriter类型,所以它有缓冲区,缓冲区的默认大小为8KB。也就是说,在响应数据没有输出8KB之前,数据都是存放在缓冲区中,而不会立刻发送到客户端。当Servlet执行结束后,服务器才会去刷新流,使缓冲区中的数据发送到客户端。
如果希望响应数据马上发送给客户端:
向流中写入大于8KB的数据;
调用response.flushBuffer()方法来手动刷新缓冲区;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
//使用response相应客户端 常用来:设置响应头 设置响应体
//设置响应 内容类型
//resp.setCharacterEncoding("UTF-8");
//设置响应的字符集
//resp.setContentType("text/html");
System.out.println(username);
//合法并写
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html lang=\"en\">");
out.println("<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>欢迎页</title>\n" +
"</head>");
out.println("<body>");
out.println("欢迎"+username);
out.println("</body>");
resp.flushBuffer();//刷新缓冲区
}
4.2.设置响应头信息(了解)
可以使用response对象的setHeader()方法来设置响应头!使用该方法设置的响应头最终会发送给客户端浏览器!
- response.setHeader(“content-type”, “text/html;charset=utf-8”):设置content-type响应头,该头的作用是告诉浏览器响应内容为html类型,编码为utf-8。而且同时会设置response的字符流编码为utf-8,即response.setCharaceterEncoding(“utf-8”);
- response.setHeader(“Refresh”,“5; URL=http://www.baidu.com”):5秒后自动跳转到百度主页
4.3.设置状态码及其他方法(了解)
- response.setContentType(“text/html;charset=utf-8”):等同与调用response.setHeader(“content-type”, “text/html;charset=utf-8”);
- response.setCharacterEncoding(“utf-8”):设置字符响应流的字符编码为utf-8;
- response.setStatus(200):设置状态码;
- response.sendError(404, “您要查找的资源不存在”):当发送错误状态码时,Tomcat会跳转到固定的错误页面去,但可以显示错误信息。
4.4.请求转发和重定向
4.4.1.请求转发:
1 浏览器向服务器端只发送了一次请求
2 浏览器的地址栏不会发生变化
3 不能转发到外部资源 只能转发内部资源
4 所以转发整个的过程都是一个请求 此时的request域的数据是可以在转发的servlet之间共享的 对于转发的serlvet来说是同一个请求
5 转发不仅可以转发到一个servlet 也可以转发到一个页面
路径前边 的第一个/表示根路径 就是我们项目结构中的web目录
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//请求转发
//req.getRequestDispatcher("/demo2").forward(req,resp);
//req.getRequestDispatcher("/WEB-INF/success.html").forward(req,resp);
req.getRequestDispatcher("home.html").forward(req,resp);
}
4.4.2.重定向
- 重定向向服务器端发送了两次请求 第一次请求可以是get也可以是post 但是第二次请求肯定是get
- 重定向时 地址栏会发生变化 显示重定向的地址
- 可以重定向到外部资源 可以重定向到内部资源
- 在整个请求 中发生了两次请求 产生了两个request对象 不能实现数据共享
- 也可以重定向到页面 和serlvet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//重定向
//resp.sendRedirect("http://www.baidu.com");
//resp.sendRedirect("/demo2");
resp.sendRedirect("/home.html");
}
4.4.3.转发和重定向的使用的选择:
不同servlet之间的调用 使用转发
如果要跳转页面 则使用重定向