Servlet学习
Servlet介绍
问题:
服务器在接收到浏览器的请求后,会自动调用对应的逻辑代码进行请求
处理。但是逻辑代码是由程序员编写并放到服务器中,那么服务器怎么知道该怎
么调用并调用哪个类和哪个方法来进行请求处理。
解决:
程序员在编写代码的时候如果能够按照服务器能够识别的规则进行编写,
浏览器按照指定的规则进行发送请求,那么服务器就可以调用并执行响应的逻辑
代码进行请求处理了。
实现:
Servlet 技术
概念:
狭义的 Servlet 是指 Java 语言实现的一个接口,广义的 Servlet 是
指任何实现了这个 Servlet 接口的类,一般情况下,人们将 Servlet 理解为后者。
Servlet 运行于支持 Java 的应用服务器中。从原理上讲,Servlet 可以响应任何
类型的请求,但绝大多数情况下 Servlet 只用来扩展基于 HTTP 协议的 Web 服 务器
特点:
运行在支持 java 的应用服务器上
Servlet 的实现遵循了服务器能够识别的规则,也就是服务器会自动的根据请求调用对应的 servlet 进行请求处理。简单方便,可移植性强
使用:
1、 创建普通的 java 类并继承 HttpServlet
2、 覆写 service 方法
3、 在 service 方法中书写逻辑代码即可
4、 在 webRoot 下的 WEB-INF 文件夹下的 web.xml 文件中配配置 servlet
Servlet 的 web.xml 配置
Web.xml 配置的作用: 保护 Servlet。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
<servlet> <servlet-name>my</servlet-name> <servlet-class>com.szxy.servlet.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>my</servlet-name> <url-pattern>/my</url-pattern> <url-pattern>/m</url-pattern> <url-pattern>*.do</url-pattern> <url-pattern>/*</url-pattern> </servlet-mapping>
|
加载时机:
服务器启动的时候会将 webapps 中部署好的项目统一进行加载,并完成对每个项目的 web.xml 文件的加载。
注意:
一个 Servlet 可有拥有多个 url-pattern 配置,但是 一个 url-pattern 配置只能对应一个 Servlet。
Servlet是单例的
服务器在接收到浏览器的请求后,会开辟一个线程来处理此次请求, 在线程中调用对应的Servlet进行处理。
服务器调用Servlet处理请求,但是一个Servlet服务器只会创建一个实例化对象,该对象是线程共享的。
Servlet是单例的,只创建一个对象。
Servlet的生命周期
Servlet的生命周期:从第一次被调用到服务器关闭
init():servlet被初始化创建的时候调用
service():处理请求的时候
destory():Servlet对象被销毁的时候(服务器关闭时销毁Servlet对象)
注意:
可以在web.xml中配置
1 2 3 4 5 6
| <servlet> <servlet-name>LifeServlet</servlet-name> <servlet-class>com.szxy.servlet.LifeServlet</servlet-class> <!-- 配置servlet服务器启动时完成加载和初始化创建 --> <load-on-startup>1</load-on-startup> </servlet>
|
来设置Servlet加载时机为服务器启动(生命周期从服务器开启到服务器关闭)
本质:单例的懒汉式和饿汉式
注:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 在servlet的配置当中,1的含义是: 标记容器是否在启动的时候就加载这个servlet。 当值为0或者大于0时,表示容器在应用启动时就加载这个servlet; 当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。 正数的值越小,启动该servlet的优先级越高。 如果我们在web.xml中设置了多个servlet的时候,可以使用load-on-startup来指定servlet的加载顺序,服务器会根据load-on-startup的大小依次对servlet进行初始化。不过即使我们将load-on-startup设置重复也不会出现异常,服务器会自己决定初始化顺序。 配置load-on-startup后,servlet在startup后立即加载,但只是调用servlet的init()方法,用以初始化该servlet相关的资源。初始化成功后,该servlet可响应web请求;如未配置load-on-startup,容器一般在第一次响应web请求时,会先检测该servlet是否初始化,如未初始化,则调用servlet的init()先初始化,初始化成功后,再响应请求。 PS:一般我们在开发web应用时,都会配置这个参数,有两个好处:1、如果初始化过程失败,则容器会提示启动失败,此时我们能够提前知道相关错误;2、配置该参数相当于将初始化servlet的工作转移到容器启动过程,使得容器只要启动成功后,就可立即响应web请求。 --------------------- 作者:saiofo 来源:CSDN 原文:https://blog.csdn.net/u012393192/article/details/79323979 版权声明:本文为博主原创文章,转载请附上博文链接!
|
Servlet中的方法
doGet(HttpServletRequest req, HttpServletResponse resp)
特点:
处理get方式的请求
注意:如果在servlet中没有生命对应的请求处理方法,则会报405
doPost(HttpServletRequest req, HttpServletResponse resp)
特点:
处理post方式的请求
service(HttpServletRequest req, HttpServletResponse resp)
特点:
如果没有重写该方法,父类中调用service()方法进行请求转发(doGet(),doPost())
如果重写该方法,服务器会执行该方法
注意:tomcat服务器只会调用Servlet中service()方法(Servlet接口中只有service()方法),如果没有覆写service(),则会调用父类HttpServlet中的service()
Servlet 的常见错误总结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| Servlet的常见错误: 404错误:资源未找到 原因一:在请求地址中的servlet的别名书写错误。 原因二:虚拟项目名称拼写错误
500错误:内部服务器错误 错误一: java.lang.ClassNotFoundException: com.bjsxt.servlet.ServletMothod 解决: 在web.xml中校验servlet类的全限定路径是否拼写错误。 错误二: 因为service方法体的代码执行错误导致 解决: 根据错误提示对service方法体中的代码进行错误更改。
405错误:请求方式不支持 原因: 请求方式和servlet中的方法不匹配所造成的(调用父类的doGet() 或者doPost()方法) 解决: 尽量使用 service 方法进行请求处理,并且不要再 service 方法中调用父类的service
|
Request 对象
问题:
浏览器发起请求到服务器,会遵循HTTP协议将请求数据发送给服务器。
那么服务器接受到请求的数据改怎么存储呢?不但要存,而且要保证完整性。
解决:
使用对象进行存储,服务器每接受一个请求,就创建一个对象专门的存 储此次请求的请求数据。
实现:
request 对象
解释:
服务器接收到浏览器的请求后,会创建一个 Request 对象,对象中 存储了此次请求相关的请求数据。服务器在调用 Servlet 时会将创建的 Request 对象作为实参传递给 Servlet 的方法,比如:service 方法。
使用:
获取请求头数据
获取请求行数据
获取用户数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| package com.szxy.servlet;
import java.io.IOException; import java.util.Enumeration;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
/** * Request对象学习 * 获取请求数据: * 请求行:请求方式,请求URL,协议 * getMethod(); 返回请求方式 * getRequestURL() 返回请求url * getRequestURI() 返回请求uri * getQueryString() 返回get请求URL中的请求数据 * getScheme() 返回协议 * 请求头:getHeader(String name) * 注意:如果获取的请求头信息不存在则放回null * getHeaderNames() 返回存储请求头键名的枚举集合 * 请求实体: * getParameter(String name) 根据键名获取数据 * 注意: * 键名就是前端页面的表单标签的name属性的值或者其他方式提交的数据的键的名 * 如果没有对应的请求数据,则返回null * getParameterValues(String name) 根据键名获取同名不同的值,返回数组 * 注意:没有对应的键名,返回null * getParameterNames() 返回实体数据中键名的枚举 * 请求中的网络数据 * getRemoteAddr() 获取客户端ip * getRemotePort() 获取客户端的端口号 * getLocalAddr() 获取服务器端的ip * getLocalPort() 获取服务器端的端口 * @author Momentonly * */ @WebServlet("/request") public class RequestServlet extends HttpServlet{ @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取请求信息 //获取请求行信息 //获取请求方式 String method = req.getMethod(); System.out.println("请求方式:" + method); //获取请求URL StringBuffer requestURL = req.getRequestURL(); System.out.println("请求URL:" + requestURL.toString()); //获取请求URI String requestURI = req.getRequestURI(); System.out.println("请求URI:" + requestURI); //获取get请求URL中的请求数据 String queryString = req.getQueryString(); System.out.println("获取get请求URL中的数据:" + queryString); //获取协议 String scheme = req.getScheme(); System.out.println("获取请求协议:" + scheme); //获取请求头信息 //根据键名获取请求头信息(如果不存在则返回null) String header = req.getHeader("User-Agent"); System.out.println("获取浏览器版本信息" + header); //获取请求头中键名的枚举 Enumeration<String> headerNames = req.getHeaderNames(); while(headerNames.hasMoreElements()){ //获取请求头键名 String name = headerNames.nextElement(); //获取请求头的键名对应的值 System.out.println(name + ":" + req.getHeader(name)); } //获取请求实体数据(用户数据) //根据键名获取数据(如果请求中不存在键则返回null) String uname = req.getParameter("uname"); String pwd = req.getParameter("pwd"); System.out.println("请求实体数据:" + uname + ":" + pwd); //获取同键不同值的实体数据 String[] favs = req.getParameterValues("fav"); if(favs != null){ //NullPointerException for(String s : favs){ System.out.println("fav的值为" + s); } } //获取请求实体中键名的枚举 Enumeration<String> names = req.getParameterNames(); while(names.hasMoreElements()){ //System.out.println(names.nextElement()); //判断 String name = names.nextElement(); if("fav".equals(name)){ String[] favs2 = req.getParameterValues(name); if(favs2 != null){ for(String s : favs2){ System.out.println(name + ":" + s); } } }else{ String value = req.getParameter(name); System.out.println(name + ":" + value); } } //请求相关的网络数据 //获取客户端信息 String remoteAddr = req.getRemoteAddr(); System.out.println("客户端ip:" + remoteAddr); //获取客户端的端口号(浏览器) int remotePort = req.getRemotePort(); System.out.println("客户端的端口号:" + remotePort); //获取服务器主机ip String localAddr = req.getLocalAddr(); System.out.println("获取服务器ip:" + localAddr); //获取服务器的端口号 int localPort = req.getLocalPort(); System.out.println("服务器端口号:" + localPort); //处理请求信息 //响应处理结果 } }
|
Response对象
问题:
在使用 Request 对象获取了请求数据并进行处理后,处理的结果如何显示到浏览器中呢?
解决:
使用 Response 对象
解释:
服务器在调用指定的 Servlet 进行请求处理的时候,会给 Servlet 的方法 传递两个实参 request 和 response。 其中 request 中封存了请求相关的请求 数据,而 response 则是用来进行响应的一个对象。
使用:
设置响应行
设置响应头
设置响应实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package com.szxy.servlet;
import java.io.IOException;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
/** * Response对象 * 设置响应行: 协议 状态码 状态消息 * resp.sendError(int status) 自定义响应的状态码 * 设置响应头 * addHeader(String name, String value) 添加响应头信息,同名数据不会覆盖 * setHeader(String name, String value) 设置响应头信息,会覆盖原有信息,如果没有该响应头则添加 * 设置响应实体(处理结果) * resp.getWriter().write("实体内容"); * 注意: * 实体内容可以分开响应 * * 注意:使用resp对象做出了请求响应,意味着此次请求处理完毕,服务器在响应后会将此次请求相关的req和resp对象销毁 * * @author Momentonly * */ @WebServlet("/response") public class ResponseServlet extends HttpServlet{ @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("ResponseServlet.service()"); //获取请求数据 //处理请求数据 //响应处理结果 //设置响应行 //自定义响应404异常 //resp.sendError(404); //设置响应头 //添加响应头信息 resp.addHeader("mouse", "thinkpad"); resp.addHeader("mouse", "thinkpad2"); //设置响应头 //resp.setHeader("Content-Length", "20"); //resp.setHeader("mouse", "two fly"); //resp.setHeader("mouse", "two fly2"); //设置响应实体 resp.getWriter().write("resp object"); resp.getWriter().write("resp object"); } }
|
乱码问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| package com.szxy.servlet;
import java.io.IOException;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
/** * 乱码问题 * 请求乱码(服务器获取的请求数据乱码) * post: * req.setCharacterEncoding("utf-8"); * get: * 方式一: * String uname = req.getParameter("uname"); String uname2 = new String(uname.getBytes("iso-8859-1"),"utf-8"); * 方式二:配置tomcat的server.xml * req.setCharacterEncoding("utf-8"); * 在tomcat中的server.xml中的Connecgtor标签中增加属性:useBodyEncodingForURI="true" * 响应乱码(浏览器中显示的服务器响应数据乱码) * service流程 * 设置请求编码格式 * 设置响应编码格式 * 获取请求信息 * 处理请求信息 * 响应处理结果 * @author Momentonly * */ @WebServlet("/code") public class CodeServlet extends HttpServlet{ @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置请求编码格式 req.setCharacterEncoding("utf-8"); //设置响应编码格式 //resp.setHeader("ontent-type","text/html;charset=utf-8"); resp.setContentType("text/html;charset=utf-8"); //获取请求数据 System.out.println(req.getMethod()); System.out.println(req.getRequestURI()); String uname = req.getParameter("uname"); //String uname2 = new String(uname.getBytes("iso-8859-1"),"utf-8"); //处理请求数据 System.out.println(uname); //响应处理结果 resp.getWriter().write("请求处理完毕"); } }
|
request中的setCharacterEncoding方法作用是用指定的编码集去覆盖request对象中的默认的”ISO-8859-1”编码集,这样request.getParameter(“”)方法就会用新的编码集去解码
请求转发
转发
问题:
服务器在接收到浏览器的请求后,仅仅使用一个 Servlet 进行请求处理,会造成不同的 Servlet 逻辑代码冗
余,Servlet 的职责不明确。
解决: 使用请求转发。
特点: 降低servlet之间的代码冗余
一次请求转发内的servlet共享此次请求
浏览器地址栏信息不改变,一次请求
1
| req.getRequestDispatcher("转发的路径").forward(req, resp);
|
Request对象作用域
一次请求
问题:使用请求转发后,不同的 Servlet 之间怎么进行数据的共享 呢?或者说数据怎么从一个 servlet 流转给另外 一个 Servlet 呢?
解决:使用 request 对象的作用域
使用:
request.setAttribute(object name,Object value);
request.getAttribute(Object obj)
作用:解决了一次请求内的不同 Servlet 的数据(请求数据+其他数据)共享问题。
作用域:基于请求转发,一次请求中的所有 Servlet 共享。
注意:
使用 Request 对象进行数据流转,数据只在一次请求内有效。
特点:
服务器创建
每次请求都会创建
生命周期一次请求
重定向
两次请求
问题:
如果当前的请求,Servlet 无法进行处理怎么办?
如果使用请求转发,造成表单数据重复提交怎么办?
解决:
使用重定向
使用:
response.sendRedirect(“路径”).
本地路径为:uri
网络路径为:定向资源的 URL 信息
特点:
两次请求浏览器地址栏信息改变
避免因为用户的刷新而造成表单重复提交
Cookie
作用:解决了不同请求之间请求数据共享的问题
特点:
1)如果不设置有效期,则默认存储在浏览器内存中,浏览器关闭即失效
2)如果不设置有效路径则(Cookie的默认path为当前项目的根路径
3)如果设置了有效期,则存储到客户端的硬盘里,到期后自动销毁
4)如果设置了有效路径则只有在该路径下的请求才会附带设置的cookie信息
注意:
c.setPath(“/bjservlet/mm”);
表示/bjservlet/mm以及/bjservlet/mm/*
c.setPath(“/bjservlet/mm/“);
表示/bjservlet/mm/*
Session
问题:
Request 对象解决了一次请求内的不同 Servlet 的数据共享问 题,那么一个用户的不同请求的处理需要使 用相同的数据怎么办呢?
解决:
使用 session 技术。
原理:
用户使用浏览器第一次向服务器发送请求,服务器在接受到请 求后,调用对应的 Servlet 进行处理。在处理过程中会给用户创建 一个 session 对象(调用req.getsession()才会创建),用来存储用户请求处理相关的公共数据,并将 此 session 对象JSESSIONID 以 Cookie 的形式存储在浏览器中(临 时存储,浏览器关闭即失效)。用户在发起第二次请求及后续请求 时,请求信息中会附带 JSESSIONID,服务器在接收到请求后,调用对应的 Servlet 进行请求处理,同时根据 JSESSIONID 返回其对应的 session 对象。
特点:Session 技术是依赖 Cookie 技术的服务器端的数据存储技术。由服务器进行创建 每个用户独立拥有一个 session
默认存储时间为 30 分钟
作用:
解决了一个用户的不同请求的数据共享问题。
使用:
创建 Session 对象
存储数据到 session 对象
获取 session 对象
获取数据从 session 对象
如果获取 session 中不存在的数据返回 null。
注意:
只要不关闭浏览器,并且 session 不失效的情况下,同一个用 户的任意请求在项目的任意Servlet中获取到 的都是同一个session对象。
作用域:
一次会话
注意: 一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <%@page session=”false”%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。
由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。
ServletContext
问题:
Request 解决了一次请求内的数据共享问题,session 解决了 用户不同请求的数据共享问题,那么不同的用户的数据共享该怎 么办呢?
解决:
使用 ServletContext 对象
作用:
解决了不同用户的数据共享问题
原理:
ServletContext 对象由服务器进行创建,一个项目只有一个对 象。不管在项目的任意位置进行获取得到的都是同一个对象,那 么不同用户发起的请求获取到的也就是同一个对象了,该对象由 用户共同拥有。
特点:
服务器进行创建用户共享
一个项目只有一个
生命周期:
服务器启动到服务器关闭
作用域:
项目内
使用:
获取 ServletContext 对象
使用作用域进行共享数据流转
获取 web.xml 中的全局配置
获取 webroot 下项目资源流对象
获取 webroot 下资源绝对路径
ServletConfig
问题:
使用 ServletContext 对象可以获取 web.xml 中的全局配置文件, 在 web.xml 中 每个 Servlet 也可以进行单独的配置,那么该怎么获取配置信息呢?
解决:
使用 ServletConfig 对象作用:
ServletConfig 对象是 Servlet 的专属配置对象,每个 Servlet 都单独拥有一个 ServletConfig 对象,用来获取 web.xml 中的配置信息。
使用:
获取 ServletConfig 对象
获取 web.xml 中 servlet 的配置信息