JavaWeb
1、Servlet工作原理:
先 localhost:8080/javaweb-01/zhang 请求发过来
服务器接收到请求后,会解析请求的URL路径,访问Servlet的资源路径。
根据这个路径 localhost:8080 找到正在运行的tomcat容器↓
根据idea中tomcat给当前项目配置的虚拟目录 javaweb-01 找部署在tomcat中对应的项目↓
//下面需要打开Servlet项目的web.xml文件对比解读
如果没有虚拟路径或者默认为 / ,跳过上一步直接去找项目下的 web.xml 查找遍历是否有 /zhang 对应的<servlet-mapping>里的<url-pattern>配置,再根据这个配置的同级配置<servlet-name>去找映射的<servlet>配置,最后根据<servlet>下的<servlet-class>里的全类名对应的字节码文件加载到内存中: Class cls= Class.forName(全类名),再创建对象: HelloServlet hs= cls.newInstance(),最后调用service()方法:hs.service。 因为遵守了Servlet的规则,所以会自动调用service()方法↓
最终service方法执行,由开发人员事先实现的操作来处理请求消息和返回内容。
Servlet生命周期:servlet在用户第一次请求时创建,直到服务器关闭才销毁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xydfQXGF-1624634278975)(D:\有道\Text\weixinobU7VjuPiULJulDY7dzNgusLPRY8\4ad93a3487104ece83b8819a52de829f\servlet执行原理.png)]
得出的结论:
Servlet依赖于toncat容器,容器帮Servlet实现方法的调用
xml文件中的<servlet-mapping>中的路径优先级为 单指>多指>/*
ServletContext(上下文/环境共享数据)(后期使用session代替):
ServletContext(上下文),可以实现数据共享,一个Servlet通过创建实例存入一些数据或者节点信息之后,所有的Servlet都可以通过创建ServletContext的实例来拿到这些节点信息
注:存入数据以k-v的方式
ServletContext的方法:
首先 ://获取context对象 :ServletContext context = this.getServletContext();
获取web应用的初始化参数
String gp = context.getInitParameter("url");
//返回的结果:
"jdbc:mysql://localhost:3306:mybatis"
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306:mybatis</param-value>
</context-param>
请求转发(后期使用request代替)
//请求转发:传一个目标路径,然后 forward转发把request和response的参数传进去
//这里起到的只是将请求转发的作用
context.getRequestDispatcher("/contexTest").forward(req,resp);
<!--转发的url-->
<servlet>
<servlet-name>contexTest</servlet-name>
<servlet-class>com.zsh.servlet.ReaderContext</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>contexTest</servlet-name>
<url-pattern>/context</url-pattern>
</servlet-mapping>
解释一下转发和重定向的大概意思
假设有 A(客户端) B(1号服务器) C(2号服务器) 这样的关系
转发:
A想去拿一个资源,这个资源只有C有,但是A不知道有C的存在,也就是不知道C有资源和如何访问到C,只能面向B。
B接收到请求之后B做了一件事,把A需要的资源请求转发到C,C接收到请求之后返回资源给B。
B再将资源发送给A,A从头到尾都不知道C的存在
重定向:
A想去拿一个资源,这个资源只有C有,但是A不知道有C的存在,也就是不知道C有资源和如何访问到C,只能面向B。
B接收到请求之后B做了一件事,把A请求的目标变为C,去访问C可以拿到这个资源。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28IcfCMg-1624634278977)(C:\Users\Administrator\Pictures\QQ浏览器截图\请求和重定向.PNG)]
读取资源文件(Properties)(也不会常用这种方式,代替的方式有 类加载、反射机制等待其他技术)
1)、在java类路径下新建properties
2)、在resources目录下新建properties
发现:每次都被打包到了同一个路径下classes,这个路径称为类路径:classpath
思路:需要一个文件流去读取路径下的文件
//返回InputStream字节输入流
//参数为Resources下的资源路径 \代表当前target下的web应用
//给一个文件流
InputStream stream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
//通过new Properties类来操作 Properties资源文件
Properties prop = new Properties();
prop.load(stream);
//getkey返回value
String s = prop.getProperty("username");
String s2 = prop.getProperty("password");
2、HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象
和一个代表响应的HttpServletResponse;
如果要获取客户端请求过来的参数:找HttpServletRequest
如果要给客户端响应一些信息:找HttpServletResponse
简单分类
负责向浏览器发送数据的方法
//实现父接口获得的方法
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
//实现父接口获得的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
//自己类中的方法
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
void setStatus(int var1);
响应的状态码
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
//200 = ok
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
//3xx = 请求重定向
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
//400 = 错误的请求
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
//404 = 未找到
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
//500 = 内部服务器错误
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
//502 = 网关错误
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
//504 = 网关超时
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
下载文件
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//- 获取下载文件的路径 直接使用绝对路径 写死了??
String realPath = "D:\\ideaProject\\az\\javaweb-02-servlet\\httpresponse\\target\\classes\\彭于晏.jpg";
System.out.println("----->"+realPath);
//- 定义下载的文件名 记住:一个资源路径的最后一位/后面的字符就是请求文件的文件名
//这行代码的意思是:截取获得的绝对路径最后的 / 后面的字符
String filename = realPath.substring(realPath.lastIndexOf("/") + 1);
//- 设置浏览器支持我下载需要的资源 修改字符编码,防止中文名文件下载时出现乱码 也不一定解决
resp.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(filename,"UTF-8"));
//- 获取下载文件的输入流 把文件加载到流中
FileInputStream in = new FileInputStream(realPath);
//- 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
//- 获取response的OutputStream对象
ServletOutputStream out = resp.getOutputStream();
//- 将FileOutputStream流写入到buffer缓冲区
//- 使用OutputStream将缓冲区的数据输出到客户端
while ((len = in.read(buffer))> 0){
out.write(buffer,0,len);
}
//- 关闭流
out.close();
in.close();
}
//再去web.xml文件补充servlet信息和映射
//访问下载
//成功!
验证码功能
验证怎么来的?
-
前端实现
-
后端实现
这种方法以后基本碰不到,了解一下代码格式流程,大概意思就好
第一步:需要用到java的图片类,产生一个图片
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //让浏览器3秒刷新一次 resp.setHeader("refresh","3"); //在内存中创建一个图片 //参数为:宽 高 颜色类型 BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB); //拿到这张图片,以2d的格式 Graphics2D graphics = (Graphics2D) image.getGraphics(); //设置这张图片填充的背景颜色 graphics.setBackground(Color.WHITE); //填充位置 参数:坐标 宽 高 graphics.setClip(0,0,80,20); //字体颜色 graphics.setColor(Color.red); //设置字体样式 第一个节点为null,字体类型,大小 graphics.setFont(new Font(null,Font.BOLD,20)); //给图片写入随机数,坐标 graphics.drawString(makeRandom(),0,20); //告诉浏览器,这个请求用图片打开 resp.setContentType("image/png"); //网站存在缓存,不让浏览器缓存 这些都是缓存策略 resp.setDateHeader("expires",-1); resp.setHeader("Cache-Control","no-cache"); resp.setHeader("Pragma","no-cache"); //把图片写到浏览器 ImageIO.write(image,"png",resp.getOutputStream()); }
第二步:创建一个随机数类
//生成随机数作为验证码 private String makeRandom(){ //先获取随机数的对象 Random random = new Random(); //创建指定范围的随机数,加一个空串让这个随机数变为String类型 String num = random.nextInt(999999) + ""; //防止出现低于六位数的随机数,计算得到的随机数需要补几个 0 //i < 6-s.length() 意思是如果拿到的随机数不够六位的 也就是 > 6那就进入循环 sb添加进去一个零,与6相比随机数的length差n<=位就会进入n<6次循环,补的0就有n<=6个 StringBuffer sb = new StringBuffer(); for (int i = 0; i < 6-num.length() ; i++) { sb.append("0"); } num = num + sb.toString(); return num; }
String random = drawRandomNum((Graphics2D) g,“l”);//生成纯字母的验证码
Response重定向
//需要使用的方法
void sendRedirect(String var1) throws IOException;
//如果这个servlet被访问就会重定向到 /yzm
//记得加上tomcat中设置的项目路径
resp.sendRedirect("/rp/yzm");
/*
剖析一下原理,其实就做了两步
*/
//跳转的路径
resp.setHeader("Location","/rp/yzm");
//状态码,3xx = 请求重定向
resp.setStatus(302);
面试题
请你聊聊重定向和请求转发的区别
相同点:都会实现页面跳转
不同点:
请求转发的时候,url不会发生变化 header状态码:307
重定向的时候,url地址栏会发生变化 header状态码:302
3、HttpServletRequest
HttpServletRequest代表用户端的请求,用户通过http协议访问服务器
转发不需要虚拟项目路径,重定向需要
获取前端请求参数,并请求转发
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求和响应的编码格式,保证不乱码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
//单个接收前端发送的请求
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbies = req.getParameterValues("hobbies");
//控制台打印
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbies));
//转发到成功页面
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
4、cookie,session
逻辑和原理
由于HTTP是一种无状态协议,服务器没有办法单单从网络连接上面知道访问者的身份,为了解决这个问题,就诞生了Cookie
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie
客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,
以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
实际就是颁发一个通行证,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理
cookie 可以让服务端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果 Cookie 很多,这无形地增加了客户端与服务端的数据传输量,
而 Session 的出现正是为了解决这个问题。同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只要传回一个 ID,这个 ID 是客户端第一次访问服务器的时候生成的, 而且每个客户端是唯一的。这样每个客户端就有了一个唯一的 ID,客户端只要传回这个 ID 就行了,这个 ID 通常是 NANE 为JSESIONID 的一个 Cookie。
源码和工作原理剖析
cookie的内容主要包括name(名字)、value(值)、maxAge(失效时间)、path(路径),domain(域)和secure
name:cookie的名字,一旦创建,名称不可更改。
value:cookie的值,如果值为Unicode字符,需要为字符编码。如果为二进制数据,则需要使用BASE64编码.
maxAge:cookie失效时间,单位秒。如果为正数,则该cookie在maxAge后失效。如果为负数,该cookie为临时cookie,关闭浏览器即失效, 浏览器也不会以任何形式保存该cookie。如果为0,表示删除该cookie。默认为-1
path:该cookie的使用路径。如果设置为"/sessionWeb/",则只有ContextPath为“/sessionWeb/”的程序可以访问该cookie。如果设置为“/”,则本域名下ContextPath都可以访问该cookie。
domain:域.可以访问该Cookie的域名。第一个字符必须为".",如果设置为".google.com",则所有以"google.com结尾的域名都可以访问该cookie",如果不设置,则为所有域名
secure:该cookie是否仅被使用安全协议传输。
Session机制
Session机制是一种服务端的机制,服务器使用一种类似散列表的结构来保存信息。
当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端里的请求里是否已包含了一个session标识--sessionID,
如果已经包含一个sessionID,则说明以前已经为此客户端创建过session,服务器就按照sessionID把这个session检索出来使用
如果客户端请求不包含sessionID,则为此客户端创建一个session并且声称一个与此session相关联的sessionID,
sessionID的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串(服务器会自动创建),这个sessionID将被在本次响应中返回给客户端保存。
两种技术的优弊端or解决方案
使用cookie的缺点
如果浏览器使用的是cookie,那么所有的数据都保存在浏览器端,
cookie可以被用户禁止
cookie不安全(对于敏感数据,需要加密)
cookie只能保存少量的数据(大约是4k),cookie的数量也有限制(大约是几百个),不同浏览器设置不一样,反正都不多
cookie只能保存字符串
对服务器压力小
使用session的缺点
一般是寄生在Cookie下的,当Cookie被禁止,Session也被禁止
当然可以通过url重写来摆脱cookie
当用户访问量很大时,对服务器压力大
我们现在知道session是将用户信息储存在服务器上面,如果访问服务器的用户越来越多,那么服务器上面的session也越来越多, session会对服务器造成压力,影响服务器的负载.如果Session内容过于复杂,当大量客户访问服务器时还可能会导致内存溢出。
用户信息丢失, 或者说用户访问的不是这台服务器的情况下,就会出现数据库丢失.
session销毁
手动销毁
某个对象.getSession.invalidate();
自动销毁
<!--在xml配置session的销毁时间-->
<session-config>
<session-timeout>1</session-timeout> //分钟
</session-config>
cookie和session的区别
具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到, 由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的
cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session
session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie
单个cookie保存的数据不能超过4k,很多浏览器都限制一个站点最多保存20个cookie。
可以将登陆信息等重要信息存放为session。
cookie是把用户的数据写给用户浏览器,浏览器保存(可以多个)
session是把用户的数据写到用户独占的session中,服务端保存(只保存重要的信息,减少资源的浪费),且对象由服务器创建
session-config
session配置
<session-config>
<cookie-config></cookie-config> cookie配置
<session-timeout></session-timeout> session超时时间,多久之后过期,单位:分钟
<tracking-mode></tracking-mode>
</session-config>
5、JSP(Java Server Pages)
jsp : Java服务端页面,也和servlet一样,用于动态web技术!
最大的特点:
- 写JSP就像在写HTML
- 区别
- HTML只给用户提供静态的数据
- JSP中可以嵌入Java代码,为用户提供动态数据
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问servlet
jsp最终也会被转换为一个java类
jsp本质上就是一个servlet
这是jsp.java中的方法
//初始化
public void _jspInit(){
}
//销毁
public void _jspDestroy(){
}
//JSPService
public void _jspService(.HttpServletRequest req,HttpServletResponse resp){
这个方法中先判断了请求是否错误,然后判断请求为post还是get
}
内置了一些对象
final javax.servlet.jsp.PageContext pageContext; //页面上下文
javax.servlet.http.HttpSession session = null; //会话
final javax.servlet.ServletContext application; //applicationContext
final javax.servlet.ServletConfig config; //配置
javax.servlet.jsp.JspWriter out = null; //写出
final java.lang.Object page = this; //page = this:代表当前页面
HttpServletRequest req //请求
HttpServletResponse resp //响应
//后面还有两个不需要了
输出页面前的代码
response.setContentType("text/html"); //定义响应的页面类型
pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext(); //application 本质上就是 ServletContext
config = pageContext.getServletConfig(); //获取配置对象
session = pageContext.getSession(); //会话对象
out = pageContext.getOut(); //写出对象
_jspx_out = out;
//以上的对象,在JSP中可以直接使用
访问JSP的整个过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b4FBiTIN-1624634278978)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210331084112199.png)]
JSP页面中:
只要是嵌入在<%这里的代码%>,直接原封不动的用out.write()写到页面
只要是HTML代码,就会转换为out.write(“字符串,这里会自动识别是否需要换行”)写到页面
JSP基础语法
先导入需要的依赖
<dependencies>
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!--jsp依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!--JSTL表达式依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!--jsp标签库-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
JSP注释:<%–在这写–%>
JSP表达式:<%= 变量或者表达式%>
<%里面写的Java代码可以分段插入html代码 比如:
for(int i = 0;i<100;i++){
out.write("111");
%>
<h1>
你好
</h1>
<%
out.write("2222");
}
%>
JSP声明:
<%!在这写%>
<%!
写函数的地方,这里写的代码会被编译到类中,其他的会被编译到_jspService方法中
作用域更大,可以写全局方法、变量和静态代码块
%>
EL表达式:
JSP的注释不会在客户端显示
JSP指令
<%注意%>
这个脚本文件里写的代码片段一定要是Java代码,注释也是,不能出现jsp,html的代码和注释。
因为这个代码片段会被编译成java代码,其他语言的代码在这个片段里被编译之后无法转换为java语法的代码,
定制错误跳转页面
<%--访问到这个页面的时候,错误代码为500时就会跳转到 "error/500.jsp"--%>
<%@ page errorPage="error/500.jsp" %>
<%--显示的声明这是一个错误页面%>
<%@ page isErrorPage="true" %>
xml文件中配置访问错误映射
<error-page>
<!--接收什么错误代码-->
<error-code>404</error-code>
<!--异常类型-->
<exception-type></exception-type>
<!--接收到错误之后跳转的位置-->
<location>/error/500.jsp</location>
</error-page>
9大内置对象
- PageContext 存东西
- Request 存东西
- Response
- Session 存东西
- Application 【ServletContext】 存东西
- Config 【ServletConfig】
- out
- Page 不用
- exception
内置对象的作用域及生命周期
<%--内置对象--%>
<%
pageContext.setAttribute("name1","小张一号");//保存的数据只在一个页面中有效
request.setAttribute("name2","小张二号");//保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","小张三号");//保存的数据只在一次会话中有效,生命周期在打开浏览器到关闭浏览器
application.setAttribute("name4","小张四号");//保存的数据在服务器中有效,生命周期从打开服务器到关闭服务器
%>
<%
//从底层到高层(作用域)去查找:Page-->Request--->Session--->Application
String name1 = (String) pageContext.findAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");
String name5 = (String) pageContext.findAttribute("name5");//没有这个数据
%>
<%--使用EL表达式输出--%>
<h1>${name1}</h1>
<h1>${name2}</h1>
<h1>${name3}</h1>
<h1>${name4}</h1>
<%--这个不存的数据用两种方式输出--%>
<%--输出结果:什么也没有,空--%>
<h1>${name5}</h1>
<%--输出结果:NULL--%>
<h1><%=name5%></h1>
开启另一个会话再访问(启动另一个浏览器窗口)
<%
String name1 = (String) pageContext.findAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");
String name5 = (String) pageContext.findAttribute("name5");//没有这个数据
%>
<h1>${name1}</h1>
<h1>${name2}</h1>
<%--打开新的标签页,取出;打开新的浏览器窗口之后找不带该元素,结果为空--%>
<h1>${name3}</h1>
<%--取出--%>
<h1>${name4}</h1>
<%--空--%>
<h1>${name5}</h1>
<%--NULL--%>
<h1><%=name5%></h1>
结论
request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的!
session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车;
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:评论;
重定向前后端方式
Servlet:
request.getRequestDispatcher("路径.jsp/.html/.htm").forward(request,response);
Jsp
pageContext.forward("路径");
JSP标签库(JSTL标签)、EL表达式
使用 JSTL标签 需要引入依赖
<!--JSTL表达式依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!--jsp标签库-->
<dependency>
<groupId>taglibs</groupId