javaWeb2之Jsp
Java Server Page
放在除WEB-INF(放web项目的隐私文件)外的其他任何目录中。
Java服务器端网页,实现在HTML页面编写Java代码实现动态内容,一种简化Servlet编写的技术(不用在java里拼写html)
本质上是一个Servlet,每个JSP页面在第一次被访问时
,JSP引擎会将它翻译成一个Servlet程序(java文件),接着编译为class文件,然后由web容器(Servlet引擎,tomcat)去装载、执行。
workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\项目名\org\apache\jsp\xxx_jsp.java
HttpJspBase看源码知,它继承了HttpServlet!
运行tomcat后,被装载在:
tomcat_home\work\Catalina\localhost\项目名\org\apache\xxx_jsp.java
整个过程在Servlet容器的控制下将请求的JSP资源转变为Servlet之后,同样会经历Servlet的实例化、初始化、就绪、销毁四部分的生命周期。从图中可以看到,之所以第一次请求一个JSP页面会比之后的请求响应慢一些,就是要经过转译成Servlet,编译成class这样一个过程。
JSP页面的隐含对象
没有声明就可以引用的,源码:
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
//...
//使用 <% %> 编写的代码会出现在此位置. 可以用到 request, response, pageContext, session
//application, config, out, page 这 8 个隐含对象. (实际上还可以使用一个叫 exception 的隐含对象)
}
1.request:HttpServletRequest,属性的作用范围仅限于同一个请求。
2.response:HttpServletResponse,JSP页面里几乎不用。
3.pageContext:PageContext,页面的上下文,可以从该对象中获取其他8个隐含对象和当前页面其他信息(标签),属性作业范围仅限于当前JSP页面。
4.session:HttpSession,代表浏览器和服务器的一次会话(浏览器打开直到关闭即一次会话)
5.application:ServletContext,代表当前Web应用,属性作业范围仅限于当前Web应用。只要在一处设置属性, 在其他各处的 JSP 或 Servlet 中都可以获取到。
6.config:当前JSP对应的Servlet的ServletConfig对象(几乎不用)。若需访问当前JSP配置的初始化参数,需要通过映射地址(url-pattern,如果你给这个jsp文件在web.xml配了的话)。
7.out:JspWriter对象,调用out.print(),直接把字符串打印在浏览器上。
8.page:指向当前JSP对应的Servlet对象的引用(this),但为Object类型,只能调用Object类的方法(几乎不用)。
9.exception:在声明了page指令的isErrorPage="true"时才可用。
<%@ page isErrorPage="true" %>
pageContext, request, session, application(对属性的作用域的范围从小到大) ,都有这些方法!这四个对象也称之为域对象:
void setAttribute(String name, Object o): 设置属性
Object getAttribute(String name): 获取指定的属性
Enumeration getAttributeNames(): 获取所有的属性的名字组成的 Enumeration 对象
removeAttribute(String name): 移除指定的属性
pageContext, request, session, application 对象
JSP模版元素:
JSP页面中的静态HTML内容称 。
JSP脚本片断(scriptlet)
是指嵌套在<% 和 %>之中的一条或多条Java程序代码。 多个脚本片断中的代码可以相互访问。
<%
String ageStr = request.getParameter("age");
Integer age = Integer.parseInt(ageStr);
if(age >= 18){
%>
成人...
<%
}else{
%>
未成人...
<%
}
%>
JSP表达式
提供了将一个 java 变量或表达式的计算结果输出到客户端
的简化方式,它将要输出的变量或表达式直接封装在<%= 和 %>之中。
<%
Date date = new Date();
out.print(date);
%>
<!--jsp表达式-->
<%= date %>
JSP 声明
JSP 声明将 Java 代码封装在<%!和 %>之中,它里面的方法声明代码将被插入进 Servle t的 _jspService 方法的外面,不然报错!(在 JSP 页面中几乎从不这样使用)。
JSP指令
eclipse新建jsp文件的第一、二行,在目前的JSP 2.0中,定义了page、include 和 taglib这三种指令。
page指令
1). page指令用于定义JSP页面的各种属性, 无论page指令出现在JSP页面中的什么地方, 它作用的都是整个JSP页面, 为了保持程序的可读性和遵循良好的编程习惯, page指令最好是放在整个JSP页面的起始位置。
2). page 指令常用的属性:
①. import 属性: 指定当前 JSP 页面对应的 Servlet 需要导入的类。
查看jsp翻译成的servlet文件源码可知,这些包已经导入过:
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
<%@page import="java.text.DateFormat"%>
②. session 属性: 取值为 true 或 false, 指定当前页面的 session 隐藏变量是否可用, 也可以说访问当前页面时是否一定要生成 HttpSession对象.
<%@ page session="false" %>
③. errorPage 和 isErrorPage:
errorPage 指定若当前页面出现错误的实际响应页面时什么. 其中 / 表示的是当前 WEB 应用的根目录.
<%@ page errorPage="/error.jsp" %>
在响应 error.jsp 时, JSP 引擎使用的请求转发的方式!
isErrorPage 指定当前页面是否为错误处理页面, 可以说明当前页面是否可以使用 exception 隐藏变量. 需要注意的是: error.jsp 中,若指定 isErrorPage="true", 并使用 exception 的方法了, 一般不建议能够直接访问该页面。直接访问,exception是null!只能出错情况才让看!
那如何使客户不能直接访问某一个页面呢 ? 对于 Tomcat 服务器而言, WEB-INF 下的文件是不能通过在浏览器中直接输入地址的方式来访问的(直接访问404)。 但通过请求的转发是可以的,所以把通常它们放WEB-INF文件夹里!
还可以在 web.xml 文件中配置错误页面
:
<error-page>
<!-- 指定出错的代码: 404 没有指定的资源, 500 内部错误. -->
<error-code>404</error-code>
<!-- 指定响应页面的位置 -->
<location>/WEB-INF/error.jsp</location>
</error-page>
<error-page>
<!-- 指定异常的类型 -->
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/WEB-INF/error.jsp</location>
</error-page>
④. contentType: 指定当前 JSP 页面的响应类型. 实际调用的是 response.setContentType("text/html; charset=UTF-8");
通常情况下, 对于 JSP 页面而言其取值均为 text/html; charset=UTF-8. charset 指定返回的页面的字符编码是什么(服务器发送给客户端时的内容编码), 通常取值为 UTF-8。
⑤. pageEncoding: 指定当前 JSP 页面的字符编码(把jsp翻译成utf-8的java代码时的编码),通常情况下该值和 contentType 中的 charset 一致。
pageencoding是规则的遵循者,chartset是规则的制定者
ps:head标签里这里的charset的意思?
⑥. isELIgnored: 指定当前 JSP 页面是否可以使用 EL 表达式. 通常取值为 false,表示不忽略el表达式。
include 指令
1). include 指令用于通知 JSP 引擎在翻译当前 JSP 页面时将其他文件中的内容合并进当前 JSP 页面转换成的 Servlet 源文件中,这种在源文件级别进行引入的方式称之为静态引入, 当前JSP页面与静态引入的页面紧密结合为一个Servlet。
静态引入:用于通知JSP引擎在翻译当前JSP页面时将其他文件中的内容合并进当前页面转换成的servlet源文件中。最终只有一个.java文件。
查看原文件:
2). file属性的设置值必须使用相对路径
3). 如果以 / 开头,表示相对于当前WEB应用程序的根目录(注意不是站点根目录),否则,表示相对于当前文件
taglib 指令
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
JSP注释
<%-- JSP 注释 --%>
区别: JSP 注释可以阻止 Java 代码的执行.。
JSP标签
JSP标签可代替java代码,使JSP文件变得简洁,便于维护和代码复用。
include标签
<jsp:include page="b.jsp"></jsp:include>
动态引入: 并不是像 include 指令生成一个 Servlet 源文件, 而是生成两个 Servlet 源文件, 然后通过一个方法的方式把目标页面包含进来:
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "b.jsp", out, false);
forward标签
<jsp:forward page="/include/b.jsp"></jsp:forward>
相当于.
<%
request.getRequestDispatcher("/include/b.jsp").forward(request, response);
%>
但使用 jsp:forward 可以使用 jsp:param 子标签向 b.jsp 传入一些参数. 同样 jsp:include 也可以使用 jsp:param 子标签.
<jsp:forward page="/include/b.jsp">
<jsp:param value="abcd" name="username"/>
</jsp:forward>
OR
<jsp:include page="/include/b.jsp">
<jsp:param value="abcd" name="username"/>
</jsp:include>
在 b.jsp 页面可以通过 request.getParameter("username") 获取到传入的请求参数.
自定义JSP标签
EL表达式
EL(Expression Language) 是为了使JSP写起来更加简单。用于给jsp标签的属性赋值,也可以直接输出。
表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方法,让Jsp的代码更加简化。
1.访问bean的属性
如果该属性没有被赋过值(或是null)或取值时对象绑定名写错,会输出"",而非null。但属性名写错,会报错。
${user.name}
容器会依次从pageContext、request、session、application中查找绑定名为user的对象(getAttribute),找到之后,调用该对象的getName()方法,找不到,输出""。
如果要查指定范围的,可以使用pageScope等xxScope,如${requestScope.user.name}
${user['name']}
等价于上面的。
2.读取请求参数值
${param.username}
等价于 request.getParameter("username");
${paramValues.username}
等价于 request.getParameterValues("username");
jstl(java standard taglib)标签
请求转发与重定向
请求到达容器,容器会创建request和response对象。当响应发送完毕,容器会删除request和response对象,即request和response对象的生存时间是一次请求与响应期间。
重定向
浏览器发送请求访问A,A可以发送状态码302和Location消息头到浏览器,于是浏览器立即向Location发新的请求。重定向之前,容器会清空response对象上存放的数据。一件事情已经做完了,再坐下一件事。
response.sendRedirect(path);
特点:
重定向的地址是任意的;
重定向后浏览器地址会变为重定向地址。
转发
浏览器请求访问A,A通知容器去调用B,是一件事情没有做完,调用另一个组件继续做。转发涉及的各个Web组件共享一个request和response对象。
//请求的转发.
//1. 调用 HttpServletRequest 的 getRequestDispatcher() 方法获取 RequestDispatcher 对象
//传入要转发的地址
String path = "testServlet";
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/" + path);
//2.进行请求的转发.
requestDispatcher.forward(request, response);
特点:
发生在服务器端(由容器实现),客户端只发了一个请求,浏览器不知道转发过程,所以地址栏没有变化。
转发的地址必须是同一个web应用内部某个地址。
转发过程里涉及到的web组件共享同一个reqest、response
forward之后的其他语句还是会执行。
本质区别:
请求的转发只发出了一次请求, 而重定向则发出了两次请求.
①. 请求的转发: 地址栏是初次发出请求的地址.
请求的重定向: 地址栏不再是初次发出的请求地址. 地址栏为最后响应的那个地址
②. 请求转发: 在最终的 Servlet 中, request 对象和中转的那个 request 是同一个对象.
请求的重定向: 在最终的 Servlet 中, request 对象和中转的那个 request 不是同一个对象.
③. 请求的转发: 只能转发给当前 WEB 应用的的资源
请求的重定向: 可以重定向到任何资源.
④. 请求的转发: / 代表的是当前 WEB 应用的根目录
请求的重定向: / 代表的是当前 WEB 站点的根目录.
中文乱码问题
jsp编码设置
1). JSP 页面上有中文, 请求页面展示中文信息时不出现乱码:
保证 contentType="text/html; charset=UTF-8", pageEncoding="UTF-8" charset 和 pageEncoding 的编码一致, 且都支持中文. 通常建议取值为UTF-8。
另外还需保证浏览器的显示的字符编码也和请求的 JSP 页面的编码一致!
2). 通过页面输入框获取中文参数值,再进行展示不出现乱码:
默认参数在传输过程中使用的编码为 ISO-8859-1
①. 对于 POST 请求: POST请求参数在请求体中,只要在获取请求信息之前(在调用 request.getParameter 或者是 request.getReader 等), 调用 request.setCharacterEncoding("UTF-8")
。
②. 对于 GET 请求: 前面的方式对于 GET 如果无效。
可以通过先解码后编码的方式:
String val = request.getParameter("username");
String username = new String(val.getBytes("iso-8859-1"), "UTF-8");
out.print(username);
也可以通过修改 Tomcat 的 server.xml 文件的方式,使得前面的方式对于 GET 起效:
参照 http://localhost:8989/docs/config/index.html 文档的 useBodyEncodingForURI
属性,
为 Connector 节点添加 useBodyEncodingForURI="true" 属性,或者添加URIEncoding=utf-8
。
<Connector connectionTimeout="20000" port="8989" protocol="HTTP/1.1" redirectPort="8443" useBodyEncodingForURI="true"/>
本地的和映射到eclipse的server的都改:
3).浏览器以get方式提交数据时,参数有中文的情况
(RFC3986文档规定)首先url的编码格式采用的是ASCII码,如果url包含中文,客户端浏览器会按照操作系统的字符集进行编码提交到服务器,服务器会按照配置的字符集进行解码,所以如果两者不一致就会导致乱码。解决如下:
encodeURI函数采用UTF-8对URL进行编码。如果只进行一次encodeURI,得到的是UTF-8形式的URL,服务器端通过request.getParameter()解码查询参数(通常是iso-8859-1)就会得到乱码。
如果进行两次encodeURI,第一次编码得到的是UTF-8形式的URL,第二次编码得到的依然是UTF-8形式的URL,但是在效果上相当于首先进行了一次UTF-8编码(此时已经全部转换为ASCII字符),再进行了一次iso-8859-1编码,因为对英文字符来说UTF-8编码和ISO-8859-1编码的效果相同。在服务器端,首先通过request.getParameter()自动进行第一次解码(可能是gb2312,gbk,utf-8,iso-8859-1等字符集,对结果无影响)得到ascii字符,然后再使用UTF-8进行第二次解码,通常使用java.net.URLDecoder("","UTF-8")方法。
两次编码两次解码的过程为:
UTF-8编码->UTF-8(iso-8859-1)编码->iso-8859-1解码->UTF-8解码,编码和解码的过程是对称的,所以不会出现乱码。
参考:https://blog.csdn.net/zqd_java/article/details/53924769
PS:
请求出现RFC 7230 and RFC3986的错误
路径问题
javaWeb中的"/"
什么情况下”/“表示当前web应用的根路径即http://localhost:8080/Dive/
需要交由servlet容器处理的时候
1)请求转发时
request.getRequestDispatcher("/jspDir/b.jsp").forward(request,response);
2)配置web.xml文件中映射servlet访问路径时
什么情况下”/“表示表示web站点的根路径即http://localhost:8080/
需要交由浏览器处理的时候
1)超链接href的值;
2)表单中的action值;
3)请求重定向
开发过程中,建议写“绝对路径”,"/"代表的是站点的根目录时,在后面直接加contextPath(request或application获取);
如果不愿意写,也可以通过head元素里的base标签指定基本 URL 来解析所有的相对 URL。