内容简述:Http协议、Servlet工作原理、DAO、Servlet生命周期、JSP基础、cookie、Session、过滤器、监听器、线程安全、JSTL、自定义标签、MVC。
一、Servlet基础
1.什么是Servlet?
- sun公司制订的一种用来扩展web服务器功能的组件规范。
扩展web服务器功能
a. web服务器通常只能处理
静态资源
(即需要事先将html文件写好并存放在服务器上)的请求,不能够处理
动态资源
(即需要计算,动态生成相应的页面)的请求。b. 早期,开发者会使用CGI程序来扩展web服务器功能。
了解:CGI程序指的是使用c,perl等语言开发的符合CGI(Common Gateway Interface)标准的程序。因为CGI程序开发繁琐,并且可移值性不好,所以用得越来越少了。
c. 可以使用Servlet来扩展web服务器功能。
补充:当web服务器收到请求之后,如果是动态资源的请求,可以调用servlet来处理。
组件规范
a. 什么是组件?
- 符合规范,具有一定功能,并且需要部署到相应的容器上才能运行的软件模块。
- Servlet就是一个符合Servlet规范的组件,需要部署到Servlet容器里面才能运行。
b. 什么是容器?
- 符合规范,提供组件的运行环境的程序。
- Servlet容器(比如Tomcat)提供Servlet运行环境。
2.如何开发一个Servlet?
step1. 写一个java类,实现Servlet接口或者继承HttpServlet类。
(通常我们选择继承HttpServlet类)
step2. 编译。
step3. 打包。
- 需要创建一个具有如下结构的文件夹:
- appname (名称可以自定义) 应用名
- WEB-INF
- classes (存放.class文件)
- lib(可选,用来存放.jar文件)
- web.xml (部署描述文件)
- 需要创建一个具有如下结构的文件夹:
step4. 部署。
将step3创建好的文件夹拷贝到容器指定的位置。
(也可以将step3创建好的文件夹使用 jar命令压缩成.war结尾的文件,然后拷贝)
step5. 启动容器
- 打开浏览器,在地址栏输入:http://ip:port/appname/url-pattern
3.安装Tomcat(Tomcat是一个相对标准的容器)
创建servlet测试项目:
1)创建一个maven项目;
2)设置响应的名称并且把打包方式改为war;
3)问题:创建后的day01项目上有一个小红叉;
**解决办法**:在javaEE模式下,右键Deployment选择Generate Deployment.,然后小红叉消失了。
**原因**:之所以有小红叉是因为没有生成部署描述文件(web.xml),操作之后,在src文件夹下main文件夹下的webapp中会出现一个WEB-INF文件夹,里面有web.xml文件。
4)右键day01项目,选择最后一项Properties,然后选择Targeted Runtimes,在右侧选中tomcat7.0
原因:这一步是为了将想要运行的tomcat 的相关jar包导入我们的项目当中;
5)在Java Resources中的src/main/java中创建Serlvet类:
1 | public class TimeServlet extends HttpServlet { |
输出结果
doGet()方法和service()方法的区别:
- doGet()方法只处理Get请求;
- doPost()方法只处理Post请求;
- service()方法即处理Get请求,也处理Post请求;
- 官方文档上建议尽量避免使用service()方法。
6)在web.xml中配置serlvet
1 |
|
7)运行:在项目处右键→Run as→Run on Server→选中Tomcat 7.0→点next→点finish→重启Server→完成
4.Servlet是如何运行的?
比如,在浏览器地址栏输入:http://ip:port/day01/hello
step1.浏览器依据ip和port建立与服务器之间的连接 (比如,建立与tomcat之间的连接)。
step2.浏览器将相关数据打包 (即按照http协议的要求,创建请求数据包)。
step3.浏览器将请求数据包发送给服务器。
step4.服务器解析请求数据包的内容,并且将解析到的数据添加到request对象里面,同时创建一个response对象。
step5.服务器将对应的Servlet(比如HelloServlet)
实例化,接下来调用Servlet实例的service方法。
注意:
- 服务器会将request和response作为参数传递给service方法,可以通过request对象获取请求数据(比如一些请求参数值),然后通过response对象写入处理结果。
step6.服务器通过response对象获取处理结果,然后创建响应数据包。
step7.服务器发送响应数据包给浏览器。
step8.浏览器解析响应数据包,并且生成相应的页面。
5.常见的错误及解决方式
(1) 404
404 Not Found,表示服务器依据请求地址找不到对应的资源。
错误原因:
a.请求地址写错了;
b.应用没有部署成功。
(2)500
500 Internal Server Error,表示服务器端的程序运行出错。
错误原因:
a.没有继承HttpServlet或者实现Servlet接口;
b.web.xml配置错误,比如servlet-class写错;
c.程序写得不够严谨,异常报错,比如将一个非数字转换成数字。
(3)405
- 405 Method Not Allowed(GET请求和POST接口对接时就会报405)
- 错误原因:
- service()方法签名不符合要求(方法名,参数类型,异常类型要符合规范–两个异常)。
课堂练习
练习1:
- 写一个DateServlet,输出当前的系统日期, 比如在浏览器地址栏输入 http://ip:port/day01-lab/date 显示 2018-02-06
- 建议新建工程,从零开始写。
练习2:
- 写一个BmiServlet,计算一个人的bmi指数。
- bmi指数 = 体重(公斤) / 身高(米) / 身高(米)
- http://ip:port/day01-lab/bmi?weight=80&height=1.5
二、HTTP协议、抓包、Servlet工作原理
1.http协议
什么是http协议?
- 一种网络应用层协议,规定了浏览器与web服务器之间如何通信以及通信过程当中所使用的数据格式。
http协议如何通信
step1.建立连接;
step2.发送请求;
step3.发送响应;
step4.关闭连接
即“一次请求,一次连接”,如果要发送新的请求,需要重新建立新的连接。
http协议的优点:
- web服务器可以使用尽可能少的连接为更多的请求服务。
2.数据格式
抓包:
1)在Eclipse中Windows→Show View→Others→Debug→TCP/IP Monitor
2)在最左上角的框中右键,点击Properties
3)点击Add,在Local monitoring port处填写监视器的端口号(比如8888)
- 在Host name处填写想要拦截的地址(localhost);
- 在Port处填写想要拦截的端口号(8080)如图:
4) 在浏览器输入网页的网址,只不过网址的端口号改为监视器的端口号
5)在Eclipse的监视器的框中便会显示抓到的信息(第二行,左边是浏览器发送给服务器的请求内容,有边是服务器发送给浏览器的响应内容)
1)请求数据包
请求行 (请求方式 请求资源路径 协议类型和版本)
若干消息头
消息头是一些键值对(使用”:”隔开),可以用来传递一些特定的信息;
比如,浏览器可以发送 user-agent消息头告诉服务器,浏览器的类型和版本。
实体内容
- 只有请求类型为post方式时,实体内容才会有数据。
2)响应数据包
状态行 (协议类型和版本 状态码 状态描述)
状态码是一个三位数字,表示服务器处理请求的一种状态,200(正常),500(系统出错),404(找不到对应的资源)。
若干消息头
服务器也可以发送一些消息头给浏览器;
比如:发送 content-type 消息头,告诉浏览器,服务器返回的数据类型。
实体内容
- 程序返回的处理结果,浏览器会解析出来,并生成相应的页面。
(3)请求方式
GET 请求
浏览器在什么情况下会发送get请求?
a.在地址栏直接输入某个地址。
b.点击链接。
c.表单默认提交方式。
GET 请求的特点:
a.会将请求参数显示在浏览器地址栏,不安全。比如,路由器会记录请求地址。
b.会将请求参数添加到请求行里面,只能提交少量的数据(整个请求行大概能存放2k左右的数据)。
POST 请求
浏览器在什么情况下会发送post请求?
a.将表单的提交方式设置为post。
post请求的特点:
a.不会将请求参数显示在浏览器地址栏,相对安全。
- 并不会对请求参数进行加密处理,所以,对于敏感数据,需要加密(使用https协议)
b.会将请求参数添加到实体内容里面,可以提交大量的数据。
Servlet输出中文,要注意什么?
- (1)为什么会有乱码?
- out默认会使用iso-8859-1来编码。
- (2)如何解决?
- response.setContentType(“text/html;charset=utf-8”);
如何获得请求参数值?
(1) String request.getParameter(String paramName)
- a.paramName是请求参数名。
- b.如果paramName写错,则获得null值。
- c.表单提交时,如果没有填写任何数据,会获得空字符串。
(2) String[] request.getParameterValues(String paramName)
- a.当有多个请求参数名相同时,使用该方法。
- b.对于多选框,如果没有选择任何选项,则获得null值。
表单提交时,如何读取中文参数值?
(1) 为什么会有乱码?
表单提交时,浏览器会对中文参数值进行编码(会使用打开表单所在的页面时的字符集来编码)。而服务器端默认会使用iso-8859-1来解码。
(2) 如何解决?
1) POST 请求
request.setCharacterEncoding(String charset)
- 这行代码要添加到所有的getParameter方法的前面。只针对post请求有效。
2) GET 请求
配置 URIEncoding=”utf-8”。
- 只针对get请求有效。
课堂练习
练习1:注册用户
参考代码如下:
1 | public class RegServlet extends HttpServlet { |
练习2:添加用户
1 | CREATE TABLE t_user( |
三、DAO、重定向
1.DAO (Data Access Object 数据访问对象)
(1)什么是DAO?
- 封装了数据访问逻辑的对象。
(2)如何写一个DAO?
step1.写一个实体类(一个java类,里面有字段、get()、set()、toString()方法)
将查询到的记录存放到一个java对象里面,就需要设计一个对应的java类(即实体类),该类与要访问的表要一致(字段与属性要一一对应)。
比如,要访问的表是:
t_user(id,username,password,phone) 就可以设计 User类(id,uname,pwd,phone)
step2.写一个DAO类
- 封装数据访问逻辑,比如,将查询到的记录中的信息存放到一个实体类的实例里面。
2.重定向
(1)什么是重定向?
服务器通过发送302状态码及Location消息头(该消息头的值是一个地址,一般称之为重定向地址)
通知浏览器访问一个新的地址(即重定向地址)。
(2)如何重定向?
- response.sendRedirect(String url);
- url为重定向地址;
- 重定向之前,容器会先清空response对象上存放的所有数据,然后再重定向。
(3)特点
- a.重定向地址是任意的。
- b.重定向之后,浏览器地址栏的地址会发生变化。
练习:
- 实现添加员工、员工列表功能
1 | -- 员工表: |
四、Servlet生命周期、JSP基础
1.Servlet生命周期
(1)什么是Servlet的生命周期?
- 容器如何创建Servlet实例,如何对其进行初始化,
- 如何调用其方法来处理请求,以及如何销毁其实例的整个过程。
(2)生命周期分成哪几个阶段?(4个阶段–实例化、初始化、调用、销毁)
1.实例化
a.什么是实例化?
容器调用Servlet的构造器,创建相应的对象。
b.什么时候实例化?
情形1:容器收到请求之后才创建实例。(默认)
情形2:容器启动之后,立即创建实例。(需要额外配置)
1 | <servlet> |
( 注:容器只会创建一个实例,单例模式。)
2.初始化
a.什么是初始化?
容器调用Servlet实例的init方法。( 注:该方法只会执行一次!)
b.GenericServlet的init方法?
将容器传递过来的ServletConfig对象保存下来了,并且提供了一个getServletConfig方法用来获得该对象。
c.如何实现自已的初始化处理逻辑?
重写 GenericServlet提供的init()即可。(不带参的那个方法)
d.初始化参数
step1.先配置初始化参数
step2.调用ServletConfig提供的 getInitParameter方法
1
2
3
4
5
6
7
8
9
public void init(servletconfig config) throws ServletException {
// 保存容器传递过来的 ServletConfig对象
this.config = config;
// 便于开发人员去扩展自已的初始化逻辑
// (只需要 override下面这个init方法即可)。
this.init();
}
3.调用(就绪)
a.什么是就绪?
容器调用Servlet实例的service方法来处理请求。
b.HttpServlet的service方法
依据请求类型调用对应的doXXX方法。
比如:get请求会调用doGet方法,post请求会调用doPost方法。
注意:doGet,doPost方法只是简单的抛出了一个异常。需要开发人员去重写(override)。
我们要写一个Servlet,既可以重写HttpServlet的service方法,也可以重写其doGet方法和doPost方法。
4.销毁
a.什么是销毁?
容器在删除Servlet实例之前,会调用该实例的 destroy() 方法。(该方法只会执行一次)
b.如何实现自已的销毁处理逻辑?
只需要override GenericServlet的destroy方法即可。
相关的接口与类
- Servlet接口
- init()
- service()
- destroy()
- GenericServlet抽象类
- 实现了Servlet接口中的init和destroy方法。
- HttpServlet抽象类
- 继承了GenericServlet抽象类,实现了 service() 方法。
2.JSP
(1)什么是jsp?
sun公司制订的一种服务器端动态页面技术规范。
- a.虽然直接使用Servlet也可以生成动态页面,但是,过于繁琐(需要使用out.println方法输出),也不利于页面的维护(需要修改java代码),所以,sun才制订了jsp技术规范。
- b.jsp是一个以.jsp为后缀的文件(主要内容是html和少量的java代码),容器会将这个文件转换成相应的Servlet然后执行。
- jsp的本质是Servlet。
注意:jsp文件在运行之后需要访问才会生成.class文件
(2)如何写一个jsp文件?
step1. 添加一个以.jsp为后缀的文件。
step2. 在文件当中,可以添加如下内容:
1)html(包括css,javascript) 直接写即可。
2)java代码
- 方式一: java代码片断 <% java代码 %>
* 方式二 :jsp表达式 <%= java表达式 %>
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201204142806386.jpg)
3)隐含对象
a.什么是隐含对象?
在jsp文件当中可以直接使用的对象(比如out,request,response)。
b.为什么可以直接使用这些隐含对象?
容器会自动生成获得这些对象的代码。
4)指令
a.什么是指令?
告诉容器,在将jsp文件转换成Servlet的时候,做一些额外的处理,比如导包。
b.语法 <%@ 指令名 属性=值 %>
c.page指令
import属性:导包,比如:(多个包用”,”隔开)
1
<%page import="java.util.*,java.util.*"%>
(3)jsp是如何执行的?
step1. 容器将jsp文件转换成一个Servlet
a. html(css,js) —-> 在service方法里,使用out.write输出。
b. <% %> —-> 在service方法里,照搬。
c. <%= %> —-> 在service方法里,使用out.print输出。
step2.容器调用该Servlet。
五、page指令、转发、JSP开发常见问题
1.page指令
- contentType属性:设置response.setContentType的值。
pageEncoding属性:告诉容器,在读取jsp文件的内容时,使用指定的字符集去解码。
注意:charset和pageEncoding的值可以不一样,比如:
上图是可以运行的,因为charset是给浏览器说明的编码格式,pageEncoding是读jsp文件的时候需要的编码格式,只要是中文就可以读取,但是我们一般使用相同的编码格式。
2.转发
(1)什么是转发?
一个web组件将未完成的处理转交给另外一个web组件继续做。
web组件(servlet或者是jsp),大多数情况下,是一个servlet获得数据之后,转发给jsp来展现这些数据。
(2)如何转发?
step1.绑订数据到request对象上。
request.setAttribute(String name,Object obj)
- name 绑订名。
- obj 绑订值。
- Object request.getAttribute(String name)
step2.获得转发器
RequestDispatcher rd = request.getRequestDispatcher(String uri);
- uri:转发的目的地地址,通常是一个jsp。
- RequestDispatcher:是一个接口,getRequestDispatcher方法会返回该接口的实现。
step3.转发
rd.forward(request,response)
- 详细步骤:
1:在浏览器地址栏输入地址后回车发送listUser请求
2:创建request对象
3:就绪,调用service()方法
4:在ListUserServlet中向request对象中绑定数据
5:调用forward()方法转发后,通知容器
6:让web容器去调用另外一个组件的service()方法
7:另一个组件再从request对象中获得之前绑定的数据
注意:转发的本质是通知容器调用(即执行service方法)另外一个web组件。
(3)特点
a.转发的目的地有限制(同一个应用)。
b.转发之后,浏览器地址栏的地址不变。
3.比较转发与重定向
- a.能否共享request和response?
- 转发可以,而重定向不行。
- 分析:当请求到达容器,容器创建request和response,当响应发送完毕,容器会销毁这两个对象,也就是说,request 和 response 的生存时间是一次请求和响应期间存在。
- 转发是一次请求,重定向是两次请求。
- b.浏览器地址栏地址有无变化?
- 转发无变化,重定向有变化。
- c.目的地有无限制?
- 转发有限制(同一个应用),重定向无限制。
如何用Eclipse创建Servlet:
step1:右键“src/main/java”,新建一个Servlet
step2:点击next,可以自定义url-mapping地址名
step3:点击next,可以选择默认生成的方法,我们一般只选择默认生成service()方法
4.web容器
什么是web容器,其作用是? 尽可能写出你知道的容器名称
- web容器给处于其中的应用程序组件(JSP、SERVLET)提供一个环境;
- 作用:使JSP、SERVLET直接跟容器中的环境变量接口交互,不必关注其它系统问题;
- 例如:TOMCAT、WEBLOGIC、WEBSPHERE等都为web容器。
容器如何处理请求资源路径?
step1.容器默认认为访问的是一个servlet
如果地址名和HTML等文件名重复的话先访问servlet,会查看配置文件(web.xml)有没有一个匹配的servlet。
step2.如果没有匹配的servlet,则查找对应位置的文件,然后执行(如果找不到,则返回404)。
配置规则
(1)精确匹配:
- 要求url-pattern的值必须等于”/abc.html”。
(2)通配符匹配:
- 使用 * 号匹配任意的零个或者多个字符,比如:
1 | <url-pattern>/*</url-pattern> |
- (3)后缀匹配:(注意:后缀匹配不能用/开头)
- 使用”*.”开头,后接一个后缀,比如:
1 | <url-pattern>*.do</url-pattern> |
如何让一个servlet处理多种请求?
- step1.采用后缀匹配。比如:
1 | <url-pattern>*.do</url-pattern> |
- step2.分析请求资源路径,然后进行不同的处理。
六、路径问题
链接:
1 | <a href=""> |
表单提交:
1 | <form action=""> |
重定向:
1 | response.sendRedirct(""); |
转发:
1 | request.getRequestDispatcher(""); |
相对路径
- 不以/开头的路径。
绝对路径
- 以/开头的路径。
- 如何写绝对路径?
- 链接,表单提交,重定向从应用名开始写;转发从应用名之后开始写。
注意:
不要直接将应用名写在路径里面,而应该使用 request.getContextPath()来获取。
(应该尽量避免硬编码—-把应用名写死)
建议使用绝对路径,好写,并且更好维护。
综合练习:登录
需求:
- 用户填写用户名和密码并提交;
- 服务器端查询数据库,看是否有匹配的记录(即用户名和密码都匹配);
- 如果有,则登录成功(返回用户列表);
- 如果没有,则登录失败(在登录页面当中,提示用户名或密码错误)。
提示:
- step1.给UserDAO添加一个方法
1 | public User findByUsername(String uname) |
step2.添加一个登录页面 login.jsp
step3.给ActionServlet添加一个处理 “/login” 的分支,用于处理登录请求。
1 | request.setAttribute("login_failed", "用户名或密码错误"); |
七、状态管理-cookie
1.状态管理
(1) 什么是状态管理?
- 将浏览器与web服务器之间多次交互做为一个整体来处理,将多次交互所涉及的数据(即状态)保存下来。
(2) 如何进行状态管理?
方式一: 将状态保存在浏览器端,通常使用cookie技术;
方式二: 将状态保存在服务器端,通常使用session技术。
2.cookie
(1) 什么是cookie?
- 服务器临时存放在浏览器端的少量数据。
(2) cookie工作原理
- 浏览器访问服务器时,服务器会将一些数据以set-cookie消息头的形式发送给浏览器,
- 浏览器会将这些数据临时保存下来;
- 当浏览器再次访问服务器时,会将之前存放的数据以cookie消息头的形式发送给服务器。
(3) 添加cookie
1 | Cookie c = new Cookie(String name,String value); |
注意:cookie的名称和值都必须是String。
(4) 读取cookie
1 | // 有可能返回null |
(5) cookie的生存时间
a.默认情况下,cookie会被浏览器保存在内存里面。即,浏览器只要不关闭,cookie就会一直存在。
b.setMaxAge(int seconds)
- 单位是秒。
- seconds > 0: 浏览器会将cookie保存在硬盘上,当超过指定时间,浏览器会删除cookie。
- seconds < 0: 默认值(即保存在内存)
- seconds = 0: 删除cookie。
比如,要修改名称为username的cookie的值:
- 再发送一个Cookie,名为username,值为想要修改成的值
1 | Cookie c = new Cookie("username", ""); |
- 比如,要删除名称为username的cookie:
1 | Cookie c = new Cookie("username", ""); |
注意:如果Cookie的路径不一样是不能被覆盖的。
6)编码问题
cookie只能保存合法的ascii字符,如果要保存中文,
要将中文转换成相应的ascii字符的形式。
String URLEncoder.encode(String str,String charset);
String URLDecoder.decode(String str,String charset);
注:建议,添加cookie时,最好对保存的字符统一编码处理。
7)cookie的路径问题
a.什么是cookie的路径问题?
当浏览器访问服务器上的某个地址时,会比较
该地址是否匹配cookie的路径,只有匹配的cookie
才会发送给服务器。
b.cookie的默认路径
等于添加该cookie的web组件的路径。
c.匹配规则
要访问的地址必须等于cookie的路径或者
是其子路径。
比如 cookie的路径是”/day07/biz01”,则
/day07/findCookie1.jsp no
/day07/biz01/findCookie2.jsp yes
/day07/biz01/sub/findCookie3.jsp yes
d.修改cookie的路径
cookie.setPath(String path);
8)cookie的限制
a.cookie可以被用户禁止。
b.cookie不安全。
对于敏感数据,一定要加密。
c.cookie只能保存少量的数据。
大约4k左右
d.cookie的数量也有限制。
总的数量大约是几百个,对于某个网站,数量
也有限制(大约是二十个左右)。
e.cookie只能保存字符串。
练习:写一个Servlet(比如CountServlet),记录
用户访问的次数(要求使用cookie技术)。
八、状态管理-Session
1.session 会话
(1) 什么是session?
- 服务器端为了保存状态而创建的一个特殊的对象。
(2) session的工作原理
- 浏览器访问服务器时,服务器创建一个session对象(该对象有一个唯一的id,称之为sessionId);
- 然后将sessionId以cookie的方式发送给浏览器(意味着浏览器重启的话,sessionId将会更新)(是在request.getSession()方法中实现的);
- 当浏览器再次访问服务器时,会将sessionId发送过来,服务器可以利用sessionId找到之前的session对象。
(3) 如何获得session对象?
方式一:
HttpSession session = request.getSession(boolean flag);
- a. HttpSession是一个接口。
- b. 当flag为true时,先查看请求当中是否有sessionId,
- 如果没有,则创建一个session对象;
- 如果有,则依据sessionId查找对应的session对象,
- 如果找到了,则返回,找不到,则创建一个新的session对象。
- c. 当flag为false时,先查看请求当中是否有sessionId,
- 如果没有,则返回null;
- 如果有,则依据sessionId查找对应的session对象,
- 如果找到了,则返回,找不到,返回null。
方式二:
HttpSession session = request.getSession();
- 等价于 request.getSession(true)。
request.getSession();
(4) 保存状态相关的几个方法
- setAttribute(String name,Object obj);–绑定数据
- Object getAttribute(String name);–获取数据
- removeAttribute(String name);–解除绑定
(5) session超时
什么是session超时?
- 服务器会将空闲时间过长的session对象删除掉。
- 注:这样可以节省内存空间。一般空闲时间长度为半个小时左右。
修改超时时间长度
方式一:
在Servers的web.xml文件中修改以下参数(单位是分钟):(一般没有必要去修改!)
1
2
3<session-config>
<session-timeout>30</session-timeout>
</session-config>方式二:
session.setMaxInactiveInterval(int seconds);
- 两次请求之间的间隔如果超过指定的时间(单位是秒),则session对象被销毁。
(6) 删除session
- session.invalidate();(invalidate–失效)
session验证(案例)
- step1.登录成功之后,将一些数据绑订到session对象之上,比如:
1 | session.setAttribute("user",user); |
- step2.如果请求涉及到需要保护的资源(即只有登录成功之后才能访问的资源,比如用户列表),则进行session验证。比如:
1 | Object obj = session.getAttribute("user"); |
九、过滤器(上)
1.比较cookie与session
cookie的优点是不占用服务器端的内存资源,
session的优点是安全,并且可以保存更丰富的数据类型。
2.验证码
1 | protected void service(HttpServletRequest request, HttpServletResponse response) |
3.过滤器
(1) 什么是过滤器?
- servlet规范当中定义的一种特殊的组件,用来拦截容器的调用过程。
- 容器收到请求之后,会调用过滤器,再调用servlet。
(2) 如何写一个过滤器?
step1.写一个java类,实现Filter接口。
step2.在doFilter方法里面,实现拦截处理逻辑。
step3.配置过滤器。(web.xml)
练习:写一个过滤器,检查评论的字数,如果超过 20个字,则提示“字数过多”。
十、过滤器(下)、监听器
(3) 过滤器的优先级
- 当有多个过滤器都满足拦截要求,则依据配置的先后顺序来执行。
(4) 初始化参数
step1.配置初始化参数。
step2.调用FilterConfig对象的 getInitParameter() 方法来获取初始化参数值。
1
2
3
4
5<!-- 配置初始化参数 -->
<init-param>
<param-name>illegal</param-name>
<param-value>猫</param-value>
</init-param>
(5) 过滤器的优点
a.可以在不修改原有代码的基础上,增加新的功能。
b.可以将多个组件相同的逻辑集中写在一个类里面(即过滤器里面),方便代码的维护。(比如session验证功能)
2.监听器
(1) 什么是监听器?
- servlet规范当中定义的一种特殊的组件,用来监听容器产生的事件并进行相应的处理。
容器主要会产生两大类事件:
a.生命周期相关的事件:
容器创建或者销毁了request,session,servlet上下文时产生的事件。
b.绑订数据相关的事件:
调用了request,session,servlet上下文的setAttribute,removeAttribute时产生的事件。
(2) servlet上下文
- 容器启动之后,会为每一个应用创建唯一的一个符合ServletContext接口要求的对象,该对象一般称之为servlet上下文。
- 特点:
- a.唯一性:一个应用对应一个servlet上下文;
- b.持久性:只要容器不关闭,应用没有被卸载,则servlet上下文会一直存在。
- 如何获取servlet上下文?
- GenericServlet、ServletConfig、FilterConfig、HttpSession都提供了一个方法:getServletContext()
- servlet上下文的作用:
- a.绑订数据(类似于request、session)
- setAttribute(String name,Object obj)
- Object getAttribute(Sting name)
- removeAttribute(String name)
- b.读取全局的初始化参数。
- a.绑订数据(类似于request、session)
注1:
request,session和servlet上下文都提供了绑订数据相关的方法,
从生存时间的长度来看:servlet上下文 > session >request;
在满足使用条件的情况下,应该优先使用生命周期短的。
注2:如果数据需要所有用户共享,应该绑订到servlet上下文对象上。
注3:同一个应用中的所有组件(servlet,filter)都可以访问的初始化参数。
- step1.配置初始化参数;
- step2.调用ServletContext提供的getInitParameter方法来读取初始化参数值。
1 | <!-- 配置全局的初始化参数 --> |
(3) 如何写一个监听器?
step1.写一个java类,实现相应的监听器接口。
注:要依据监听的事件类型来选择实现相应的接口,比如,要监听session对象的创建和销毁,可以实现HttpSessionListener接口。
step2.在接口方法当中,实现监听处理逻辑。
step3.配置监听器。(web.xml)(没有先后顺序)
统计在线人数:
1 | /** |
3.实现一个简单的web缓存 (扩展)
基本思路:
将一些常用的、不怎么发生变化,并且数据量不大的数据从数据库中查询出来,并存放到内存里面。
可以写一个监听器,实现servletContextListener接口,在上下文创建时,查询数据库,并且将查询到的数据绑订到上下文上。
step1.建表 t_role
1 | create table t_role( |
step2.实体类 Role
step3.DAO类 RoleDAO
1 | List findAll(); |
- step4.监听器类 CacheListener
1 | /* 伪代码 */ |
注意:可以绑定数据的4个作用域里分别是
pageContext、request、session、ServletContext
十一、Servlet线程安全、jsp基础、EL
1.Servlet线程安全问题
(1) 为什么说Servlet会有线程安全问题?
- a.容器只会创建一个Servlet实例。
- b.容器收到一个请求,就会启动一个线程来处理该请求,这样,就可能会有多个线程同时调用同一个对象,就有可能产生线程安全问题(比如,这些线程同时去修改某个属性)。
(2) 如何解决?
- 将有可能产生线程安全问题的代码使用synchronized加锁。
- a.使用synchronized加锁对性能会有一些影响。不要对整个方法加锁,而是对有可能产生线程安全问题的代码块加锁。
- b.尽量不要修改属性。
2.JSP基础
(1) jsp是什么?
- JSP全称Java Server Pages,是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。
(2) 如何写jsp?
step1.写一个以.jsp为后缀的文件。
step2.添加:
1) html (css,js) 直接写即可。
2) java代码。
- a.java代码片断 <% java代码 %>
- b.jsp表达式 <%= java表达式 %>
- c. jsp声明 (a1.jsp) <%! 声明变量或者方法 %>
3) 指令
a.什么是指令?
告诉容器,在将jsp转换成servlet时,做一些额外的处理,比如导包。
b.语法 <%@ 指令名 属性=值 %>
c.page指令
import属性:导包
pageEncoding属性:jsp文件的编码。
contentType属性:设置setContentType的内容。
session属性:缺省值(默认值)是true,如果值是false,则不能够使用session隐含对象。 (a2.jsp)
errorPage属性:指定一个异常处理页面。
*(注: 当jsp运行发生了异常,则容器会调用异常处理页面。(a3.jsp))*
isErrorPage属性:缺省值是false,如果值为true,则可以使用exception隐含对象。
- 使用exception.getMessage()获得错误信息。(a4.jsp)
d.include指令
告诉容器,在将jsp转换成servlet时,将file属性指定的文件的内容插入到该指令所在的位置。
e. taglib指令
jsp标签技术中,用来引入某个标签。
4) 隐含对象
a.什么是隐含对象? 可以直接使用的对象。
b.为什么可以直接使用这些隐含对象? 容器会自动添加获得这些对象的代码。
c.有哪些隐含对象?
out,request,response
session,application
exception
pageContext: 页面上下文
容器会为每一个jsp实例创建唯一的一个符合PageContext接口要求的对象,该对象会一直存在,除非jsp实例被删除。
作用1:绑订数据 (a6.jsp,a7.jsp) setAttribute、getAttribute、removeAttribute
(注:绑订到pageContext上的数据,只有对应的jsp实例能访问。)
作用2:获得其它所有隐含对象
(注:该对象提供了获得其它隐含对象的方法。)
config: 相当于ServletConfig,通过它来读取初始化参数 (a5.jsp)
page: jsp实例本身(过度设计,从来不用)
(注: jsp对应的那个servlet实例。)
5) 注释 (a8.jsp)
a. 如果注释的内容是java代码,java代码会执行,但是不会显示。
b. <%– 注释内容 –%> 如果注释的内容是java代码,不会执行。
(3) JSP是如何执行的?
step1. JSP要转换成一个servlet
- html —–> service方法里,使用out.write输出。
- <% %> —> service方法里,照搬。
- <%= %> –> service方法里,使用out.print
- <%! %> —> 增加新的属性和方法。
step2.调用servlet。
3.jsp标签和el表达式
(1) jsp标签是什么?
- jsp标签语法类似于html标签,用于替换jsp文件中的java代码。
- a.因为直接在jsp文件当中写java代码,不利于jsp文件的维护(比如,将带有java代码的jsp文件交给美工去修改就很不方便)。所以,sun才制订了jsp标签技术规范。
- b.使用jsp标签来替换java代码,一方面jsp文件会变得简洁,利于代码的维护与复用,另外一方面,也利于美工去修改。
(2) el表达式是什么?
- 一套简单的运算规则,用于给jsp标签的属性赋值,也可以脱离jsp标签直接运行。
(3) el表达式的使用
1) 访问bean的属性
补充:一个类,满足如下几个条件,就可以称之为一个javabean:
要求:
public class
有无参构造器
实现序列化接口(不是强制要求)
有一些属性
有对应的get/set方法
方式一:(e1.jsp)
- ${user.username}
- a.执行过程:容器依次从pageContext-> request -> session -> application 中查找绑订名为”user”的对象 (即getAttribute),如果找到了,则调用该对象的”getUsername”方法并输出。
- b.优点:会将null转换成””(空字符串)输出。如果找不到对应的对象,不会报空指针异常,而是输出””(空字符串)。但是不能把方法名写错,否则会报错。
- c.指定查找范围:可以使用pageScope,requestScope、sessionScope、applicationScope 指定查找范围,比如:${sessionScope.user.username}
方式二:
${user[‘username’]} 等价于 ${user.username}
[]里面允许出现绑订名,比如 ${user[str]}。找不到则输出空字符串”” ${user[requestScope.str]}
[]里面还可以出现从0开始的下标,用于访问实体类属性数组中的某个元素。例:
1
2<%request.setAttribute("str", "username");%>
username:${user[str] }
2) 做一些简单的运算 (e2.jsp)
运算的结果可以用来给jsp标签的属性赋值,也可以直接输出。
a.算术运算 +,-,*,/,%,其中”+”只能求和。不能做字符串链接,但是可以计算”1”+”1”(结果为2)
b.关系运算 >,>=,<,<=,==,!=(结果为true或false)
==可以用来判断字符串是否相同。如果不指定查找范围就会依次查找。
c.逻辑运算 &&,||,!
d.empty运算 empty:判断是否为一个空字符串或者是否为一个空的集合。例:
1
2
3
4
5
6
7<%
request.setAttribute("s2", "");
%>
空字符串:${empty s2 }<br/><!-- true -->
空的集合:${empty list1 }<br/><!-- true -->
值为null:${empty null}<br/><!-- true -->
找不到对应的值:${empty abc }<br/><!-- true -->
十二、JSTL、自定义标签
3) 读取请求参数值 (e1.jsp)
- ${param.username} 等价于 request.getParameter(“username”);
- ${paramValues.interest} 等价于 request.getParameterValues(“interest”);
jstl (java standard tag lib 即JAVA标准标签库)
(1)jstl是什么?
- apache开发的一套通用的jsp标签,后来捐献给了sun,sun将其命名为jstl。
(2) 如何使用jstl?
step1.导入jstl相关的jar包。
step2.使用taglib指令引入要使用的jsp标签。
uri属性:指定要引入的jsp标签的命名空间。
- 命名空间:为了区分同名的元素而在元素前添加的一个限定(通常是一个域名)。
prefix属性:命名空间的别名。
(3) 几个核心标签
1) if标签(e3.jsp)
1 | <c:if test="" var="" scope="">标签体</c:if> |
- 当
test属性
值为true时,容器会执行标签体的内容。 test属性
,可以使用el表达式赋值来作为绑定值。var属性
,用来指定绑订名。scope属性
用来指定绑订范围(”page”,”request”,”session”,”application”)。例:
1 | 性别: |
2) choose标签 (e4.jsp)
1 | <c:choose> |
- when可以出现1次或者多次,表示一个分支;
- 当test属性值为true时,执行标签体的内容;
- otherwise可以出现0次或者1次,表示例外。
注意:这两个标签不能独立使用,必须要嵌套在choose标签中才有意义!
例:
1 | <c:choose> |
3) forEach标签 (e5.jsp)
用于遍历集合或者数组。
a.语法:
1
2<c:forEach items="" var="" varStatus="">
</c:forEach>b.items属性用来指定要遍历的集合或者数组,可以使用el表达式来赋值。
c.var属性用来指定绑订名(绑订范围固定是pageContext,该标签每次从集合或者数组当中取一个元素,然后绑订到pageContext上)。
d.varStatus属性也是用来指定绑定名(绑订范围固定是pageContext,绑订值是一个特殊对象,该对象由该标签创建,提供了几个方法用来获得当前遍历的状态,比如,
- getIndex():获得当前正在被遍历的元素的下标(从0开始)
- getCount():获得当前是第几次被遍历(从1开始)例:
1 | <c:forEach items="${users }" var="u" varStatus="s"> |
补充:容器依据标签的命名空间找到标签的描述文件(.tld文件),然后依据标签的名称找到对应的标签类,然后将该类实例化,并且调用该实例对应的方法。
注意:如果是更改项目名,要记得右键项目名称,然后更改web project settings的值为相应的项目名称。这个web project settings是你访问服务器上你部署的应用的应用名默认情况下 不指定这个的话应用名和你的工程名是一样的指定了之后 就是以这个名字为准了。
2.自定义标签
(1) 编程步骤
step1.写一个java类,继承SimpleTagSupport类。
- 简单标签技术(new),继承SimpleTagSupport类来开发。
- 复杂标签技术(old)
step2.override doTag方法,在该方法里面,编写处理逻辑。
step3.描述标签(.tld文件)
1
<body-content>JSP</body-content>
- body-content的值可以是
empty
、scriptless
、JSP
。 empty
: 该标签没有标签体。scriptless
: 该标签可以有标签体,但是标签体的内容不能够出现java代码(<% %>,<%= %>,<%! %>)JSP
:该标签可以有标签体,并且标签体可以出现java代码。只有复杂标签技术才支持该值。
- body-content的值可以是
案例:
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/**
* 自定义标签,用来输出系统的当前时间
*
* @author ACGkaka
*/
public class SysdateTag extends SimpleTagSupport {
//设置默认格式
private String format="yyyy/MM/dd HH:mm:ss";
//设置format的get方法
public String getFormat() {
return this.format;
}
//设置format的set方法
public void setFormat(String format) {
this.format=format;
}
//重写doTag方法来实现具体的功能
public void doTag() throws JspException, IOException {
//创建服务器时间
Date date=new Date();
//格式化时间
SimpleDateFormat sdf=new SimpleDateFormat(format);
String now=sdf.format(date);
//将时间输出给浏览器
//PageContext extends JspContext
//该方法声明返回JspContext
//但在实现时返回的是PageContext
//所以可以将其强转为PageContext
//从而获得其他8个隐含对象
PageContext ctx=(PageContext)getJspContext();
JspWriter out=ctx.getOut();
out.println(now);
//注意:此处一定不能关闭流,因为其他的标签
//也要用这个流
super.doTag();
}
}s.tld文件:
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<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<description>这是我自己的标签库</description>
<display-name>My tag</display-name>
<tlib-version>3.1</tlib-version>
<short-name>s</short-name>
<uri>/my-tag</uri>
<tag>
<description>用来输出服务器的时间</description>
<name>sysdate</name>
<tag-class>web.SysdateTag</tag-class>
<!-- 声明该标签可以包含哪些内容 -->
<!-- empty: 该标签没有标签体。
scriptless: 该标签可以有标签体,但是标签体
的内容不能够出现java代码(<% %>,
<%= %>,<%! %>)。
JSP:该标签可以有标签体,并且标签体可以出现
java代码。只有复杂标签技术才支持该值
-->
<body-content>empty</body-content>
<!-- 以下是标签的属性设置 -->
<attribute>
<description>用来设置时间的格式</description>
<name>format</name>
<!-- 设置是否必须给这个属性赋值 -->
<required>false</required>
<!-- 设置是否可以用EL表达式给次属性赋值 -->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>应用:
1
2<%@ taglib uri="/my-tag" prefix="s" %>
<s:sysdate format="yyyy-MM-dd" />
十三、MVC
1.MVC (Model 模型,View 视图,Controller 控制器)
(1) MVC模式
- 是一个经典的设计模式,是软件的分层思想:
- M:Model,即业务层,用来处理业务;
- V:View,即视图层,用来处理数据;
- C:Controller,即控制层,是业务层和视图层的桥梁。
- 它可以降低软件中代码的耦合度,便于团队开发及维护。
(2) 如何使用MVC?
- 在web开发当中,我们经常使用Servlet充当控制器,jsp充当视图,java充当模型。它们的关系如下图所示:
(3) MVC的优点
a.模型返回的处理结果,可以使用不同的视图来展现。
b.方便测试
- 比如将业务逻辑写在java类里面,可以直接测试。
- 如果写在Servlet里面,需要部署之后才能测试。
c.方便代码的维护,修改模型或者视图,彼此不受影响。
注意:
使用MVC,会增加代码量,会增加软件的成本,也会增加设计的难度。所以,只有一定规模的软件才需要使用MVC。
案例:
NETCTOSS–中国电信运营支持系统-网络版
注意:浏览器访问服务器获得网页,以及加载网页的过程中包含多次请求
2.实现一个简单的MVC框架
(1) 基本架构
十四、smartmvc
1.smartmvc架构
2.使用smartmvc
step1.导包 dom4j
step2.将base包下面的common,web包拷贝到工程里面。(这两个包是smartmvc的核心包)。
step3.配置DispatcherServlet。
(注:主要是指定配置文件的位置。)
step4.添加处理器类,比如,添加LoginController
a.方法前添加@RequestMapping。
b.方法的返回值是一个字符串(即视图名,默认会转发),如果是重定向,前面要加”redirect:”。
step5.在配置文件(比如context.xml)中添加处理器的配置信息。
(注:要写完整的类名。)
step6.添加jsp文件。
- a.要添加到WEB-INF下。
- b.文件名要等于视图名。
- 本文作者: ACG kaka
- 本文链接: http://acgkaka.github.io/2020/12/04/9Java入门(九)Servlet JSP MVC/
- 版权声明: 文章均为个人整理,如有侵权,请联系删除。