编写Servlet代码的时候,向响应中输出HTML文档是很是不方便的。html
PrintWriter writer = response.getWriter(); writer.append("<!DOCTYPE html>\r\n") .append("<html>\r\n") .append(" <head>\r\n") .append(" <title>Hello World Application</title>\r\n") .append(" </head>\r\n") .append(" <body>\r\n") .append(" Nick says, \"Hello, World!\"\r\n") .append(" </body>\r\n") .append("</html>\r\n");
在普通的HTML文件中编写上面代码的返回内容是很是简单的。java
<!DOCTYPE html> <html> <head> <title>Hello World Application</title> </head> <body> Nick says, "Hello, World!" </body> </html>
因而,Java EE规范的建立者就设计了JavaServer Pages(也称为JSP)用于知足这个需求。数据库
JSP结合了Java代码和HTML标签。JSP能够包含除了Java代码以外的任何HTML标签、内建的JSP标签、自定义JSP标签以及表达式语言。apache
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <title>Hello World Application</title> </head> <body> Hello, World! </body> </html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
将设置页面的内容类型和字符编码。以前咱们使用HttpServletResponse
的setContentType
和setCharacterEncoding
方法进行设置。该JSP中的其余内容都只是普通的HTML,将做为响应内容被发送到客户端。编程
JSP实际上只是一个精心设计的Servlet。
在编译Java代码的时候,它将被转换成字节码。重要的是咱们将使用字节码而不是Java代码。字节码并非对Java程序最终的渲染。字节码仍然是独立于平台的,并不足以运行在各类不一样的操做系统上。
当Java在JRE中运行时,即时编译器将把它编译成机器码(特定于运行JRE的目标机器)。最终执行的实际是机器码。
在运行时,JSP代码将由JSP编译器进行转换,它将解析出JSP代码的全部特性,并将它们转换成Java代码。由JSP建立获得的Java类都将实现Servlet。
在IDE中编译hello-world-jsp项目,启动调试器并打开浏览器访问http://localhost:8080/hello-world-jsp/。在Tomcat的主目录下的work\Catalina\localhost\hello-world-jsp\org\apache\jsp中,能够看到编译过的JSP文件和生成的中间Java文件。
浏览器
各个Web容器生成的JSP Servlet类看起来并不一致。JSP编译后的类最终取决于它在其中运行的Web容器。重要的一点是:JSP的行为和语法有标准规范,只要使用的Web容器兼容于该规范,那么JSP在全部容器中都将有着相同的行为,即便它们编译生成的代码可能不尽相同。session
JSP就像普通的Servlet同样,能够在运行时进行调试(IntelliJ IDEA能够在任何JSP代码中添加断点,Eclipse只容许在JSP的内嵌Java代码中添加断点)。
在某些容器(例如Tomcat)中,JSP将在第一次请求到达时被即时转换并编译。对于以后的请求,能够直接使用编译好的JSP。
JSP能够改善编程速度、效率和开发过程的准确性,因此它是最好的选择。app
JSP在执行时有不少事情必须处理,但全部的这些事情都已经被处理了。因此,访问一个什么内容都没有的JSP页面不会出现任何错误,一切都正常工做。
JSP默认的内容类型为text/html,默认的字符编码为ISO-8859-1。不过,默认的字符编码与许多特殊字符并不兼容,例如非英语的语言,它们可能会影响应用程序的本地化。
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
转换后的代码为:函数
response.setContentType("text/html;charset=UTF-8"); /* //等同于 response.setContentType("text/html"); response.setCharacterEncoding("charset=UTF-8"); */ //也等同于 //response.setHeader("Content-Type","text/html;charset=UTF-8");
<%@ 这是一个指令 %> <%! 这是一个声明 %> <% 这是一个脚本 %> <%= 这是一个表达式 %>
声明用于在JSP Servlet类的范围内声明一些东西,例如能够定义实例变量、方法或声明标签中的类。
脚本被复制到_jspService
方法的主体中。该方法中的全部局部变量均可以在脚本中使用。能够在脚本中使用条件语句、操做对象和执行数学计算。
表达式的做用域与脚本相同,它也将被复制到_jspService
方法中。表达式用于向客户端输出一些内容,把代码的返回值变量输出到客户端。在表达式中能够执行数学计算,还能够调用一些返回字符串、数字或其余原生类型的方法。事实上,任何赋值表达式的整个右侧均可以用在表达式中。
在JSP中实现代码注释的方法有如下4种:
XML注释<!-- 这是一个HTML/XMl注释 -->
能够被发送到客户端,重要的是它并未阻止其中Java代码的执行。
能够在JSP的声明和脚本中使用任何合法的Java注释,包括行注释和块注释。
JSP注释<%-- 这是一个JSP注释 --%>
不只不会发送到浏览器,甚至连JSP编译器也不会解释/转换它们。若是须要注释一段包含JSP脚本、表达式、声明、指令和标记的代码的话,这是很是有用的。
不管什么时候在JSP中包含直接使用类的Java代码,该JSP要么使用彻底限定类名,要么在JSP文件中添加一条导入指令。正如在Java文件中,java.lang包中的全部类都将被隐式地导入同样,它们也会被隐式地导入到JSP文件中。
在JSP中导入Java类的方式:
<%@ page import="java.util.*,java.io.IOException" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*,java.io.IOException"%>
<%@ page import="java.util.Map" %> <%@ page import="java.util.List" %> <%@ page import="java.io.IOException" %>
pageEncoding指定JSP所使用的字符编码,等同于HttpServletResponse中的setCharacterEncoding
方法。
在page指令中使用<%@ page contentType="text/html" pageEncoding="UTF-8" language="java" %>
取代以前的<%@ page contentType="text/html;charset=UTF-8" language="java" %>
session的值只能是真和假中的一个,表示JSP是否将参与HTTP会话。默认值为真,所以在JSP中能够访问隐式的session变量。
isELIgnored表示JSP编译器是否将解析和转换JSP中的表达式语言(EL)。
若是在JSP的执行过程当中出现了错误,errorPage将告诉容器应该将请求转发到哪一个JSP。
isErrorPage表示当前的JSP是否被用做错误页面(默认值为假)。
<%@ include file="index.jsp" %>
特性file指定须要包含的JSP文件的路径。
若是使用的是绝对路径,容器将从应用程序的Web根目录开始定位该文件。对于存储在WEB-INF目录中的included.jsp文件来讲,可使用路径/WEB-INF/included.jsp包含它。
若是使用的是相对路径,它将从包含指令的JSP文件所在的目录开始定位包含文件。
在JSP被转换成Java以前,编译器将使用被包含JSP文件的内容替换include指令。所以,该过程是静态的而且只发生一次。
<jsp:include page="index.jsp" />
是另外一种包含JSP页面的方式,它经过动态(运行时)的方式包含。它使用的路径仍然是相对于当前文件的相对路径,或者从Web根目录开始的绝对路径。被包含的文件将会单独编译。在运行时,请求将会被临时地重定向至被包含的JSP,再将该JSP的结果输出到响应中,而后再将控制权返还给主JSP页面。
这两种包含文件的方法各有优劣。指令include速度快,被引用的JSP文件能够引用主JSP文件中定义的全部变量。但这种方法将使JSP文件变大,记住,Java方法编译后的字节数目最大不能超过65534字节。
<jsp:include>
在每次页面加载时都会从新计算,而且被包含的JSP文件不能使用主JSP中已定义的变量。
大多数状况下,include指令都是最好的选择。
若是但愿在JSP中使用标签库中定义的标签,使用taglib指令引用该标签库便可。
<%@ taglib uri="http://java.sum.com/jsp/jstl/core" prefix="c" %>
特性uri指定了目标标签库所属的URI命名空间,特性prefix则定义了用于引用库中标签时使用的别名。
经过<jsp:forward page="/some/other/page.jsp" />
标签能够将当前JSP正在处理的一些请求转发至其余JSP。在该标签以前生成的任何响应内容仍然会被发送到客户端浏览器中。任何在此标签以后的代码都将被忽略。
<jsp:useBean>
标签在页面中声明一个JavaBean。
<jsp:getProperty>
将从使用<jsp:useBean>
声明的bean中获取属性值。
<jsp:setProperty>
将用于设置该实例的属性。
<jsp:useBean>
标签建立的bean能够经过其余JSP标签、JSP脚本和表达式访问;若是在脚本中声明一个bean,那么该实例只能用于脚本表达式中。
JSP文件提供了几个可在脚本和表达式中使用的隐式变量。之因此称它们为隐式变量:是由于不须要在任何位置定义或声明便可使用它们。它们被定义在JSP执行的Servlet方法的开头。
import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent, org.apache.jasper.runtime.JspSourceImports { //... public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { //... //隐式变量(pageContext、session、application、config、out、page)的声明。 //另外,_jspService方法的参数request和response也是隐式变量 final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; 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; try { //隐式变量的赋值 response.setContentType("text/html;charset=UTF-8"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; //... } //... } }
若是出于某个缘由,须要直接向响应中输出内容,那么应该使用out变量。不过,在大多数状况下,均可以直接使用表达式,或者在JSP中编写文本或HTML内容。
pageContext提供了获取请求特性和会话特性值、访问请求和响应、包含其余文件、转发请求的几个便利方法。
由于page指令的isErrorPage特性的默认值为假,因此exception变量没有在上面出现。若是建立了一个isErrorPage被设置为真的JSP,那么页面中将会自动定义一个隐式变量exception,类型为Throwable。
示例源码连接:https://pan.baidu.com/s/17tSRQEQg1nlyVnjexbYfrA 密码:tsqn
JavaServer Pages是一门用于开发表示层(也称为视图)的技术。
尽管能够在表示层中混合数据库访问操做或数学计算,但并非一个好主意。函数型语言、脚本语言和其余从文件头执行到尾的语言,例如PHP,固然能够这么作。可是不可能在选择Java做为平台语言的时候,还继续以这种方式进行开发。
在一个具备良好结构、干净代码的应用程序中,表示层一般会与业务层分隔开,一样也与数据持久层分隔开。实际上,在JSP中显示动态内容,能够不使用一行Java代码。这使得应用开发者能够专一于业务和数据逻辑,而用户界面开发者则负责JSP的开发。
使用请求派发器
private void showTicketForm(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("/WEB-INF/jsp/view/ticketForm.jsp") .forward(request, response); }
请求转发以后,浏览器的URL不会改变。
示例源码连接:https://pan.baidu.com/s/1x6nSWQWV6bKdRn6nkshWag 密码:xx6t
参考:《Java Web高级编程》第4章