jsp与Servlet本质都是一样的。jsp是在html中嵌入java代码,servlet是在java代码中嵌入html。
tomcat在获得jsp页面后,在work目录下,会将其转换为servlet的java文件(如hello.jsp--->hello_jsp.java),而后对其进行编译,这里分析jsp各个部分功能的时候,注意它转为servlet中的代码结构。hello_jsp.hava中的_jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)方法相当于Servlet中的service(javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)。
--------------------------------------------------------------------------------------------------->
JSP脚本元素
表达式: <%= 表达式 %> 将表达式结果输出到浏览器,底层解析后 out.print(表达式);
代码片段: <% java 代码 %> 将“java代码”完整复制到service方法体中。 service(){ java代码片段}
脚本声明: <%! 声明内容 %> 将“声明内容”完整复制到class类体中, class hello { 脚本声明 }
<body> Hello again! <br> <%=i%> <% int i = 5; %> <%=i%> <%! int i = 10; %> <% i++; %> <%=this.i%> </body>
这里<%! %>属于声明,所以其中i=10为hello_jsp.java代码中的成员变量,而<% int i = 5 ;%>会被直接搬到_jspService(httpServletRequest, httpServletResponse)方法体中,所以属于局部变量,而<%= %>中的内容会在搬到方法体中用out.print()进行输出。
--------------------------------------------------------------------------------------------------->
jsp中的注释分<%-- --%>,注释部分的内容到java源码中就不存在了。
--------------------------------------------------------------------------------------------------->
JSP指令
jsp在web容器中被转为servlet,文件编码、依赖类、发送给浏览器输出编码、行为等等需要jsp页面与tomcat进行约定,这个依据就是jsp指令。指令的格式为:<%@指令名称 属性=值 属性=值 .....%>,指令有三,page, include, taglib。
<%@page 属性=值 属性=值 .....%>
>使用
* 一个指令可以编写多个属性
* 相同指令可以使用多次
* 指令可以使用在任意位置
* 大部分属性只能使用一次,否则jsp将抛出异常。之后少数可以重复使用。例如:import
>编码
pageEncoding :当前页面的编码
contentType:jsp生成servlet响应给浏览器编码
注意:一般一致
对比:
如果只有pageEncoding,设置当前编码,也可以设置响应编码
如果只有contentType,可以设置响应编码,也可以设置当前页面编码
>缓存机制
buffer : 设置缓存大小,默认:8kb
autoFlush : 缓存如果溢出,将自动刷新,常设置为true,设置false有可能异常:java.io.IOException: Error: JSP Buffer overflow
>常用
session : 表示当前jsp页面是否可以使用session 内置对象。 true:可以使用 jsp脚本(表达式、代码块)中 使用 session
import : jsp使用其他类,进行导包。 分别导入:java.util.List 一次性导入:java.util.List,java.util.ArrayList 星号:java.util.*
language :表示jsp支持嵌入语言(java)
info ,使用在servlet接口第5个方法,getServletInfo()
>错误处理机制
在每个展示页面中都设置<%@ page errorPage="error.jsp"%>,当页面出现错误时,会跳转到该jsp页面,在error.jsp中设置<%@ page isErrorPage="true" %> 就可以引用exception对象,获取到错误的原因,可以灵活处理它的内容,像<%=exception.getMessage()%>。当页面出现错误时,Chrome浏览器进行访问,可以看到它会把错误原因,以及页面内容都输出到浏览器上。但ie浏览器会出现下图显示,原因是此时服务器响应的状态码为500,ie浏览器将会给出自己的错误页面,可以利用过滤器对其进行处理。
一般来说,我们会对其进行页面的统一配置,给整个工程进行友好错误页面。在web.xml中配置如下:
...... <error-page> <exception-type></exception-type> <location></location> </error-page> </web-app>
看error-page的说明,可以发现,这个标签需要两个内容,错误定义和位置,而错误定义又分两种:
Element : error-page The error-page element contains a mapping between an error code or exception type to the path of a resource in the web application Used in: web-app Content Model : ((error-code | exception-type), location)
这几个参数的内容分别为((错误码(状态码)|java异常类型),友好页面位置)。
taglib指令之后专门汇总
这里再总结一下<%@ include %>指令的用法与场景。
在很多门户网站中,往往一个页面会被切成很多块儿,不同的开发人员会对不同的快分别进行处理,但整个网页也会有很多公共资源,例如框架,各种链接等等,同样的版面和内容会被放到很多独立的子模块中使用,如果子模块中处理这些公共内容,无疑工程量巨大,而且容易出错,不易扩展,所以我们会将其做成单独的界面,而后在每个需要的子模块中引用该界面。就相当于这个界面会与子模块的界面合并,所以这个指令主要就是做合并的工作。
这里的合并分为两种,一种静态包含<%@include file="" %>,另一种是动态包含<jsp:include page="">。前者的过程是A.jsp包含B.jsp,tomcat将A和Bjsp合并在一起,生成一个A_jsp.java文件,然后编译,最后运行,其结果为:AB合并(一个servlet),所以两个页面中处理变量时需要注意;后者的过程是A.jsp 包含 B.jsp ,tomcat将A生成 A_jsp.java文件,将B生成B_jsp.java文件,分别编译,在运行时将内容合并输出。结果:AB合并(两个servlet)。
--------------------------------------------------------------------------------------------------->
JSP内置对象
所谓的内置对象,即可以直接在<%= %>与<% %>中可以直接使用的变量,即_jspService(httpServletRequest, httpServletResponse)中提供的变量,看代码:
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; java.lang.Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request); if (exception != null) { response.setStatus(javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } final javax.servlet.ServletContext application; final javax.servlet.ServletConfig config; javax.servlet.jsp.JspWriter out = null; final java.lang.Object page = this; javax.servlet.jsp.JspWriter _jspx_out = null; javax.servlet.jsp.PageContext _jspx_page_context = null; ......
page 表示当前页,this引用。
config 表示 servlet配置,类型:ServletConfig
application 表示 web 应用上下文,类型:ServletContext
request 表示一次请求,类型:HttpServletRequest
response 表示一次响应:类型:HttpServletResponse
session 表示一次会话,类型HttpSession
out 表示输出响应体,类型JspWriter
exception 表示发生异常,类型 Throwable
pageContext 表示 jsp页面上下文(jsp管理者) 类型:PageContext
其中,page 指代当前页面(一个页);request,一次请求(默认就一个页,如果使用请求转发可以多个页面);session,一次会话(可以有多次请求);application,一个应用(可以多次会话)。这里主要查看JspWriter与pageContext。我们将out对象打印出来,可知:
[email protected]
到tomcat中找到源码,查看该方法中的write(),最后查看到initOut():
private void initOut() throws IOException { if (out == null) { out = response.getWriter(); } }
所以这里底层使用了response.getWriter()获取out对象,在之前的servlet中已经解释过该方法的详细用法,这里略过。
另外,有个缓存的问题。看页面中的代码:
...... <br> <%= out %> <% out.print("111"); out.print("2222"); response.getWriter().print("3333"); %> </body>
这块代码会直接放入到hello_jap.java中然后统一在页面按照代码逻辑输出,但实际上,我们看页面源码:
3333 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> Hello again! <br> 10 5 10 <br> [email protected] 1112222 </body> </html>
request.getWriter().print()中的内容被放到了页面最前面。原因在于,jsp与servlet缓存并不一致(JspWriter<->response.getWriter()),各自有各自的缓存,out的内容会首先被放到jsp缓存中,而request.getWriter()输出在servlet缓存里,等到最后,servlet会将自己缓存与jsp缓存内容结合输出到页面,造成上述情况。知道了这个原理,我们就可以当jsp将request.getWriter()之前所有内容刷到缓存中时,用out.flush()将jsp缓存中的内容刷新到servlet缓存中,而后再直接向servlet中存放3333,再输出就是代码逻辑顺序的展示了。
...... <br> <%= out %> <% out.print("111"); out.print("2222"); out.flush(); response.getWriter().print("3333"); %> </body>
pageContext是jsp页面的管理者,可以用getXXX()获得其它8个对象的引用;可以对指定作用域属性的值进行快捷操作:
默认作用域属性进行操作:page getAttribute(name) 获得 page作用域数据 setAttribute(name ,value ) 给page作用域设置内容 removeAttribute(name ) 移除所有作用域的内容(page/request/session/application) 指定作用域 getAttribute(name ,scope) 获得 指定作用域数据 setAttribute(name ,value ,scope ) 给指定作用域设置内容 removeAttribute(name ,scope) 移除指定作用域的内容(page/request/session/application) 提供作用域常量 PageContext.PAGE_SCOPE page PageContext.REQUEST_SCOPE request PageContext.SESSION_SCOPE session PageContext.APPLICATION_SCOPE application
--------------------------------------------------------------------------------------------------->
JSP动作标签
这里给出实际场景中的代码,看懂即可:
<jsp:include page="" /> 动态包含 <jsp:forward /> 转发 <jsp:param/> 处理请求参数的 , 可以将中文内容进行 URL编码,类似 <form enctype="application/x-www-form-urlencoded"> <jsp:include page="/error.jsp"> <jsp:param value="uservalue" name="username"/> </jsp:include> <jsp:useBean id="user" class="com.itheima.User"></jsp:useBean> <%-- User user = new User(); pageContext.setAttribute("user",user)--%> <jsp:setProperty property="username" name="user" value="jack"/> <%-- user.setUsername("jack") --%> <jsp:getProperty property="username" name="user"/> <%-- user.getUsername() --%>