Java Web 学习笔记:Servlet/JSP 核心原理与避坑指南
整理这份笔记的初衷很简单:
我在学习 Java Web 时,发现很多教程要么只讲理论,要么直接上 Spring 框架,对 Servlet/JSP 这些底层机制的解释总是一笔带过。
实际开发中遇到这些问题:
明明配置了 web.xml,但 Filter 就是不生效
分不清 forward() 和 sendRedirect() 的使用场景
Session 数据偶尔丢失却找不到原因
于是决定边学边记录,用最直白的语言+图示梳理核心知识点。
概念:Java Web,是用Java技术来解决相关web互联网领域的技术总和。web包括:web服务器和web客户端两部分。JavaWeb 是基于请求和响应来开发的。
一、servlet
概念:Servlet 是运行在服务器上的一个 java 小程序,它可以接收客户端发送过来的请求,并响应数据给客户端
1、开发流程
1、创建javaweb项目,同时将tomcat添加为当前项目的依赖·
2、重写service方法,service(HttpServletRequest req,HttpservletResponce resp)
3、在service方法中,定义处理代码
4、在web.xml中,配置Servlet 对应的请求映射路径
<servlet-name>
提供的是名字,没有<servlet-mapping>
会报错
<servlet-class>
提供的是文件所存储的位置
<url-pattern>
设置的是在jsp或者html中读取时所选择的位置,可以自由设置,但必须加/
<url-pattern>
可以书写多中不同的匹配路径,但是不能够有相同的名字
重点:url的路径可以进行模糊匹配,而多个路径匹配成功
'*'
进行模糊匹配的,不包括jsp
'/*'
包括jsp
/a/*
前面的路径为a,后面为模糊匹配
*.action
前面为模糊匹配,后面为action
5、写class类时最好是固定格式
作用是:将头文件的类型设置为HTML的格式
可以通过更改这项代码设置展示的格式
6、XML的功能可以通过注释来进行实现
@WebServlet("/service")
"/service"相当于value,通过/可以进行省略不写,或者写成urlpattern=“service”,两个值是差不多的,也可以添加name名字属性
2、生命周期
1 、实例化 构造器的建立 1次
2、初始化(init) 建立完成后使用 1次
3、接受请求,处理请求(service) 每次请求使用 无数次
4、销毁(destroy) 关闭服务 1次
xml或者注释设置服务器的时候,要进行服务器的先后进行设置
在xml中
通过load-on-startup设置,一般是设置为6及之后,以为在服务器自带的xml中已经占用了前面几个的进行了
在注释中
3、一些接口的使用
Servlet接口
GenericServlet抽象类:将Servlet接口中其他的方法做了默认空实现,只将service()方法作了抽象,将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
HttpServlet抽象类:对http协议的一种封装,简化操作
4、HttpServlet
里面主要运用到了doget和dopost两种方法,在表单提交时选择get则采取doget方法,选择post则采取dopost方法
当一个类继承HttpServlet时要么重写service,要么采用doget,dopost进行重写
可以主动设置报错
通过request获取路径
二进制的格式
设置状态,跟sendError效果相识
设置文件的长度以及格式
5、ServletConfig类
概念: Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象,是Servlet 程序的配置信息类,主要用于获取Servlet的配置信息
在xml中进行数据的存储
不能跟loadonstartup同时存在service中
在类当中的使用
上面部分适用于知道名字,下面部分适合输出所有的姓名
ServletConfig只适用于在一个类当中进行使用,不具有跨服务器的功能,更推荐使用ServletContext
通过注解可以代替xml中的使用
6、ServletContext类
注意:
ServletContext 是一个接口,它表示 Servlet 上下文对象,代表整个Web应用。
一个 web 工程,只有一个 ServletContext 对象实例。
ServletContext 对象是一个域对象。
ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。
ServletContext
代表整个 Web 应用的上下文,而 ServletConfig
仅代表单个 Servlet 的配置。两者均可通过 getInitParameter()
获取参数,但作用范围不同
可以定义在servlet以外的地方
在类当中的使用
获取名字可以采取跟之前config一样的方法
获得真实路径的方法,以及设置context值
context可以跨软件进行传输,运用的范围更加的广泛
二、请求转发和重定向
1、请求转发
概念:通过request来进行实现,请求转发的路径不会发生改变,还是原路径,请求转发过程中存储的内容不会发生改变
请求转发可以访问WEB-INF中的内容,请求转发只能访问当前服务器下的内容
请求转发是服务器内部的行为,屏蔽了客户端
2、重定向
概念:通过response来进行实现,请求转发的过程中路径会发生改变,存储当中的内容不能进行转发
请求转发是客户端两次请求下实现的
请求转发不能访问WEB-INF当中的内容
一般可以的情况,优先选择重定向
三、路径问题
1、相对路径:
以当前资源的所在路径为出发点去找目标资源
语法:不以/开头
./表示当前资源的路径
…/表示当前资源的上一次路径
2、绝对路径:
始终以固定的路径作为出发点去寻找目标资源,和当前资源所在的路径没有关系
语法:以/开头
不同的项目中,固定路径的出发点可能不同
3、其他方法:
当前页面中,所有不加任何修饰的相对路径前会自动补充href中的内容
四、会话技术
1、Cookie
概念:
客户端会话技术,将数据保存到客户端中,每个Cookie的大小不能超过4kb
创建以及添加Cookie
获得传输的Cookie
可以设置存在的时间
以秒为单位
作用:一般用于存在少量的不太敏感的数据在不登陆的情况下,完成服务器对客户端的身份识别
2、Session
服务器对话技术,可以存储一次会话的多次请求,将数据存储在服务器对象中
设置存储值
取值
设置存在时间
不设置时间默认为30分钟
3、context
概念:可以跨服务器进行操作,是操作范围最大的一个
设置存储值
取值
三个会话对象的区别:
范围以小到大依次进行排序
session没有数据大小限制,cookie有
Cookie 默认不安全,但可通过设置 HttpOnly
、Secure
等属性提升安全性。敏感数据(如用户ID)应优先使用 Session。
五、JSP
1、脚本元素
<%代码%>代码脚本:在JSP页面中,编写我们需要的功能
<%!代码%>声明脚本:适用于在JSP页面中定义成员变量和方法
<%=代码%>表达式脚本:用于输出作用
2、指令元素
1、page指令
概念:用于设置JSP页面的属性和相关的功能
eg:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
常用的属性值
Import:导包。
ErrorPage:当前页面发生异常后,会自动跳转到指定的错误页面。
IsErrorPage:标识当前页面是否是错误页面。
True:是,可以使用内置对象exception
False:否,默认值,不可以使用内置对象exception
2、include指令
概念:在JSP编译时,插入一个包含文本或代码的文件,这个包含的过程是静态的,而包含的文件可以是JSP网页、HTML网页、文本文件,或是一段Java程序,为静态包含。
格式:
<%@ include file="list.jsp"%>
动态指令:
<jsp:include page="/include/footer.jsp">
<jsp:param name="username" value="bbj"/>
<jsp:param name="password" value="root"/>
</jsp:include>
jsp:include是动态包含,而<%@ include%>是静态包含。
<jsp:include page=“file” flush=“true” />它总是会检查所含文件中的变化。
动态包含可以给被包含的页面传递参数,静态包含不可以。
动态包含的地址可以是变量,静态不可以为一个变量。
3、taglib指令
概念:
声明和使用自定义标签库(Tag Library)的一种方式
语法格式:
<%@ taglib uri="标签库的URI" prefix="前缀" %>
3、EL表达式
概念:替换和简化Jsp页面中java代码的编码,能够自动转换类型(有11个隐式对象)
${变量名}
方法调用:
对象名.方法名(参数)
使用场景:
-
访问作用域变量:EL表达式可以访问JSP的四种作用域(page, request, session, application)中的变量。
${sessionScope.userName}
-
访问对象属性:可以直接通过点(
.
)操作符访问对象的属性。${user.name}
-
调用方法:可以调用对象的方法,并且可以传递参数。
${user.getFullName()}
-
使用隐式对象:JSP定义了一些隐式对象,如
param
,paramValues
,header
,headerValues
等,它们可以通过EL表达式直接访问。${param.userId}
-
使用运算符:可以进行算术运算和逻辑运算。
${1 + 1} ${user.isLoggedIn && user.hasPermission}
-
使用EL函数:JSP EL提供了一些内置函数,如
fn:length()
,fn:substring()
等。复制 ${fn:length(list)}
4、JSTL标签
使用方式:
- 导入jstl相关的jar包
- 引入标签库:taglib指令:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
,然后在页面中使用。
常用的标签库:
if:相当于java代码中的if语句
- 属性:test 必须属性,接收boolean表达式
- 如果表达式为true,则显示if标签体内容;如果表达式为false,则不显示标签体内容
- 一般情况下,test属性值会结合el表达式一起使用
choose:相当于java代码中的switch语句
- 使用choose标签取出数字,相当于switch声明
- 使用when标签做数字判断,相当于case
- otherwise标签做其他情况的声明,相当于default
foreach: 相当于java中的for语句
- items:被迭代的集合对象
- var:用来存放迭代得到的成员
- begin:开始的位置
- end:结束的位置
- step:步长
- varStatus:用来存放现在指到得相关成员信息
formatDate:格式化标签,它允许页面对日期进行格式化操作
- value:要格式化的对象
- type:格式的类型
- pattern:自定义格式化类型
eg:
<c:if test="${page.currentPage > 1}">
<li class="page-item">
<a class="page-link" href="/findAllByPage?currentPage=${page.currentPage - 1}&pageSize=${page.pageSize}&bookid=${conditions.bookid[0]}&bookname=${conditions.bookname[0]}&bookauthor=${conditions.bookauthor[0]}&startNum=${conditions.startNum[0]}&endNum=${conditions.endNum[0]}&startPrice=${conditions.startPrice[0]}&endPrice=${conditions.endPrice[0]}" tabindex="-1" aria-disabled="true">上一页</a>
</li>
</c:if>
<c:forEach var="p" begin="1" end="${page.totalPages}" varStatus="index">
<c:choose>
<c:when test="${page.currentPage == index.count}">
<li class="page-item active" aria-current="page">
<a class="page-link" href="/findAllByPage?currentPage=${index.count}&pageSize=${page.pageSize}&bookid=${conditions.bookid[0]}&bookname=${conditions.bookname[0]}&bookauthor=${conditions.bookauthor[0]}&startNum=${conditions.startNum[0]}&endNum=${conditions.endNum[0]}&startPrice=${conditions.startPrice[0]}&endPrice=${conditions.endPrice[0]}">${index.count}</a>
</li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link" href="/findAllByPage?currentPage=${index.count}&pageSize=${page.pageSize}&bookid=${conditions.bookid[0]}&bookname=${conditions.bookname[0]}&bookauthor=${conditions.bookauthor[0]}&startNum=${conditions.startNum[0]}&endNum=${conditions.endNum[0]}&startPrice=${conditions.startPrice[0]}&endPrice=${conditions.endPrice[0]}">${index.count}</a></li>
</c:otherwise>
</c:choose>
</c:forEach>
<c:if test="${page.currentPage < page.totalPages}">
<li class="page-item">
<a class="page-link" href="/findAllByPage?currentPage=${page.currentPage + 1}&pageSize=${page.pageSize}&bookid=${conditions.bookid[0]}&bookname=${conditions.bookname[0]}&bookauthor=${conditions.bookauthor[0]}&startNum=${conditions.startNum[0]}&endNum=${conditions.endNum[0]}&startPrice=${conditions.startPrice[0]}&endPrice=${conditions.endPrice[0]}">下一页</a>
</li>
</c:if>
当前第${page.currentPage}页,共${page.totalPages}页,共${page.totalCount}条记录
<c:forEach varStatus="s" items="${page.list}" var="book">
<tr>
<td>${s.count}</td>
<td>${book.bookname}</td>
<td>${book.booknum}</td>
<td>${book.bookprice}</td>
<td>${book.bookauthor}</td>
<td><fmt:formatDate value="${book.bookpublish}" pattern="yyyy-MM-dd"/></td>
<td>${book.typename}</td>
<td>
<a href="/updateBookServlet?currentPage=${page.currentPage - 1}&pageSize=${page.pageSize}&id=${book.bookid}">修改</a>
<a href="${book.bookid}">删除</a>
</td>
</tr>
</c:forEach>
六、Fiffter
概念:截取用户端的请求与响应信息,并对这些信息进行过滤,当需要限制用户访问某些资源或者在处理请求时提前处理某些资源的时候,就可以使用过滤器完成
在xml中的实现
跟servlet的设置方法很相似
使用的时候要实现filterChain的doFilter方法
执行的顺序:
Filter 执行顺序默认由 web.xml
中 <filter-mapping>
的声明顺序决定。若使用注解配置,顺序可能不可控,建议通过 web.xml
显式管理
如果这篇笔记帮你解决了某个困惑,不妨点个赞👍鼓励一下~