引入
Tomcat HTTP协议 协议:Protocol
定义 HTTP协议是Hyper Text Transfer Protocol(超文本传输协议) 的缩写, HTTP是万维网(WWW:World Wide Web)的数据通信的基础。
HTTP是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。
HTTP是一个基于TCP/IP 通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。
特点 1支持客户/服务器模式
HTTP协议支持客户端服务端模式,需要使用浏览器作为客户端来访问服务端。
2简单快速
客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、POST等。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3灵活
HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type(Content-Type是HTTP包中用来表示内容类型的标识)加以标记。
4无连接
每次请求一次,释放一次连接。所以无连接表示每次连接只能处理一个请求。优点就是节省传输时间,实现简单。我们有时称这种无连接为短连接。对应的就有了长链接,长连接专门解决效率问题。当建立好了一个连接之后,可以多次请求。但是缺点就是容易造成占用资源不释放的问题。当HTTP协议头部中字段Connection:keep-alive表示支持长链接。
5单向性
服务端永远是被动的等待客户端的请求。
6无状态
HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。为了解决HTTP协议无状态,于是,两种用于保持HTTP连接状态的技术就应运而生了,一个是Cookie,而另一个则是Session。
HTTP协议发展和版本
http协议在1991年发布第一个版本版本号为0.9。随后WWW联盟(WWW Consortium-W3C)于1994年成立,http协议被纳入到W3C组织中进行维护和管理。
http1.0
最早在1996年在网页中使用,内容简单,所以浏览器的每次请求都需要与服务器建立一个TCP连接,服务器处理完成后立即断开TCP连接(无连接),服务器不跟踪每个客户端也不记录过去的请求(无状态),请求只能由客户端发起(单向性)。
http1.1
到1999年广泛在各大浏览器网络请求中使用,HTTP/1.0中默认使用Connection: close。在HTTP/1.1中已经默认使用Connection: keep-alive(长连接),避免了连接建立和释放的开销,但服务器必须按照客户端请求的先后顺序依次回送相应的结果,以保证客户端能够区分出每次请求的响应内容。通过Content-Length字段来判断当前请求的数据是否已经全部接收。不允许同时存在两个并行的响应。
1.1中最重要的一个特点是支持“长连接”,即“一次连接可以多次请求”。
HTTP 1.1支持持久连接(HTTP/1.1的默认模式使用带流水线的持久连接),在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。一个包含有许多图像的网页文件的多个请求和应答可以在一个连接中传输,但每个单独的网页文件的请求和应答仍然需要使用各自的连接。HTTP 1.1还允许客户端不用等待上一次请求结果返回,就可以发出下一次请求,但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果,以保证客户端能够区分出每次请求的响应内容,这样也显著地减少了整个下载过程所需要的时间。
http2.0
长连接
在HTTP/2中,客户端向某个域名的服务器请求页面的过程中,只会创建一条TCP连接,即使这页面可能包含上百个资源。 单一的连接应该是HTTP2的主要优势,单一的连接能减少TCP握手带来的时延。HTTP2中用一条单一的长连接,避免了创建多个TCP连接带来的网络开销,提高了吞吐量。
多路复用 (Multiplexing)
HTTP2.0中所有加强性能的核心是二进制传输,在HTTP1.x中,我们是通过文本的方式传输数据。在HTTP2.0中引入了新的编码机制,所有传输的数据都会被分割,并采用二进制格式编码。
多路复用,连接共享。不同的request可以使用同一个连接传输(最后根据每个request上的id号组合成正常的请求)。
HTTP2.0中,有两个概念非常重要:帧(frame)和流(stream)。 帧是最小的数据单位,每个帧会标识出该帧属于哪个流,流是多个帧组成的数据流。 所谓多路复用,即在一个TCP连接中存在多个流,即可以同时发送多个请求,对端可以通过帧中的表示知道该帧属于哪个请求。在客户端,这些帧乱序发送,到对端后再根据每个帧首部的流标识符重新组装。通过该技术,可以避免HTTP旧版本的队头阻塞问题,极大提高传输性能。
首部压缩(Header Compression)
由于1.1中header带有大量的信息,并且得重复传输,2.0使用encoder来减少需要传输的hearder大小。
服务端推送(Server Push)
在HTTP2.0中,服务端可以在客户端某个请求后,主动推送其他资源。 可以想象一下,某些资源客户端是一定会请求的,这时就可以采取服务端push的技术,提前给客户端推送必要的资源,就可以相对减少一点延迟时间。在浏览器兼容的情况下也可以使用prefetch。
更安全
HTTP2.0使用了tls的拓展ALPN做为协议升级,除此之外,HTTP2.0对tls的安全性做了近一步加强,通过黑名单机制禁用了几百种不再安全的加密算法。
Servlet 1. 引入 介绍 Servlet是Server Applet的简称,称为服务端小程序,是JavaEE平台下的技术标准,基于Java语言编写的服务端程序。Web容器或应用服务器实现了Servlet标准所以Servlet需运行在Web容器或应用服务器中。Servlet主要功能在于能在服务器中执行并生成数据。
特点 使用单进程多线程方式运行
应用程序中的位置
静态资源和动态资源的区分
静态资源:每次访问都不需要运算,直接就可以返回的资源,如HTML CSS JS 多媒体文件等等 每次访问获得地资源都是一样的
动态资源:每次访问都需要运算代码生成的资源如 Servlet JSP 每次访问获得的结果可能都是不一样的
Servlet 作为一种动态资源技术 是后面学习框架的基础
Servlet在程序中的地位 Servlet是可以接受Http请求并作出相应的一种技术,是JAVA语言编写的一种动态资源 Servlet是前后端衔接的一种技术,不是所有的JAVA类都可以接收请求和作出相应,Servlet可以
在MVC模式中,Servlet作为Controller层(控制层)主要技术,用于和浏览器完成数据交互,控制交互逻辑
==servlet三大域对象== Servlet三大域对象的应用 request、session、application(ServletContext)
ServletContext是一个全局的储存信息的空间,服务器开始就存在,服务器关闭才释放。
request,一个用户可有多个;session,一个用户一个;而servletContext,所有用户共用一个。所以,为了节省空间,提高效率,ServletContext中,要放必须的、重要的、所有用户需要共享的线程又是安全的一些信息。
案例1:初步认识
案例2:登录页 需求: 准备一个登陆页,可以输入要用户名和密码
输入完毕向Servlet提交用户名和密码
Servlet接收到用户名和密码之后校验是否正确
如果正确响应success
如果不正确响应Failed
具体步骤:
项目结构:
开发登录页:
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form method ="get" action ="loginServlet.do" > <table style ="margin: 0px auto" width ="300px" cellpadding ="0px" cellspacing ="0px" border ="1px" > <tr > <td > 用户名</td > <td > <input type ="text" name ="username" > </td > </tr > <tr > <td > 密码</td > <td > <input type ="password" name ="pwd" > </td > </tr > <tr align ="center" > <td colspan ="2" > <input type ="submit" value ="登录" > </td > </tr > </table > </form > </body > </html >
开发后台Servlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class LoginServlet extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("login servlet invoked" ); String username = req.getParameter("username" ); String pwd = req.getParameter("pwd" ); String message=null ; if (username.equals("mashibing" )&& pwd.equals("123456" )){ message="Success" ; }else { message="Fail" ; } resp.getWriter().write(message); }
配置Servlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <servlet > <servlet-name > loginServlet</servlet-name > <servlet-class > com.mashibing.servlet.LoginServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > loginServlet</servlet-name > <url-pattern > /loginServlet.do</url-pattern > </servlet-mapping > <welcome-file-list > <welcome-file > login.html</welcome-file > </welcome-file-list >
运行测试
2. HttpServletRequest 一个http可以分为三个部分:请求行 请求头 请求体
请求行
请求头
请求体 get方式提交的请求数据通过地址栏提交 ,没有请求体 post方式提交请求数据单独放到请求体中,请求时所携带的数据 (post方式)
http支持的请求方式
==get和post的区别== ==(面试)==
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST则没有。对参数的数据类型GET只接受ASCII字符,而POST即可是字符也可是字节。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
具体操作:获得客户端请求信息 HttpServletRequest对象代表客户端浏览器的请求,当客户端浏览器通过HTTP协议访问服务器时,HTTP请求中的所有信息都会被Tomcat所解析并封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。
1.获取请求行信息 1 2 3 4 5 6 7 8 9 req.getRequestURL(): req.getRequestURI(): req.getRemoteAddr(): req.getLocalAddr(): req.getLocalPort():
2.获取请求头信息 1 2 3 4 req.getHeader("headerKey" ): String headerValue = req.getHeader("headerKey" );req.getHeaderNames(): Enumeration<String> headerNames = req.getHeaderNames();
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Servlet3 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println(req.getRequestURL()); System.out.println(req.getRequestURI()); System.out.println(req.getRemoteAddr()); System.out.println(req.getLocalAddr()); System.out.println(req.getLocalPort()); System.out.println("主机名: " + req.getLocalName()); System.out.println("客户端PORT: " + req.getRemotePort()); System.out.println("当前项目部署名: " + req.getContextPath()); System.out.println("协议名:" +req.getScheme()); System.out.println("请求方式:" +req.getMethod()); System.out.println(req.getHeader("Accept" )); Enumeration<String> headerNames = req.getHeaderNames(); while (headerNames.hasMoreElements()){ String headername = headerNames.nextElement(); System.out.println(headername+":" +req.getHeader(headername)); } } }
3.获取请求数据
案例3:HTTP请求 需求 :获得前端客户端表单中请求的数据信息
前端代码
form 不是from
form表单内部不是所有的标签信息都会提交 一些输入信息 input select textarea … …
提交的标签必须具备name属性 name属性的作用是让后台区分数据 id便于在前端区分数据
提交的标签一般都要具备value属性 value属性确定我们要提交的具体的数据
==get post==区别 get方式数据是通过URL携带 提交的数据只能是文本 提交的数据量不大 get方式提交的数据相对不安全
post 将数据单独打包放到请求体中 提交的数据可以是文本可以是各种文件 提交的数据量理论上没有上限 post方式提交数据相对安全
==readonly只读== 也是会提交数据的 ==hidden== 隐藏 也是会提交数据 ==disabled== 不可用 显示但是不提交
代码:
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 <form method="get" action="myServlet" > <table style="margin: 0px auto" width="300px" cellpadding="0px" cellspacing="0px" border="1px" > <tr> <td>用户名</td> <td> <input type="text" name="username" id="in1" value="12345" disabled > </td> </tr> <tr> <td>密码</td> <td> <input type="password" name="pwd" > </td> </tr> <tr> <td>性别</td> <td> <input type="radio" name="gender" value="1" checked>男 <input type="radio" name="gender" value="0" >女 </td> </tr> <tr> <td>爱好</td> <td> <input type="checkbox" name="hobby" value="1" >蓝球 <input type="checkbox" name="hobby" value="2" >足球 <input type="checkbox" name="hobby" value="3" >羽毛球 <input type="checkbox" name="hobby" value="4" >乒乓球 </td> </tr> <tr> <td>个人简介</td> <td> <!--文本域 双标签 页面上显示的文字是双标签中的文本 不是value属性 文本域提交的数据不是value属性值,是双标签中的文本 --> <textarea name="introduce" >b</textarea> </td> </tr> <tr> <td>籍贯</td> <td> <!-- select option没有定义value属性 那么就提交option中间的文字(不推荐) --> <select name="provience" > <option value="1" >a京</option> <option value="2" >b津</option> <option value="3" >c冀</option> </select> </td> </tr> <tr align="center" > <td colspan="2" > <input type="submit" value="提交数据" > </td> </tr> </table> </form>
效果:
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 32 33 34 35 36 37 38 39 40 public class MyServlet extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); System.out.println("username:" +username); System.out.println("password:" +req.getParameter("pwd" )); System.out.println("gender:" +req.getParameter("gender" )); String[] hobbies = req.getParameterValues("hobby" ); System.out.println("hobbies:" + Arrays.toString(hobbies)); System.out.println("introduce:" +req.getParameter("introduce" )); System.out.println("provience:" +req.getParameter("provience" )); System.out.println("___________________________" ); Enumeration<String> pNames = req.getParameterNames(); while (pNames.hasMoreElements()){ String pname = pNames.nextElement(); String[] pValues = req.getParameterValues(pname); System.out.println(pname+":" +Arrays.toString(pValues)); } System.out.println("________________________________" ); Map<String, String[]> pmap = req.getParameterMap(); Set<Map.Entry<String, String[]>> entries = pmap.entrySet(); for (Map.Entry<String, String[]> entry : entries) { System.out.println(entry.getKey()+":" +Arrays.toString(entry.getValue())); } } }
运行结果:
3. HttpServletResponse http响应部分可以分为三部分:响应行,响应头,响应体
响应行
响应状态码列表
响应头 Content-Type:响应内容的类型(MIME)
响应实体 即服务器响应回来的内容
HttpServletResponse对象代表服务器的响应,封装了响应客户端浏览器的流对象 ,以及向客户端浏览器响应的响应头、响应数据、响应状态码等信息 。
ContentType:响应设置 1 resp.setContentType("MIME" ):
MIME 全称是Multipurpose Internet Mail Extensions ,即多用途互联网邮件扩展类型。 这是HTTP协议中用来定义文档性质及格式的标准。对HTTP传输内容类型进行了全面定义。 服务器通过MIME告知响应内容类型,而浏览器则通过MIME类型来确定如何处理文档。
HTTP content-type 类型表:
https://www.runoob.com/http/http-content-type.html
常见的媒体格式类型如下:
text/html : HTML格式
text/plain :纯文本格式
text/xml : XML格式
image/gif :gif图片格式
image/jpeg :jpg图片格式
image/png:png图片格式
以application开头的媒体格式类型:
application/xhtml+xml :XHTML格式
application/xml: XML数据格式
application/atom+xml :Atom XML聚合格式
application/json: JSON数据格式
application/pdf:pdf格式
application/msword : Word文档格式
application/octet-stream : 二进制流数据(如常见的文件下载)
application/x-www-form-urlencoded :
另外一种常见的媒体格式是上传文件之时使用的:
multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
常见的字节型响应:
image/jpeg:图片类型为jpeg或jpg格式。
设置响应编码
1 2 3 response.setCharacterEncoding("utf-8" ); response.setContentType("text/html;charset=utf-8" );
设置服务端为浏览器产生响应的响应编码,服务端会根据此编码将响应内容的字符转换为字节。同时客户端浏览器会根据此编码方式显示响应内容。
在响应中添加附加信息(文件下载)
在实现文件下载时,我们需要修改响应头,添加附加信息。
1 response.setHeader("Content-Disposition" , "attachment; filename=" +文件名);
==Content-Disposition:attachment==
该附加信息表示作为对下载文件的一个标识字段。不会在浏览器中显示而是直接做下载处理。
filename=文件名,表示指定下载文件的文件名。
解决文件名中文乱码问题:
1 resp.addHeader("Content-Disposition" ,"attachment;filename=" +new String (file.getName().getBytes("gbk" ),"iso-8859-1" ));
测试代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MyServlet2 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html" ); resp.getWriter().write("<h1>this is tag h1</h1>" ); } }
乱码问题 1 控制台乱码 设置Tomcat中 conf下logging.properties中所有的UTF-8编码为GBK即可
2 post请求乱码 通过HttpServletRequest设置请求编码
1 req.setCharacterEncoding("UTF-8" );
3 get请求乱码 需要手动进行编码解码,或者设置tomcat中的server.xml中的URI编码.tomcat9已经解决了该问题
1 2 3 <Connector port ="8080" protocol ="HTTP/1.1" connectionTimeout ="20000" redirectPort ="8443" URIEncoding ="utf-8" />
4 响应乱码 通过HttpServletResponse设置响应编码
1 2 3 4 resp.setContentType("UTF-8" ); resp.setContentType("text/html;charset=UTF-8" );
4. servlet生命周期 四个阶段 Servlet的生命周期是由容器管理的,分别经历四个阶段:
阶段
次数
时机
创建 new
1次
第一次请求
初始化 init()
1次
实例化之后
执行服务 service()
多次
每次请求
销毁 destroy()
1次
停止服务
当客户端浏览器第一次请求Servlet时,容器会实例化这个Servlet,然后调用一次init方法,并在新的线程中执行service方法处理请求。service方法执行完毕后容器不会销毁这个Servlet而是做缓存处理,当客户端浏览器再次请求这个Servlet时,容器会从缓存中直接找到这个Servlet对象,并再一次在新的线程中执行Service方法。当容器在销毁Servlet之前对调用一次destory方法。
在Servlet中我们一般不要轻易使用成员变量!!!! 可能会造成线程安全问题
如果要使用的话,应该尽量避免对成员变量产生修改
如果要产生修改我们应该注意线程安全问题
如果我们自己添加线程安全编码处理,会严重影响效率
综上所述:原则,能不用成员变量就不用!!!
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 public class MyServlet4 extends HttpServlet { public MyServlet4 () { System.out.println("MyServlet4 Constructor invoked" ); try { Thread.sleep(5000 ); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void init () throws ServletException { System.out.println("MyServlet4 init invoked" ); try { Thread.sleep(5000 ); } catch (InterruptedException e) { e.printStackTrace(); } } @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet4 service invoked" ); } @Override public void destroy () { System.out.println("MyServlet4 destory invoked" ); } }
配置:
1 2 3 4 5 6 7 8 9 <servlet > <servlet-name > myServlet4</servlet-name > <servlet-class > com.mashibing.servlet.MyServlet4</servlet-class > <load-on-startup > 6</load-on-startup > </servlet > <servlet-mapping > <servlet-name > myServlet4</servlet-name > <url-pattern > /myServlet4.do</url-pattern > </servlet-mapping >
多次请求servlet并查看控制台输出即可印证上述结论,值得注意的是,如果需要Servlet在服务启动时就实例化并初始化,我们可以在servlet的配置中添加load-on-startup配置启动顺序,配置的数字为启动顺序,应避免冲突且应**>6**
Servlet处理请求的过程
当浏览器基于get方式请求我们创建Servlet时,我们自定义的Servlet中的doGet方法会被执行。doGet方法能够被执行并处理get请求的原因是,容器在启动时会解析web工程中WEB-INF目录中的web.xml文件,在该文件中我们配置了Servlet与URI的绑定,容器通过对请求的解析可以获取请求资源的URI,然后找到与该URI绑定的Servlet并做实例化处理(注意:只实例化一次,如果在缓存中能够找到这个Servlet就不会再做次实例化处理)。在实例化时会使用Servlet接口类型作为引用类型的定义,并调用一次init方法,由于HttpServlet中重写了该方法所以最终执行的是HttpServlet中init方法(HttpServlet中的Init方法是一个空的方法体),然后在新的线程中调用service方法。由于在HttpServlet中重写了Service方法所以最终执行的是HttpServlet中的service方法。在service方法中通过request.getMethod()获取到请求方式进行判断如果是Get方式请求就执行doGet方法,如果是POST请求就执行doPost方法。如果是基于GET方式提交的,并且在我们自定义的Servlet中又重写了HttpServlet中的doGet方法,那么最终会根据Java的多态特性转而执行我们自定义的Servlet中的doGet方法。
老程序员喜欢重写doGet()和doPost()方法 然后挑一个方法里面直接调用另一个方法,因为两个方法很相似。
但是又可以直接重写service,可以同时处理get和post
5. ServletContext和ServletConfig ServletContext(application) 定义 ServletContext官方叫Servlet上下文。服务器会为每一个Web应用创建一个ServletContext对象。这个对象全局唯一,而且Web应用中的所有Servlet都共享这个对象。所以叫全局应用程序共享对象
作用
相对路径转绝对路径
获取容器的附加信息
读取配置信息
全局容器
操作
获取项目的部署名 ==context.getContextPath()==
相对路径转绝对路径(文件上传下载时需要注意)
==context.getRealPath(“path”)==
该方法可以将一个相对路径转换为绝对路径,在文件上传与下载时需要用到该方法做路径的转换。
获取容器的附加信息
==servletContext.getServerInfo()==
返回Servlet容器的名称和版本号
返回Servlet容器所支持Servlet的主版本号
==servletContext.getMajorVersion()==
返回Servlet容器所支持Servlet的副版本号
==servletContext.getMinorVersion()==
获取web.xml文件中的信息
1 2 3 4 <context-param > <param-name > key</param-name > <param-value > value</param-value > </context-param >
读取web.xml文件中标签中的配置信息
==servletContext.getInitParameter(“key”)==
读取web.xml文件中所有param-name标签中的值。
==servletContext.getInitParameterNames()==
全局容器
向全局容器中存放数据。
==servletContext.setAttribute(“key”,ObjectValue)==
从全局容器中获取数据。
==servletContext.getAttribute(“key”)==
根据key删除全局容器中的value。
==servletContext.removeAttribute(“key”)==
测试代码: xml配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <context-param > <param-name > username</param-name > <param-value > mashibing</param-value > </context-param > <context-param > <param-name > password</param-name > <param-value > 123456</param-value > </context-param > <servlet > <servlet-name > servlet1</servlet-name > <servlet-class > com.msb.testServlet.Servlet1</servlet-class > </servlet > <servlet-mapping > <servlet-name > servlet1</servlet-name > <url-pattern > /servlet1.do</url-pattern > </servlet-mapping >
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 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 public class Servlet1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext servletContext1 = req.getServletContext(); ServletContext servletContext2 = this .getServletContext(); System.out.println(servletContext1==servletContext2); String contextPath = servletContext1.getContextPath(); System.out.println("contextPath" +contextPath); String fileUpload = servletContext1.getRealPath("fileUpload" ); System.out.println(fileUpload); String serverInfo = servletContext1.getServerInfo(); System.out.println(serverInfo); int majorVersion = servletContext1.getMajorVersion(); int minorVersion = servletContext1.getMinorVersion(); System.out.println("majorVersion" +majorVersion); System.out.println("minorVersion" +minorVersion); String username = servletContext1.getInitParameter("username" ); String password = servletContext1.getInitParameter("password" ); System.out.println("username" +username); System.out.println("password" +password); Enumeration<String> pNames = servletContext1.getInitParameterNames(); while (pNames.hasMoreElements()){ String e = pNames.nextElement(); System.out.println(e+":" + servletContext1.getInitParameter(e)); } List<String> data = new ArrayList <>(); Collections.addAll(data,"张三" ,"李四" ,"王武" ); servletContext1.setAttribute("list" ,data); servletContext1.setAttribute("gender" ,"boy" ); List<String> list = (List<String>) servletContext1.getAttribute("list" ); System.out.println(list); String gender = (String) servletContext1.getAttribute("gender" ); System.out.println(gender); } }
测试结果
生命周期 当容器启动是会创建ServletContext对象并一直缓存该对象,知道容器关闭后该对象生命周期结束。ServletContext对象的生命周期非常长,所以在使用全局容器时不建议存放业务数据
ServletConfig 定义 ServletConfig对象对应web.xml文件中的节点。当Tomcat初始化一个Servlet时,会将该Servlet的配置信息,封装到一个ServletConfig对象中。
操作 我们通过Config对象读取servlet节点中的配置信息
1 2 3 4 5 6 7 8 <servlet > <servlet-name > servletName</servlet-name > <servlet-class > servletClass</servlet-class > <init-param > <param-name > key</param-name > <param-value > value</param-value > </init-param > </servlet >
读取web.xml文件中标签中标签中的配置信息。
==servletConfig.getInitParameter(“key”);==
读取web.xml文件中当前标签中所有标签中的值。
==servletConfig.getInitParameterNames();==
测试代码 xml配置
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 <servlet > <servlet-name > servlet3</servlet-name > <servlet-class > com.msb.testServlet.Servlet3</servlet-class > <init-param > <param-name > brand</param-name > <param-value > ASUS</param-value > </init-param > <init-param > <param-name > screen</param-name > <param-value > 三星</param-value > </init-param > </servlet > <servlet > <servlet-name > servlet4</servlet-name > <servlet-class > com.msb.testServlet.Servlet4</servlet-class > <init-param > <param-name > pinpai</param-name > <param-value > 联想</param-value > </init-param > <init-param > <param-name > pingmu</param-name > <param-value > 京东方</param-value > </init-param > </servlet > <servlet-mapping > <servlet-name > servlet3</servlet-name > <url-pattern > /servlet3.do</url-pattern > </servlet-mapping > <servlet-mapping > <servlet-name > servlet4</servlet-name > <url-pattern > /servlet4.do</url-pattern > </servlet-mapping >
servlet
1 2 3 4 5 6 7 8 public class Servlet3 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletConfig servletConfig = this .getServletConfig(); System.out.println(servletConfig.getInitParameter("brand" )); System.out.println(servletConfig.getInitParameter("screen" )); } }
1 2 3 4 5 6 7 8 9 public class Servlet4 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletConfig servletConfig = this .getServletConfig(); System.out.println(servletConfig.getInitParameter("pinpai" )); System.out.println(servletConfig.getInitParameter("pingmu" )); } }
6. URL的匹配规则 精确匹配 精确匹配是指中配置的值必须与url完全精确匹配。
1 2 3 4 <servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > /demo.do</url-pattern > </servlet-mapping >
http://localhost:8888/demo/demo.do 匹配
http://localhost:8888/demo/suibian/demo.do 不匹配
扩展名匹配 在允许使用统配符“”作为匹配规则,“ ”表示匹配任意字符。在扩展名匹配中只要扩展名相同都会被匹配和路径无关。注意,在使用扩展名匹配时在中不能使用“/”,否则容器启动就会抛出异常。
1 2 3 4 <servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > *.do</url-pattern > </servlet-mapping >
http://localhost:8888/demo/abc.do 匹配
http://localhost:8888/demo/suibian/haha.do 匹配
http://localhost:8888/demo/abc 不匹配
路径匹配 根据请求路径进行匹配,在请求中只要包含该路径都匹配,“*”表示任意路径以及子路径
1 2 3 4 <servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > /suibian/*</url-pattern > </servlet-mapping >
http://localhost:8888/demo/suibian/haha.do 匹配
http://localhost:8888/demo/suibian/hehe/haha.do 匹配
http://localhost:8888/demo/hehe/heihei.do 不匹配
任意匹配 匹配“/ 匹配所有但不包括JSP页面
1 <url-pattern > /</url-pattern >
http://localhost:8888/demo/suibian.do匹配
http://localhost:8888/demo/addUser.html匹配
http://localhost:8888/demo/css/view.css匹配
http://localhost:8888/demo/addUser.jsp不匹配
http://localhost:8888/demo/user/addUser.jsp不匹配
匹配所有 1 <url-pattern > /*</url-pattern >
http://localhost:8888/demo/suibian.do匹配
http://localhost:8888/demo/addUser.html匹配
http://localhost:8888/demo/suibian/suibian.do匹配
优先顺序 当一个url与多个Servlet的匹配规则可以匹配时,则按照 “ 精确路径 > 最长路径 >扩展名”这样的优先级匹配到对应的Servlet。
案例分析 Servlet1映射到 /abc/*
Servlet2映射到 /*
Servlet3映射到 /abc
Servlet4映射到 *.do
当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,Servlet引擎将调用Servlet1。
当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,Servlet引擎将调用Servlet3。
当请求URL为“/abc/a.do”时,“/abc/”和“ .do”都匹配,Servlet引擎将调用Servlet1。
当请求URL为“/a.do”时,“/”和“ .do”都匹配,Servlet引擎将调用Servlet2。
当请求URL为“/xxx/yyy/a.do”时,“/”和“ .do”都匹配,Servlet引擎将调用Servlet2。
URL映射方式 在web.xml文件中支持将多个URL映射到一个Servlet中,但是相同的URL不能同时映射到两个Servlet中。
方式一 1 2 3 4 5 <servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > /suibian/*</url-pattern > <url-pattern > *.do</url-pattern > </servlet-mapping >
方式二 1 2 3 4 5 6 7 8 <servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > /suibian/*</url-pattern > </servlet-mapping > <servlet-mapping > <servlet-name > demoServlet</servlet-name > <url-pattern > *.do</url-pattern > </servlet-mapping >
7. 注解开发 基于注解式开发Servlet
在Servlet3.0以及之后的版本中支持注解式开发Servlet。对于Servlet的配置不在依赖于web.xml配置文件而是使用@WebServlet将一个继承于javax.servlet.http.HttpServlet的类定义为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 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebServlet { String name () default "" ; String[] value() default {}; String[] urlPatterns() default {}; int loadOnStartup () default -1 ; WebInitParam[] initParams() default {}; boolean asyncSupported () default false ; String smallIcon () default "" ; String largeIcon () default "" ; String description () default "" ; String displayName () default "" ; }
相关属性
8. forward 请求转发 forward处理流程
清空Response存放响应正文数据的缓冲区
如果目标资源为Servlet或JSP,就调用它们的service()方法,把该方法产生的响应结果发送到客户端;如果目标资源文件系统中的静态HTML,就读取文档中的数据把它发送到客户端。
请求转发是一种服务器的行为 ,是对浏览器屏蔽
浏览器的地址栏不会发生变化
请求的参数是可以从源组件传递到目标组件的
请求对象和响应对象没有重新创建,而是传递给了目标组件
请求转发可以帮助我们完成页面的跳转
请求转发可以转发至WEB-INF里
请求转发只能转发给当前项目的内部资源,不能转发至外部资源
常用forward
forword处理特点
由于forword()方法先清空用于存放响应正文的缓冲区,因此源Servlet生成的响应结果不会被发送到客户端,只有目标资源生成的响应结果才会被发送到客户端。
如果源Servlet在进行请求转发之前,已经提交了响应结(flushBuffer(),close()方法),那么forward()方法抛出IllegalStateException。为了避免该异常,不应该在源Servlet中提交响应结果。
注意
在forward转发模式下,请求应该完全交给目标资源去处理,我们在源组件中,不要作出任何的响应处理
在forward方法调用之后,也不要在使用req和resp对象做其他操作了
测试代码 servlet1 请求转发至 servlet2
servlet1
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 @WebServlet(urlPatterns = "/servlet1.do") public class Servlet1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servlet1 service invoked" ); String money = req.getParameter("money" ); System.out.println("money:" +money); RequestDispatcher requestDispatcher = req.getRequestDispatcher("https://www.baidu.com" ); requestDispatcher.forward(req,resp); } }
Servlet2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @WebServlet(urlPatterns = "/servlet2.do") public class Servlet2 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servlet2 service invoked" ); String money = req.getParameter("money" ); System.out.println("money:" +money); resp.setCharacterEncoding("UTF-8" ); resp.setContentType("text/html;charset=UTF-8" ); PrintWriter writer = resp.getWriter(); writer.println("支付宝到账:" +money+"元" ); } }
include(了解) 在include转发模式下,设置响应可以在转发之前,也可以在转发之后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 resp.setCharacterEncoding("UTF-8" ); resp.setContentType("text/html;charset=UTF-8" ); resp.getWriter().println("servlet1在转发之前增加的响应内容" ); RequestDispatcher requestDispatcher = req.getRequestDispatcher("servlet2.do" ); requestDispatcher.include(req,resp); resp.getWriter().println("servlet1在转发之后增加的响应内容" );
但是不常用的
9. sendRedirect 响应重定向 响应重定向是通过HttpServletResponse对象sendRedirect(“路径”)的方式。实现是——服务器通知浏览器,让浏览器去自主请求其他资源的一种方式
运作流程:
用户在浏览器端输入特定URL,请求访问服务器端的某个Servlet。
服务器端的Servlet返回一个状态码为302的响应结果,该响应结果的含义为:让浏览器端再请求访问另一个Web资源,在响应结果中提供了另一个Web资源的URL。另一个Web资源有可能在同一个Web服务器上,也有可能不再同一个Web服务器上。
当浏览器端接收到这种响应结果后,再立即自动请求访问另一个Web资源。
浏览器端接收到另一个Web资源的响应结果。
测试代码: servlet3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @WebServlet(urlPatterns = "/servlet3.do") public class Servlet3 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servlet3 service invoked" ); String money = req.getParameter("money" ); System.out.println("money:" +money); resp.sendRedirect("servlet4.do?money=" +money); } }
servlet4
1 2 3 4 5 6 7 8 9 @WebServlet(urlPatterns = "/servlet4.do") public class Servlet4 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servlet4 service invoked" ); String money = req.getParameter("money" ); System.out.println("money:" +money); } }
注意
重定向是服务器给浏览器重新指定请求方向 是一种浏览器行为 地址栏会发生变化
重定向时,请求对象和响应对象都会再次产生,请求中的参数是不会携带
重定向也可以帮助我们完成页面跳转
重定向不能帮助我们访问WEB-INF中的资源
重定向可以定向到外部资源
10. 路径问题 前端路径 项目结构
测试代码
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 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <base href ="http://127.0.0.1:8080/testServlet4_war_exploded/" > </head > <body > this is page a1 <br /> <a href ="a2.html" TARGET ="_self" > 相对路径跳转至A2</a > <a href ="../../b/b2/b1.html" TARGET ="_self" > 相对路径跳转至b1</a > <br /> <a href ="a/a2/a2.html" TARGET ="_self" > base相对路径跳转至A2</a > <a href ="b/b2/b1.html" TARGET ="_self" > base相对路径跳转至b1</a > <br /> <a href ="/testServlet4_war_exploded/a/a2/a2.html" TARGET ="_self" > 绝对路径跳转至A2</a > <a href ="/testServlet4_war_exploded/b/b2/b1.html" TARGET ="_self" > 绝对路径跳转至b1</a > </body > </html >
总结
以/开头的路径是绝对路径,不以/开头是相对路径
绝对路径/后面要写当前服务的上下文路径名
==../==代表向上一层的路径
==base标签==可以简化相对路径,当使用相对路径时,默认会在相对路径之前补充 base中的内容;如果没有定义base 默认就是当前文件所在的路径
11. 会话管理 认识Cookie和Session Cookie对象与HttpSession对象的作用是维护客户端浏览器与服务端的会话状态的两个对象。由于HTTP协议是一个无状态的协议,所以服务端并不会记录当前客户端浏览器的访问状态,但是在有些时候我们是需要服务端能够记录客户端浏览器的访问状态的,如获取当前客户端浏览器的访问服务端的次数时就需要会话状态的维持。在Servlet中提供了Cookie对象与HttpSession对象用于维护客户端与服务端的会话状态的维持。二者不同的是Cookie是通过客户端浏览器实现会话的维持,而HttpSession是通过服务端来实现会话状态的维持。
Cookie Cookie是一种保存少量信息至浏览器的一种技术,第一请求时,服务器可以响应给浏览器一些Cookie信息,第二次请求,浏览器会携带之前的cookie发送给服务器,通过这种机制可以实现在浏览器端保留一些用户信息.为服务端获取用户状态获得依据
特点
Cookie使用字符串存储数据
Cookie使用Key与Value结构存储数据
单个Cookie存储数据大小限制在4097个字节
Cookie存储的数据中不支持中文,Servlet4.0中支持
Cookie是与域名绑定所以不支持跨一级域名访问
Cookie对象保存在客户端浏览器内存上或系统磁盘中
Cookie分为持久化Cookie(保存在磁盘上)与状态Cookie(保存在内存上)
浏览器在保存同一域名所返回Cookie的数量是有限的。不同浏览器支持的数量不同,Chrome浏览器为50个
浏览器每次请求时都会把与当前访问的域名相关的Cookie在请求中提交到服务端。
创建对象和响应 1 2 3 4 Cookie cookie = new Cookie ("key" ,"value" )response.addCookie(cookie)
数据的获取 1 2 Cookie[] cookies = request.getCookies()
Cookie持久化和状态Cookie
==状态Cookie==:浏览器会缓存Cookie对象。浏览器关闭后Cookie对象销毁。
==持久化Cookie==:浏览器会对Cookie做持久化处理,基于文件形式保存在系统的指定目录中。在Windows10系统中为了安全问题不会显示Cookie中的内容。
当Cookie对象创建后默认为状态Cookie 。可以使用Cookie对象下的==cookie.setMaxAge(60)==方法设置失效时间,单位为秒。一旦设置了失效时间,那么该Cookie为持久化Cookie,浏览器会将Cookie对象持久化到磁盘中。当失效时间到达后文件删除。
测试代码 通过响应对象 向浏览器响应cookie
1 2 3 4 5 6 7 8 9 10 11 12 @WebServlet(urlPatterns = "/servlet1.do") public class Servlet1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie c1=new Cookie ("age" ,"10" ); Cookie c2=new Cookie ("gender" , "男" ); resp.addCookie(c1); resp.addCookie(c2); } }
获取请求中cookie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @WebServlet(urlPatterns = "/servlet2.do") public class Servlet2 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies(); if (null != cookies){ for (Cookie cookie : cookies) { System.out.println(cookie.getName()+"=" +cookie.getValue()); } } } }
案例:通过Cookie判断用户是否访问过当前Servlet 需求:
当客户端浏览器第一次访问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 @WebServlet(urlPatterns = "/servlet3.do") public class Servlet3 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies(); boolean flag = false ; if (null !=cookies){ for (Cookie cookie : cookies) { String cookieName = cookie.getName(); if (cookieName.equals("servlet3" )){ Integer value = Integer.parseInt(cookie.getValue())+1 ; Cookie c=new Cookie ("servlet3" , String.valueOf(value)); resp.addCookie(c); System.out.println("欢迎您第" +value+"访问" ); flag=true ; } } } if (!flag){ System.out.println("欢迎您第一次访问" ); Cookie c=new Cookie ("servlet3" , "1" ); resp.addCookie(c); } } }
Session 案例:判断用户是否登录 需求: 实现登录一次即可,在一次会话内,可以反复多次访问WEB-INF/ welcome.html,如果没有登录过,跳转到登录页,登录成功后,可以访问
项目结构:
组件介绍: login.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form method ="get" action ="loginServlet.do" > 用户名:<input type ="text" name ="username" > <br /> 密码:<input type ="password" name ="password" > <br /> <input type ="submit" > </form > </body > </html >
main.html 1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > this is main page </body > </html >
LoginServlet 用来校验登录的,登陆成功将用户信息存户HttpSession,否则返回到登录页。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @WebServlet(urlPatterns = "/loginServlet.do") public class LoginServlet extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); if ("msb" .equals(username) && "1234" .equals(password)){ User user = new User (null , null , "msb" , "1234" ); HttpSession session = req.getSession(); session.setAttribute("user" , user); resp.sendRedirect(req.getContextPath()+"/mainServlet.do" ); }else { resp.sendRedirect(req.getContextPath()+"/login.html" ); } } }
MainServlet 用来向mian.html中跳转的,同时验证登录的,可以直接跳转,否则回到登录页。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @WebServlet(urlPatterns = "/mainServlet.do") public class MainServlet extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); User user = (User)session.getAttribute("user" ); if (null != user){ req.getRequestDispatcher("/WEB-INF/main.html" ).forward(req,resp); }else { resp.sendRedirect("login.html" ); } } }
User 存储用户信息的实体类
1 2 3 4 5 public class User implements Serializable { private Integer uid; private String realname; private String username; private String pasword;
JSP 指令标签 三种指令标签
指令
描述
<%@ page %>
定义网页依赖属性,如脚本语言,error页面、缓存需求等
<%@ include %>
包含其他文件
<%@ taglib%
引入标签库的定义
page标签 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <%--告知浏览器以什么格式和编码解析 响应的数据--%> <%@ page contentType="text/html;charset=UTF-8" %> <%--设置JSP页面转换的语言--%> <%@ page language="java" %> <%--导包--%> <%@ page import ="com.msb.entity.User" %> <%--在转换成java代码时使用的编码 一般不用设置--%> <%@ page pageEncoding="UTF-8" %> <%--指定错误页 当页面发生错误时 指定跳转的页面--%> <%@ page errorPage="error500.JSP" %> <%--指定当前页为异常提示页 当前页面可以接收异常对象 --%> <%@page isErrorPage="true" %>
errorPage是一种处理错误提示也的功能除了JSP有的错误提示页功能
javaEE中自带其他的错位提示页处理功能,具体配置如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <error-page > <error-code > 500</error-code > <location > /error500.JSP</location > </error-page > <error-page > <error-code > 404</error-code > <location > /error404.JSP</location > </error-page >
当JSP中发生了异常时,如果JSP中配置的错误页和web.xml 中配置的错误页冲突了,JSP page指令的 errorPage优先级更高
include标签 JSP可以通过Include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。除了include指令标签可以实现引入以外,使用jsp:include也可以实现引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <%--静态引入使用的是 include 指令标签 被引入的JSP页面不会生成java代码 被引入的网页和当前页生成代码后形成了一个java文件--%> <%@include file="head.JSP" %> <%--动态引入 JSP标签中的 include选项 被引入的JSP页面会生成独立的java代码 在生成的java代码中 使用JSPRuntimeLibrary.include(request, response, "head.JSP" , out, false );引入其他页面 --%> <jsp:include page="head.JSP" />
查看转译以后的java源代码文件中的区别
静态引入:@include被引入的网页和当前页生成代码后形成了一个java文件
动态引入:jsp:include被引入的JSP页面会生成独立的java代码
taglib指令标签 JSP API允许用户自定义标签,一个自定义标签库就是自定义标签的集合。Taglib指令引入一个自定义标签集合的定义,包括库路径、自定义标签。
语法:
1 <%@ taglib uri="uri" prefix="prefixOfTag" %>
内置对象 九大对象 四大域对象
响应对象
输出流对象
其他三个对象
servletConfig:由于JSP本身也是一个Servlet,所以容器也会给我们准备一个ServletConfig
page 就是他this对象 当前JSP对象本身
exception 异常对象,在错误提示页上使用,当isErrorpage=true 才具有该对象
案例一:在浏览器上访问Emp表 动态地分等级 EmpDaoImpl.java
实现类
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 public class EmpDaoImpl implements EmpDao { private String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai" ; private String username="root" ; private String password="root" ; @Override public List<Emp> findAll () { List<Emp> list =new ArrayList <>(); Connection connection = null ; PreparedStatement pstat=null ; ResultSet resultSet=null ; try { Class.forName("com.mysql.cj.jdbc.Driver" ); connection = DriverManager.getConnection(url, username, password); pstat = connection.prepareStatement("select * from emp" ); resultSet = pstat.executeQuery(); while (resultSet.next()){ Integer empno=resultSet.getInt("empno" ); Integer deptno=resultSet.getInt("deptno" ); Integer mgr=resultSet.getInt("mgr" ); String ename=resultSet.getString("ename" ); String job=resultSet.getString("job" ); Double sal=resultSet.getDouble("sal" ); Double comm=resultSet.getDouble("comm" ); Date hiredate=resultSet.getDate("hiredate" ); Emp emp = new Emp ( empno, ename, job, mgr, hiredate, sal, comm, deptno); list.add(emp); } } catch (Exception e) { e.printStackTrace(); }finally { if (null !=resultSet){ try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (null !=pstat){ try { pstat.close(); } catch (SQLException e) { e.printStackTrace(); } } if (null !=connection){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } return list; } }
Emp.java
1 2 3 public interface EmpDao { List<Emp> findAll () ; }
EmpServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @WebServlet("/empServlet.do") public class EmpServlet extends HttpServlet { EmpDao empDao=new EmpDaoImpl (); @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<Emp> list = empDao.findAll(); req.setAttribute("emps" , list); req.getRequestDispatcher("showEmp.jsp" ).forward(req,resp); } }
showEmp.jsp
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 <%@ page import ="java.util.List" %> <%@ page import ="com.msb.pojo.Emp" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> <style> table{ border: 3px solid blue; width: 80 %; margin: 0px auto; } td,th{ border: 2px solid green; } </style> </head> <body> <table cellspacing="0px" cellpadding="0px" > <tr> <th>编号</th> <th>姓名</th> <th>上级编号</th> <th>职务</th> <th>入职日期</th> <th>薪资</th> <th>补助</th> <th>部门号</th> <th>薪资等级</th> </tr> <% List<Emp> emps = (List<Emp>) request.getAttribute("emps" ); for (Emp emp : emps) { %> <tr> <td><%=emp.getEmpno()%></td> <td><%=emp.getEname()%></td> <td><%=emp.getMgr()%></td> <td><%=emp.getJob()%></td> <td><%=emp.getHiredate()%></td> <td><%=emp.getSal()%></td> <td><%=emp.getComm()%></td> <td><%=emp.getDeptno()%></td> <td><%--out.print("<td>" )--%> <% Double sal = emp.getSal(); if (sal<=500 ){ out.print("A" ); }else if ( sal <=1000 ){ out.print("B" ); }else if ( sal <=1500 ){ out.print("C" ); }else if ( sal <=2000 ){ out.print("D" ); }else if ( sal <=3000 ){ out.print("E" ); }else if ( sal <=4000 ){ out.print("F" ); }else { out.print("G" ); } %> </td> </tr> <% } %> </table> </body> </html>
EL表达式 Expression Languaga
EL表达式中定义了一些可以帮助我们快捷从域对象中取出数据的写法,基本语法为
四个域标志关键字分别为
EL表达式快捷取出域对象
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 <%@ page import ="com.msb.pojo.User" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <%--向pageContext域中放数据--%> <% pageContext.setAttribute("msg" , "pageContextMessage" ); pageContext.setAttribute("userx" , new User (1 ,"大黄" ,"abcdefg" )); %> <%-- 从域中取出数据 El表达式在获取对象属性值得时候,是通过对象的属性的get方法获取的 保证对象的要获取的属性必须有对应get方法才可以 EL表达式在使用时是不需要import 其他类的 El如果获取的是NULL值,是不展示任何信息的 --%> pageContext域中的数据:<br/> msg:${pageScope.msg}<br/> username:${pageScope.userx.name}<br/> <hr/> request域中的数据:<br/> msg:${requestScope.msg}<br/> username:${requestScope.user.name}<br/> <hr/> session域中的数据:<br/> msg:${sessionScope.msg}<br/> username:${sessionScope.users[1 ].name}<br/> <hr/> application域中的数据:<br/> msg:${applicationScope.msg}<br/> username:${applicationScope.userMap.a.name}<br/> <hr/> <%--EL表达式在取出数据的时候是可以省略域标志的 EL表达式会自动依次到四个域中去找数据 --%> PageContext username:${userx.name}<br/> Request username:${user.name}<br/> Session username:${users[1 ].name}<br/> Application username:${userMap.a.name}<br/> <hr/> <%-- ${数据的名字}如果省略域标志,取数据的顺序如下 pageContext request session application --%> ${msg} <hr/> <%-- 移除域中的数据 --%> <% %> pagecontextMsg:${pageScope.msg}<br/> requestMsg:${requestScope.msg}<br/> sessionMsg:${sessionScope.msg}<br/> applicationMsg:${applicationScope.msg}<br/> <hr/> <%-- EL表达式获取请求中的参数 --%> username:${param.username}<br/> hobby:${paramValues.hobby[0 ]} hobby:${paramValues.hobby[1 ]} </body> </html>
总结
EL表达式定义在JSP页面上,在转译之后的java文件中,会被转化成java代码
EL表达式是一种后台技术,服务器上运行,不是在浏览器上运行,不能用于HTML页面
EL表达式底层是通过反射实现的,在获取对象属性值时是通过对象的get方法实现的
EL表达式运算符 运算符 算数运算符 : + - * / %
比较运算符:
== eq equals
>gt greater then
< lt lower then
>= ge greater then or equals
<= le lower then or equals
!= ne not equals
逻辑运算符 : || or && and
三目运算符 : ${条件 ?表达式1 : 表达式2}
判空运算符 : empty
使用 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 <%@ page import ="java.util.List" %> <%@ page import ="java.util.ArrayList" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <%-- +两端如果有字符串,会尝试将字符串转换成数字之后进行加法运算 /如果除以0 结果为Infinity 而不是出现异常 %如果和0 取余数,那么会出现异常 --%> 算数运算符: <hr/> ${10 + 10 }<br/> ${"10" + 10 }<br/> ${"10" + "10" }<br/> <%--${"10a" + 10 }<br/>--%> ${10 /0 }<br/> <%-- ${10 %0 }<br/>--%> 关系运算符/比较运算符 <%-- 比较运算符推荐写成字母形式,不推荐使用 == >= <= --%> <hr/> ${10 == 10 }<br/> ${10 eq 10 }<br/> ${10 gt 8 }<br/> 逻辑运算符 <hr/> ${ true || false }<br/> ${ true or false }<br/> ${ true && false }<br/> ${ true and false }<br/> 条件运算符/三目运算符 <hr/> ${(100 -1 )%3 ==0 ?10 +1 :10 -1 }<br/> 判断空运算符 <%--empty 为null 则为true --%> <% pageContext.setAttribute("a" ,null ); pageContext.setAttribute("b" ,"" ); int [] arr ={}; pageContext.setAttribute("arr" ,arr); List list = new ArrayList (); pageContext.setAttribute("list" ,list); %> <hr/> ${empty a}<br/> ${empty b}<br/><%--字符串长度为0 则认为是空--%> ${empty arr}<br/><%--数组长度为0 认为不是空--%> ${empty list}<br/><%--集合长度为0 认为是空--%> ${list.size() eq 0 }<br/><%--集合长度为0 认为是空--%> </body> </html>
案例一优化:使用EL表达式优化查询员工信息的页面处理 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 <%@ page import ="java.util.List" %> <%@ page import ="com.msb.pojo.Emp" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> <style> table{ border: 3px solid blue; width: 80 %; margin: 0px auto; } td,th{ border: 2px solid green; } </style> </head> <body> <table cellspacing="0px" cellpadding="0px" > <tr> <th>编号</th> <th>姓名</th> <th>上级编号</th> <th>职务</th> <th>入职日期</th> <th>薪资</th> <th>补助</th> <th>部门号</th> <th>薪资等级</th> </tr> <% List<Emp> emps = (List<Emp>) request.getAttribute("emps" ); for (Emp emp : emps) { pageContext.setAttribute("emp" , emp); %> <tr> <td>${emp.empno}</td> <td>${emp.ename}</td> <td>${emp.mgr}</td> <td>${emp.job}</td> <td>${emp.hiredate}</td> <td>${emp.sal}</td> <td>${emp.comm}</td> <td>${emp.deptno}</td> <td> ${emp.sal le 500 ?"A" :"" } ${emp.sal gt 500 and emp.sal le 1000 ?"B" :"" } ${emp.sal gt 1000 and emp.sal le 1500 ?"C" :"" } ${emp.sal gt 1500 and emp.sal le 2000 ?"D" :"" } ${emp.sal gt 2000 and emp.sal le 3000 ?"E" :"" } ${emp.sal gt 3000 and emp.sal le 4000 ?"F" :"" } ${emp.sal gt 4000 ?"G" :"" } </td> </tr> <% } %> </table> </body> </html>
JSTL JSTL(Java server pages standarded tag library,即JSP标准标签库)是由JCP(Java community Proces)所制定的标准规范,它主要提供给Java Web开发人员一个标准通用的标签库,并由Apache的Jakarta小组来维护。
使用前提
需要导包
页面中通过taglib指令引入标签库
1 <%@ taglib uri="标签库的定位" prefix="前缀(简称)" %>
uri可以在对应的tld文件中找到
核心标签库 导入语句为
1 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
操作对象的标签c:set/out/remove
==<c:set>== 向域对象中放入数据 setAttribute
==<c:out>== 从域对象中取出数据 getAttribute
==<c:remove>== 从域对象中移除数据 removeAttribute
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 <%-- c:set scope 指定放数据的域 可选值 page request session application var 数据的名称 value 数据 --%> <c:set scope="page" var ="msg" value="pageMessage" ></c:set> <c:set scope="request" var ="msg" value="requestMessage" ></c:set> <c:set scope="session" var ="msg" value="sessionMessage" ></c:set> <c:set scope="application" var ="msg" value="applicationMessage" ></c:set> <%--移除指定域中的值--%> <%-- <c:remove var ="msg" scope="page" ></c:remove> <c:remove var ="msg" scope="request" ></c:remove>--%> <c:remove var ="msg" scope="session" ></c:remove> <c:remove var ="msg" scope="application" ></c:remove> <%--通过EL表达式取出域中的值--%> <hr/> ${pageScope.msg}<br/> ${requestScope.msg}<br/> ${sessionScope.msg}<br/> ${applicationScope.msg }<br/> <hr/> <%--通过c:out标签获取域中的值--%> <c:out value="${pageScope.msg}" default ="page msg not found" /> <c:out value="${requestScope.msg}" default ="request msg not found" /> <c:out value="${sessionScope.msg}" default ="session msg not found" /> <c:out value="${applicationScope.msg}" default ="application msg not found" /> </body> </html>
多条件分支标签c:if和c:choose 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 <%-- 随机生成一个分数 0 -100 >=90 A >=80 B >=70 C >=60 D <60 E --%> <% int score = new Random ().nextInt(101 ); pageContext.setAttribute("score" , score); %> <%-- test 判断条件 c:if 可以将test的结果放入指定的域中 scope 指定存放的域 var 数据名 --%> 分数:${score}<br/> 等级: <c:if test="${score ge 90}" scope="page" var ="f1" >A</c:if > <c:if test="${score ge 80 and score lt 90}" scope="page" var ="f2" >B</c:if > <c:if test="${score ge 70 and score lt 80}" scope="page" var ="f3" >C</c:if > <c:if test="${score ge 60 and score lt 70}" scope="page" var ="f4" >D</c:if > <c:if test="${score lt 60}" scope="page" var ="f5" >E</c:if > <hr/> ${f1} ${f2} ${f3} ${f4} ${f5} <hr/> <%--输出分数是否及格--%> <c:if test="${score ge 60}" scope="page" var ="flag" >及格</c:if > <c:if test="${!pageScope.flag}" >不及格</c:if > <hr/> <c:choose> <c:when test="${score ge 90}" >A</c:when> <c:when test="${score ge 80}" >B</c:when> <c:when test="${score ge 70}" >C</c:when> <c:when test="${score ge 60}" >D</c:when> <c:otherwise>E</c:otherwise> </c:choose>
迭代标签c:foreach 打印乘法表 c:forEach中的属性
==var==: 迭代变量, 存放在pageContext作用域
==begin==: 迭代起始值
==end==: 迭代结束值
==step==: 迭代变量变化的步长
1 2 3 4 5 6 7 8 9 10 11 12 <%-- for ( int i = 1 ;i<=9 ;i+=2 ){ pageContext.setAttribute("i" ,i) } c:foreach 每次执时都会向page域中放入一个名为 i 值为当前值这样的一个操作 --%> <c:forEach var ="i" begin="1" end="9" step="1" > <c:forEach var ="j" begin="1" end="${i}" step="1" > ${j} * ${i} = ${i*j} </c:forEach> <br/> </c:forEach>
遍历对象数组
==items==: 要遍历的集合, 需要使用EL表达式取值
==varStatus==: 迭代变量的状态
==index:== 索引, 从0开始
==count:== 计数, 从1开始
==first==: boolean, 表示是否是第一个
==last==: boolean, 表示是否是最后一个
==current==: 对象, 当前迭代的对象值
1 2 3 4 5 6 7 8 <%--<% List<Emp> emps = (List<Emp>) request.getAttribute("emps" ); for (Emp emp : emps) { pageContext.setAttribute("emp" , emp); %> c:foreach --%> <c:forEach items="${emps}" var ="emp" varStatus="empStatus" >
格式化标签库fmt 导入标签 1 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
1 2 3 <td> <fmt:formatDate value="${emp.hiredate}" pattern="yyyy年MM月dd日 HH:mm:ss" /> </td>
数字格式化标签 1 2 3 4 5 6 7 <td> <%-- 0 代表必须有一位数字,如果对应的位置没有值怎么办?自动补充0 # 代表有一位数字,开头和结尾的所有的0 不保留 --%> ¥<fmt:formatNumber value="${emp.sal}" pattern="###,##0.00" /> </td>
showEmp.js页面最终优化 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 127 128 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <html> <head> <title>Title</title> <style> table{ border: 3px solid blue; width: 80 %; margin: 0px auto; } td,th{ border: 2px solid green; } </style> </head> <body> <table cellspacing="0px" cellpadding="0px" > <tr> <th>序号</th> <th>索引</th> <th>isFirst</th> <th>isLast</th> <th>编号</th> <th>姓名</th> <th>姓名</th> <th>上级编号</th> <th>职务</th> <th>入职日期</th> <th>薪资</th> <th>补助</th> <th>部门号</th> <th>薪资等级</th> </tr> <%-- <% List<Emp> emps = (List<Emp>) request.getAttribute("emps" ); for (Emp emp : emps) { pageContext.setAttribute("emp" ,emp); %> c:foreach items 要遍历的数组/List 可以通过EL表达式取出集合之后给改属性赋值 var 中间变量的名称 varStatus 记录每一个对象状态的设置 count 个数 index 索引 first 如果当前元素是迭代的第一个元素 true 否则为false last 如果当前元素是迭代的最后一个元素 true 否则为false current 当前迭代的元素本身 --%> <c:forEach items="${emps}" var ="emp" varStatus="empStatus" > <tr> <%--使用EL表达式来取出域对象里的对象属性值--%> <%-- <td><%=emp.getEmpno()%></td> <td><%=emp.getEname()%></td> <td><%=emp.getMgr()%></td> <td><%=emp.getJob()%></td> <td><%=emp.getHiredate()%></td> <td><%=emp.getSal()%></td> <td><%=emp.getComm()%></td> <td><%=emp.getDeptno()%></td>--%> <td>${empStatus.count}</td> <td>${empStatus.index}</td> <td>${empStatus.first}</td> <td>${empStatus.last}</td> <td>${emp.empno}</td> <td>${emp.ename}</td> <td>${empStatus.current.ename}</td> <td>${emp.mgr}</td> <td>${emp.job}</td> <td> <fmt:formatDate value="${emp.hiredate}" pattern="yyyy年MM月dd日 HH:mm:ss" /> </td> <td> <%-- 0 代表必须有一位数字,如果对应的位置没有值怎么办?自动补充0 # 代表有一位数字,开头和结尾的所有的0 不保留 --%> ¥<fmt:formatNumber value="${emp.sal}" pattern="###,##0.00" /> </td> <td>${emp.comm}</td> <td>${emp.deptno}</td> <td><%--out.print("<td>" )--%> <%--<% Double sal = emp.getSal(); if (sal<=500 ){ out.print("A" ); }else if ( sal <=1000 ){ out.print("B" ); }else if ( sal <=1500 ){ out.print("C" ); }else if ( sal <=2000 ){ out.print("D" ); }else if ( sal <=3000 ){ out.print("E" ); }else if ( sal <=4000 ){ out.print("F" ); }else { out.print("G" ); } %>--%> <%--使用EL算数表达式来判断等级 ${emp.sal le 500 ?"A" :"" } ${emp.sal gt 500 and emp.sal le 1000 ?"B" :"" } ${emp.sal gt 1000 and emp.sal le 1500 ?"C" :"" } ${emp.sal gt 1500 and emp.sal le 2000 ?"D" :"" } ${emp.sal gt 2000 and emp.sal le 3000 ?"E" :"" } ${emp.sal gt 3000 and emp.sal le 4000 ?"F" :"" } ${emp.sal gt 4000 ?"G" :"" }--%> <%--使用JSTL标签--%> <c:choose> <c:when test="${emp.sal le 500}" >A</c:when> <c:when test="${emp.sal le 1000}" >B</c:when> <c:when test="${emp.sal le 1500}" >C</c:when> <c:when test="${emp.sal le 2000}" >D</c:when> <c:when test="${emp.sal le 3000}" >E</c:when> <c:when test="${emp.sal le 4000}" >F</c:when> <c:when test="${emp.sal gt 4000}" >G</c:when> </c:choose> </td> </tr> </c:forEach> </table> </body> </html>
Filter 案例:通过过滤验证登录 需求:通过过滤器控制,只有登陆之后可以反复进入welcome.jsp欢迎页,如果没有登录,提示用户进入登录页进行登陆操作。
login.jsp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title%sSourceCode%lt;/title> </head> <body> <img src="static/img/logo.png" > please login ... ... <br/> <form action="loginController.do" method="post" > 用户名:<input type="text" name="user" > <br/> 密码:<input type="password" name="pwd" ><br/> <input type="submit" value="提交" > </form> </body> </html>
welcome.jsp 1 2 3 4 5 6 7 8 9 10 11 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <img src="static/img/logo.png" > 欢迎${user.username}登陆!!! </body> </html>
aaa.jsp 1 2 3 4 5 6 7 8 9 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> this is page aaa</body> </html>
准备Controller代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @WebServlet(urlPatterns = "/loginController.do") public class LoginController extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("user" ); String password = req.getParameter("pwd" ); System.out.println(username); System.out.println(password); User user = new User (username,password); req.getSession().setAttribute("user" , user); resp.sendRedirect("welcome.jsp" ); } }
准备登录控制过滤器
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 @WebFilter(urlPatterns = "/*") public class Filter1_LoginFilter implements Filter { @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req=(HttpServletRequest)servletRequest; HttpServletResponse resp=(HttpServletResponse) servletResponse; String requestURI = req.getRequestURI(); System.out.println(requestURI); if (requestURI.contains("login.jsp" )|| requestURI.contains("loginController.do" )|| requestURI.contains("/static/" )){ filterChain.doFilter(req,resp); return ; } HttpSession session = req.getSession(); Object user = session.getAttribute("user" ); if (null != user){ filterChain.doFilter(req,resp); }else { resp.sendRedirect("login.jsp" ); } } @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void destroy () { } }
Listener 案例:记录请求日志 RequestLoginListener.java 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 @WebListener public class RequestLogListener implements ServletRequestListener { private SimpleDateFormat simpleDateFormat=new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" ); @Override public void requestDestroyed (ServletRequestEvent sre) { } @Override public void requestInitialized (ServletRequestEvent sre) { HttpServletRequest request = (HttpServletRequest)sre.getServletRequest(); String remoteHost = request.getRemoteHost(); String requestURL = request.getRequestURL().toString(); String reqquestDate = simpleDateFormat.format(new Date ()); try { PrintWriter pw = new PrintWriter (new FileOutputStream (new File ("d:/msb.txt" ),true )); pw.println(remoteHost+" " +requestURL+" " +reqquestDate ); pw.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
案例:显示在线人数 需求 :开启一次会话session 在线人数加一 销毁会话以后在线人数减一
使用count来计数,然后存在application域中
OnlineNumberListener: 获取application域对象,存入数据count,如果一次session开启,++count;一次session关闭,–count
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 @WebListener public class OnlineNumberListener implements HttpSessionListener { @Override public void sessionCreated (HttpSessionEvent se) { HttpSession session = se.getSession(); ServletContext application = session.getServletContext(); Object count = application.getAttribute("count" ); if (null == count) { application.setAttribute("count" ,1 ); }else { int c = (int ) count; application.setAttribute("count" , ++c); } } @Override public void sessionDestroyed (HttpSessionEvent se) { HttpSession session = se.getSession(); ServletContext application = session.getServletContext(); Object count = application.getAttribute("count" ); int count1 = (int ) count; application.setAttribute("count" ,--count1); } }
案例:重启免登录 Session序列化和反序列化 1、序列化与反序列
把对象转化为字节序列的过程称为序列化(保存到硬盘,持久化)
把字节序列转化为对象的过程称为反序列化(存放于内存)
2、序列化的用途
把对象的字节序列永久保存到硬盘上,通常放到一个文件中。
把网络传输的对象通过字节序列化,方便传输本节作业
3、实现步骤
想实现序列化和反序列化需要手动配置
1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="UTF-8" ?> <Context > <Manager className ="org.apache.catalina.session.PersistentManager" > <Store className ="org.apache.catalina.session.FileStore" directory ="d:/session" /> </Manager > </Context >
==注意实体类必须实现serializable 接口==
开发过程 1 准备实体类 1 2 3 4 public class User implements Serializable { private String username; private String pwd; }
2 开发登录信息输入页面 1 2 3 4 5 <form action="loginController.do" method="post" > 用户名:<input type="text" name="user" > <br/> 密码:<input type="password" name="pwd" ><br/> <input type="submit" value="提交" > </form>
3开发登录信息验证Servlet 1 2 3 4 5 6 7 8 9 10 11 12 13 @WebServlet("/loginController.do") public class LoginController extends HttpServlet {@Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("user" ); String pwd = req.getParameter("pwd" ); User user = new User (username,pwd); HttpSession session = req.getSession(); session.setAttribute("user" , user); } }
4 开发校验当前是否已经登录的Controller loginCheckController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @WebServlet(urlPatterns = "/loginCheckController.do") public class LoginCheckController extends HttpServlet {@Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); Object user = session.getAttribute("user" ); Object listener = session.getAttribute("listener" ); String message = "" ; if (null != user){ message="您已经登录过" ; }else { message="您还未登录" ; } resp.setCharacterEncoding("UTF-8" ); resp.setContentType("text/html;charset=UTF-8" ); resp.getWriter().println(message); } }
5 测试 先登录,然后请求loginCheckController.do 校验是否登录过,然后重启项目,再起请求loginCheckController.do 校验是否登录过,发现重启后,仍然是登录过的
6 监听钝化和活化 MySessionActivationListener.java
1 2 3 4 5 6 7 8 9 10 public class MySessionActivationListener implements HttpSessionActivationListener , Serializable {@Override public void sessionWillPassivate (HttpSessionEvent se) { System.out.println(se.getSession().hashCode()+"即将钝化" ); } @Override public void sessionDidActivate (HttpSessionEvent se) { System.out.println(se.getSession().hashCode()+"已经活化" ); } }
LoginController登录时绑定监听器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @WebServlet("/loginController.do") public class LoginController extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("user" ); String pwd = req.getParameter("pwd" ); User user = new User (username,pwd); HttpSession session = req.getSession(); session.setAttribute("user" , user); session.setAttribute("listener" , new MySessionActivationListener ()); } }
重启项目 重复测试即可