什么是JSP
JSP全名为Java Server Pages,java服务器页面。JSP是一种基于文本的程序,其特点就是HTML和Java代码独特存在!
为什么须要JSP
JSP是为了简化Servlet的工作呈现的替代品,Servlet输入HTML十分艰难,JSP就是代替Servlet输入HTML的。
简略应用一下JSP
- 在idea下生成一个JSP,咱们来看一下JSP长什么样子
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>简略应用JSP</title> </head> <body> </body> </html>
- 看起来就像一个HTML页面,后面也说了:JSP的特点就是HTML和Java代码独特存在
- 咱们向浏览器输入一句HelloWorld,至于<%%>这个货色,我先不解释!
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>简略应用JSP</title> </head> <body> <% String s = "HelloWorld"; out.println(s); %> </body> </html>
JSP的工作原理
- 在Tomcat博客中我提到过:Tomcat拜访任何的资源都是在拜访Servlet!,当然了,JSP也不例外!JSP自身就是一种Servlet。为什么我说JSP自身就是一种Servlet呢?其实JSP在第一次被拜访的时候会被编译为HttpJspPage类(该类是HttpServlet的一个子类)
- 方才我简略应用了一下JSP,它被编译成了这么一个Servlet:
package org.apache.jsp;import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.jsp.*;import java.util.Date;public final class _1_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent { private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory(); private static java.util.List<String> _jspx_dependants; private javax.el.ExpressionFactory _el_expressionfactory; private org.apache.tomcat.InstanceManager _jsp_instancemanager; public java.util.List<String> getDependants() { return _jspx_dependants; } public void _jspInit() { _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory(); _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig()); } public void _jspDestroy() { } public void _jspService(final HttpServletRequest request, final HttpServletResponse response) throws java.io.IOException, ServletException { final PageContext pageContext; HttpSession session = null; final ServletContext application; final ServletConfig config; JspWriter out = null; final Object page = this; JspWriter _jspx_out = null; 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.write("\r\n"); out.write("\r\n"); out.write("<html>\r\n"); out.write("<head>\r\n"); out.write(" <title>简略应用JSP</title>\r\n"); out.write("</head>\r\n"); out.write("<body>\r\n"); String s = "HelloWorda"; out.println(s); out.write("\r\n"); out.write("</body>\r\n"); out.write("</html>\r\n"); } catch (Throwable t) { if (!(t instanceof SkipPageException)){ out = _jspx_out; if (out != null && out.getBufferSize() != 0) try { out.clearBuffer(); } catch (java.io.IOException e) {} if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); } } finally { _jspxFactory.releasePageContext(_jspx_page_context); } }}
- 编译过程是这样子的:浏览器第一次申请1.jsp时,Tomcat会将1.jsp转化成1_jsp.java这么一个类,并将该文件编译成class文件。编译结束后再运行class文件来响应浏览器的申请。
- 当前拜访1.jsp就不再从新编译jsp文件了,间接调用class文件来响应浏览器。当然了,如果Tomcat检测到JSP页面改变了的话,会从新编译的。
- 既然JSP是一个Servlet,那JSP页面中的HTML排版标签是怎么样被发送到浏览器的?咱们来看下下面1_jsp.java的源码就晓得了。原来就是用write()进来的罢了。说到底,JSP就是封装了Servlet的java程序罢了。
out.write("\r\n"); out.write("\r\n"); out.write("<html>\r\n"); out.write("<head>\r\n"); out.write(" <title>简略应用JSP</title>\r\n"); out.write("</head>\r\n"); out.write("<body>\r\n");
- 有人可能也会问:JSP页面的代码服务器是怎么执行的?再看回1_jsp.java文件,java代码就间接在类中的service()中。
String s = "HelloWorda"; out.println(s);
- JSP比Servlet更不便更简略的一个重要起因就是:内置了9个对象!内置对象有:out、session、response、request、config、page、application、pageContext、exception,这几个内置对象不在这里讲。当初先晓得一下即可!
- *
JSP生命周期
JSP也是Servlet,运行时只有一个实例,JSP初始化和销毁时也会调用Servlet的init()和destroy()办法。另外,JSP还有本人初始化和销毁的办法
public void _jspInit() { _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory(); _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig()); } public void _jspDestroy() { }
JSP的语法
JSP代码能够分为两局部:
- 模板数据:就是HTML代码
- 元素:JSP页面中的java代码、JSP指令、JSP标签
JSP脚本
- JSP的脚本就是JSP页面中的java代码,也叫做scriptlet。JSP的脚本必须应用<%%>括起来,不然会被当成是模板数据的!
JSP脚本有三种形式:
- <%%>【定义局部变量,编写语句】
- <%!%>【定义类或办法,然而没人这样用!】
- <%=%>(也称之为表达式输入)【输入各种类型的变量,int、double、String、Object等】
- 如果过多地应用<%%>会导致代码凌乱,JSP还提供了一种scriptlet标签,应用此标签和<%%>有雷同的性能,只不过它更好看了一些
<jsp:scriptlet> String s = "HelloWorld"; out.println(s); </jsp:scriptlet>
JSP正文
<%--这是JSP正文--%> <%--%> //这是java的当行正文 // /*这是java的多行正文*/ /**/
JSP指令
JSP指令用来申明JSP页面的相干属性,例如编码方式、文档类型等等
JSP指令的语法:
<%@指令 属性名="值" %>
page指令
- 我在idea生成的JSP页面就有page指令了。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
- page指令常见属性:
- language="java"
- extends="package.class"
- import="{package.class | package.*}, ..."
- session="true | false"
- buffer="none | 8kb | sizekb"
- autoFlush="true | false"
- isThreadSafe="true | false"
- info="text"
- errorPage="relative_url"
- isErrorPage="true | false"
- contentType="mimeType ;charset=characterSet " | "text/html ; charset=ISO-8859-1"
- pageEncoding="characterSet | ISO-8859-1"
- isELIgnored="true | false"
- 个别地,在eclipse或idea这些高级开发工具上开发,咱们只须要在page指令中指定contentType="text/html;charset=UTF-8",就不会呈现中文乱码问题!
- 当然了contentType 不仅仅能够指定以text/html的形式显示,还能够应用其余的模式显示进去。在conf/web.xml文件中能够查问进去
- 比方,我以doc模式显示jsp的数据
<%@ page contentType="application/msword;charset=UTF-8" language="java" %> <html> <head> <title>简略应用JSP</title> </head> <body> 1111 </body> </html>
- 成果是这样子的:
- 咱们上网的时候,如果咱们操作不当,或者服务器出错了,页面都是会呈现敌对提醒的!这个也能通过page指令来实现跳转到敌对提醒页面上!
- page指令errorPage=和isErrorPage这两个属性,上面咱们来看一下怎么应用!
- 1.jsp呈现了谬误,通过page指令的errorPage属性跳转到error.jsp页面上
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %> <html> <head> <title>该页面出错了!</title> </head> <body> <%--模仿页面出错了!!!--%> <% int result = 2 / 0; %> 你好呀 </body> </html>
- error.jsp页面要通过page指令的isErrorPage属性设置页面就是谬误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %> <html> <head> <title>敌对提醒页面</title> </head> <body> 服务器正忙着呢! </body> </html>
- 上面是成果:
- 当然了,仔细的敌人能够发现地址栏是没有变动的,所以属于是服务器跳转。以上的做法是单个页面设置的,如果我会有很多谬误(JSP多的状况下,谬误就会多),单个设置太麻烦了!
- 咱们能够在web.xml文件中全局设置谬误页,只有产生了404谬误或者空指针异样的谬误都会跳转到error.jsp页面上
<error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page> <error-page> <exception-type>java.lang.NullPointerException</exception-type> <location>/error.jsp</location> </error-page>
- 轻易输个资源进行,会产生发404谬误的,跳转到谬误页面。上面是成果:
include指令
- 在解说request对象的时候,咱们已经应用过request.getRequestDispatcher(String url).include(request,response)来对页头和页尾面进行蕴含
- inclue指令也是做这样的事件,咱们来试验一下吧!
- 这是页头
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>页头</title> </head> <body> 我是页头 <br> <br> <br> </body> </html>
- 这是页尾
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>页尾</title> </head> <body> 我是页尾 </body> </html>
- 在1.jsp中把页头和页尾蕴含进来
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>蕴含页头和页尾进来</title> </head> <body> <%@include file="head.jsp" %> <%@include file="foot.jsp" %> </body> </html>
- 拜访1.jsp
- include指令是动态蕴含。动态蕴含的意思就是:把文件的代码内容都蕴含进来,再编译!,看一下jsp的源代码就晓得了!
- jsp还提供另一种蕴含文件的形式:JSP行为---动静蕴含。jsp行为在上面会讲到!
- *
taglib指令
- JSP反对标签技术,要应用标签技术就先得申明标签库和标签前缀。taglib指令就是用来指明JSP页面内应用标签库技术。
- 这里就不具体阐明了,等到学习JSP标签的时候再应用吧!当初记住有这个指令即可。
- *
JSP行为
JSP行为(JSP Actions)是一组JSP内置的标签,只书写大量的标记代码就可能应用JSP提供丰盛的性能,JSP行为是对罕用的JSP性能的形象和封装。
为什么我不把它间接称为JSP标签呢?我把这些JSP内置的标签称之为JSP行为,可能和JSTL标签辨别开来。当然了,你也能够把它称之为JSP标签,你不要搞混就行了。我集体喜爱把这些JSP内置标签称之为JSP行为。
include行为
- 下面曾经提及到了,include指令是动态蕴含,include行为是动静蕴含。其实include行为就是封装了request.getRequestDispatcher(String url).include(request,response)
- include行为语法是这个样子的
<jsp:include page=""/>
- 咱们先来应用一下把,在1.jsp页面中也将页头和页尾蕴含进来。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>蕴含页头和页尾进来</title> </head> <body> <jsp:include page="head.jsp"/> <jsp:include page="foot.jsp"/> </body> </html>
- 拜访1.jsp页面看一下成果:
- 应用jsp行为来蕴含文件,jsp源文件是这样子的:
- jsp行为蕴含文件就是先编译被蕴含的页面,再将页面的后果写入到蕴含的页面中(1.jsp)
- 当然了,当初有动态蕴含和动静蕴含,应用哪一个更好呢?答案是:动静蕴含。
- 动静蕴含能够向被蕴含的页面传递参数(用途不大),并且是别离解决蕴含页面的(将被蕴含页面编译后得出的后果再写进蕴含页面)【如果有雷同名称的参数,应用动态蕴含就会报错!】!
- 模仿一下场景吧,当初我的头页面有个名为s的字符串变量
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>页头</title> </head> <body> <% String s = "zhongfucheng"; %> 我是页头呀 <br> <br> <br> </body> </html>
- 我的页尾也有个名为s的字符串变量
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>页尾</title> </head> <body> <% String s = "zhongfucheng"; %> 我是页尾呀 </body> </html>
- 当初我应用动态蕴含看看会产生什么,出现异常了。
- 出现异常的起因很简略,就是同一个文件中有两个雷同的变量s
- 应用动静蕴含就能够防止这种状况
param行为
- 当应用<jsp:include>和<jsp:forward>行为引入或将申请转发给其它资源时,能够应用<jsp:param>行为向这个资源传递参数。
forward行为
- 在解说request对象的时候,咱们应用request.getRequestDispatcher(String url).forward(request,response)进行跳转。其实forward行为就是对其封装!
- 咱们来看一下forward的语法:
<jsp:forward page=""/>
- 好的,咱们来应用一下吧。拜访1.jsp页面就跳转到head.jsp页面中
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>拜访1.jsp就跳转到head.jsp</title> </head> <body> <jsp:forward page="head.jsp"/> </body> </html>
- 看一下成果
- 如果我要传递参数,就要在forward行为嵌套param行为
- 在跳转到head.jsp时传入参数username值为zhongfucheng
<jsp:forward page="head.jsp"> <jsp:param name="username" value="zhongfucheng"/> </jsp:forward>
- 在head.jsp页面中获取到传递过去的参数
<% String ss = request.getParameter("username"); %> 获取到的参数是: <%=ss%>
- 成果如下图所示
directive行为
directive的中文意思就是指令。该行为就是代替指令<%@%>的语法的
- <jsp:directive.include file=""/> 相当于<%@include file="" %>
- <jsp:directive.page/> 相当于<%@page %>
- <jsp:directive.taglib/> 相当于<%@taglib %>
- 咱们来试一下能不能用的
<jsp:directive.include file="head.jsp"></jsp:directive.include> <jsp:directive.include file="foot.jsp"></jsp:directive.include>
- 看下成果,失常能够蕴含页面:
- 应用该指令能够让JSP页面更加好看!
- 应用scriptlet行为
<jsp:scriptlet>
代替<%%>是同样一个情理
javaBean行为
JSP还提供了操作javaBean对象的行为,在这里就不具体阐明了,前面会讲到的!当初记住JSP提供了javaBean行为来操作简略类即可!
<jsp:useBean id=""/>
<jsp:setProperty name="" property=""/>
<jsp:getProperty name="" property=""/>
- 什么是JSP内置对象
==========
JSP引擎在调用JSP对应的jspServlet时,会传递或创立9个与web开发相干的对象供jspServlet应用。JSP技术的设计者为便于开发人员在编写JSP页面时取得这些web对象的援用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就能够疾速取得这9大对象的援用
仔细的敌人会发现,咱们没有在JSP页面上定义过out对象,却能够间接应用!其实out对象就是JSP内置对象之一。
九个内置对象:
- pageContext
- page
- config
- request
- response
- session
- application
- exception
- out
- *
out对象
out对象的API
- int getBufferSize()【失去缓存大小】
- int getRemaining()【失去未应用缓存的大小】
- boolean isAutoFlush()
- void println()
- void flush()
- void close()
- void clearBuffer()
- void clear()
- out对象用于向浏览器输入数据,与之对应的是Servlet的PrintWriter对象。然而这个out对象的类型并不是PrintWriter,是JspWriter
- 咱们能够简略了解为:JspWriter就是带缓存的PrintWrieter。
- out对象的原理如下:
只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用ServletResponse.getWriter办法,并通过该办法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到Servlet引擎提供的缓冲区中:
- 设置page指令的buffer属性敞开了out对象的缓存性能
- out对象的缓冲区已满
- 整个JSP页面完结
- 个别咱们在JSP页面输入都是用表达式(<%=%>),所以out对象用得并不是很多!
- *
request
- 内置对象request其实就是HttpServletRequest,在Servlet解说的时候曾经具体阐明了,没什么好说的
response
- 内置对象response其实就是HttpServletResponse,在Servlet解说的时候曾经具体阐明了,没什么好说的
config
- 内置对象config其实就是ServletConfig,在Servlet解说的时候曾经具体阐明了,没什么好说的
session
- 内置对象session其实就是HttpSession。,在Servlet解说的时候曾经具体阐明了,没什么好说的
留神:在page指令配置如下信息,session将不可应用
<%@page session="false" %>
application
- 内置对象application其实就是ServletContext对象,在Servlet解说的时候曾经具体阐明了,没什么好说的
page
- 内置对象page是HttpJasPage对象,其实page对象代表的就是以后JSP页面,是以后JSP编译后的Servlet类的对象。也就是说:page对象相当于一般java类的this
exception
- 内置对象exception是java.lang.Exception类的对象,exception封装了JSP页面抛出的异样信息。exception常常被用来处理错误页面
- 后面咱们已经讲过了怎么设置谬误页面了,上面咱们就来简略应用一下exception对象吧
- 1.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %> <html> <head> <title></title> </head> <body> <%--模仿空指针异样的谬误--%> <% String sss = null; sss.length(); %> </body> </html>
- error.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %> <html> <head> <title>谬误页面</title> </head> <body> <% out.println("程序抛出了异样:" + exception); %> </body> </html>
- 成果:
pageContext
pageContext是内置对象中最重要的一个对象,它代表着JSP页面编译后的内容(也就是JSP页面的运行环境)!
pageContext获取8个内置对象
- 既然它代表了JSP页面编译后的内容,天经地义的:它封装了对其余8大内置对象的援用!,也就是说,通过pageContext能够获取到其余的8个内置对象!
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>获取八大内置对象</title> </head> <body> <% System.out.println(pageContext.getSession()); System.out.println(pageContext.getRequest()); System.out.println(pageContext.getResponse()); System.out.println(pageContext.getException()); System.out.println(pageContext.getPage()); System.out.println(pageContext.getServletConfig()); System.out.println(pageContext.getServletContext()); System.out.println(pageContext.getOut()); %> </body> </html>
- 看下成果:
pageContext作为域对象
相似于request,session,ServletContext作为域对象而言都有以下三个办法:
- setAttribute(String name,Objcet o)
- getAttribute(String name)
- removeAttribute(String name)
- 当然了,pageContext也不例外,pageContext也有这三个办法!
- pageContext实质上代表的是以后JSP页面编译后的内容,作为域对象而言,它就代表着以后JSP页面(也就是page)!也就是说:pageContext域对象只在page范畴内无效,超出了page范畴就有效了!
- 首先来看看在page范畴内能不能应用
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>应用page域对象</title> </head> <body> <% pageContext.setAttribute("name", "zhongfucheng"); %> <% String value = (String) pageContext.getAttribute("name"); System.out.println(value); %> </body> </html>
- 成果如下:
- 咱们当初来试验一下是不是超出了page范畴就有效了!
- 在2.jsp中request域对象设置属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>request域对象设置属性</title> </head> <body> <% //这是request域对象保留的内容 request.setAttribute("name","zhongfucheng"); %> <%--跳转到1.jsp中--%> <jsp:forward page="1.jsp"/> </body> </html>
- 希图在1.jsp中pageContext取出request存进去的属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>在page域对象获取属性</title> </head> <body> <% //希图获取request域对象存进的属性 String value = (String) pageContext.getAttribute("name"); System.out.println(value); %> </body> </html>
- 成果如下:
- pageContext实质上代表着编译后JSP的内容,pageContext还能够封装了拜访其余域的办法!
下面的pageContext默认是page范畴的,但pageContext对象重载了set、get、removeAttribute这三个办法
- getAttribute(String name,int scope)
- setAttribute(String name,Object value,int scope)
- removeAttribute(String name,int scope)
多了一个设置域范畴的一个参数,如果不指定默认就是page。当然了,pageContext把request、session、application、page这几个域对象封装着了动态变量供咱们应用。
- PageContext.APPLICATION_SCOPE
- PageContext.SESSION_SCOPE
- PageContext.REQUEST_SCOPE
- PageContext.PAGE_SCOPE
- 方才咱们没有应用重载办法的时候,应用pageContext是无奈获取到request域对象设置的属性的。当初咱们应用重载后的办法看一下能不能获取失去!
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>在page域对象获取request域对象的属性</title> </head> <body> <% //应用重载的办法获取request域对象的属性 String value = (String) pageContext.getAttribute("name",pageContext.REQUEST_SCOPE); System.out.println(value); %> </body> </html>
- 成果:
pageContexst还有这么一个办法:
- findAttribute(String name)
- 该办法会查找各个域的属性,从小到大开始寻找!也就是page—>request->session->application。这个是EL表达式的原理!,EL表达式前面会讲到!
- 咱们用此办法看能不能查找出request域对象的属性吧!
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>应用findAttribute</title> </head> <body> <% //应用findAttribute查找2.jsp中request域对象的属性 String value = (String) pageContext.findAttribute("name"); System.out.println(value); %> </body> </html>
- 成果如下:
引入和跳转
PageContext类中定义了一个forward办法和两个include办法来别离简化和代替RequestDispatcher.forward办法和include办法。
- pageContext.forward(String url)
- pageContext.include(String url)
4种属性范畴
到目前为止,咱们曾经学了4种属性范畴了。
- page【只在一个页面中保留属性,跳转页面有效】
- requet【只在一次申请中保留属性,服务器跳转无效,浏览器跳转有效】
- session【在一个会话范畴中保留属性,无论何种跳转均无效,敞开浏览器后有效】
- application【在整个服务器中保留,所有用户都能够应用】
- *
- 4个内置对象都反对以下的办法:
- setAttribute(String name, Object o )
- getAttribute(String name)
- removeAttribute(String name)
利用场景
- request:如果客户向服务器发申请,产生的数据,用户看完就没用了,像这样的数据就存在request域,像新闻数据,属于用户看完就没用的
- session:如果客户向服务器发申请,产生的数据,用户用完了等一会儿还有用,像这样的数据就存在session域中,像购物数据,用户须要看到本人购物信息,并且等一会儿,还要用这个购物数据结帐
- servletContext:如果客户向服务器发申请,产生的数据,用户用完了,还要给其它用户用,像这样的数据就存在servletContext域中,像聊天数据
什么是javaBean
- JavaBean就是一个一般的java类,也称之为简略java对象--POJO(Plain Ordinary Java Object),是Java程序设计中一种设计模式,是一种基于 Java 平台的软件组件思维
JavaBean遵循着特定的写法,通常有以下的规定:
- 有无参的构造函数
- 成员属性私有化
- 封装的属性如果须要被外所操作,必须编写public类型的setter、getter办法
- 下面的文字看起来如同很高大上,javaBean其实非常简单,上面的代码就是依照特定写法、规定编写的一个JavaBean对象
public class Person { private String username ; private int age; public Person() { } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
为什么须要应用Javabean
- 应用javaBean的益处就是:封装,重用,可读!
- 上面援用知乎一段答复:
JaveBean你能够了解为一辆货车,在你的java端和web页面进行数据传递的载体,你当然能够每个变量独自传递,或者应用汇合传递,然而javabean能够使你的数据更有可读性,不便开发时明确变量的意义,也使其余浏览你代码的人能间接你的用意
如果把bean类与数据库联结应用,一张表应用bean类,能够使你的代码更加简洁高效,易于了解,当初大多数框架都会应用这种机制。
JSP行为--JavaBean
JSP技术提供了三个对于JavaBean组件的动作元素,即JSP行为(标签),它们别离为:
- <jsp:useBean>【在JSP页面中查找javaBean对象或者实例化javaBean对象】
- <jsp:setProperty>【设置javaBean的属性】
- <jsp:getProperty>【获取javaBean的属性】
jsp:useBean
<jsp:useBean>
标签用于在指定的域范畴内查找指定名称的JavaBean对象:- 存在则间接返回该JavaBean对象的援用。
- 不存在则实例化一个新的JavaBean对象并将它以指定的名称存储到指定的域范畴中。
- 语法:
<jsp:useBean id="实例化对象的名称" class="类的全名" scope="保留范畴"/>
- 如果JSP不反对
<jsp:useBean>
这个行为,咱们要应用Person类是这样应用的
<%--这里须要导入Person类--%> <%@ page import="domain.Person" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title></title> </head> <body> <% //new出对象 Person person = new Person(); person.setName("zhongfucheng"); System.out.println(person.getName()); %> </body> </html>
- 成果如下
- 咱们应用
<jsp:useBean>
就显得十分简洁,不必导包,不必new出对象
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title></title> </head> <body> <jsp:useBean id="person" class="domain.Person" scope="page"/> <% person.setName("zhongfucheng"); System.out.println(person.getName()); %> </body> </html>
- 也能够实现同样的成果:
- 有人可能会想,凭什么写一个
<jsp:useBean>
这样的代码就能够创立出一个对象进去。 - 当初我把JavaBean中无参的构造函数改成有参的,咱们看看会呈现什么状况,出现异常了!
public Person(int age) { this.age = age; }
<jsp:useBean id="person" class="domain.Person" scope="page"/>
外部原理是这样子的:
- 通过下面的代码咱们也晓得了为什么要有一个无参的构造函数!外部在new 对象的时候是没有传递参数进去的!
jsp:setProperty
- 语法:
<jsp:setProerty name="对象名称" property="属性名" param="参数名" value="值">
在语法上可分为4种模式
- <jsp:setProperty name="对象名称" property="*"/>主动匹配
- <jsp:setProperty name="对象名称" property="属性名称"/>指定属性
- <jsp:setProperty name="对象名称" property="属性名称" param="参数名称"/>指定参数【很少用】
- <jsp:setProperty name="对象名称" property="属性名称" value="内容"/>指定内容【很少用】
- 当咱们没有学习到
<jsp:setProperty>
时,咱们获取表单的信息,而后导入到javaBean对象中是这样的一种状况: - 这是表单的页面代码:
<form action="/zhongfucheng/1.jsp" method="post"> 用户名:<input type="text" name="username"> 年龄:<input type="text " name="age"> <input type="submit" value="提交"> </form>
- 这是解决表单提交过去数据的jsp的代码
<jsp:useBean id="person" class="domain.Person" scope="page"/> <% int age = Integer.parseInt(request.getParameter("age")); person.setAge(age); System.out.println(person.getAge()); %>
- 这是能够实现的,然而相对来说,比拟麻烦!
- 咱们来应用<jsp:setProperty>了来看看:
<jsp:useBean id="person" class="domain.Person" scope="page"/> <%--指定属性名称为age--%> <jsp:setProperty name="person" property="age"/> <% System.out.println(person.getAge()); %>
- 也能够实现,并且代码更少,性能更弱小!
- 代码更少能够直观看进去,为什么我说它性能更加弱小呢?表单提交过去的数据都是字符串,在咱们没有用<jsp:setProperty>前,咱们存储设置int类型或其余非字符串类型的数据是须要强转的!然而<jsp:setProperty>不须要咱们强转,它外部主动帮咱们转换了!
- 咱们再来应用一下主动匹配来感触它的弱小之处吧
<jsp:useBean id="person" class="domain.Person" scope="page"/> <%--property的值设置为*就代表主动匹配--%> <jsp:setProperty name="person" property="*"/> <% System.out.println(person.getAge()); System.out.println(person.getName()); %>
- 咱们再来看一下成果:
- 看到这里,有人可能会感觉好神奇:只有设置property的值就能够将表单传递过去的数据封装到JavaBean对象中了!这到底是这样做到的???
- 仔细的敌人会发现,JavaBean的属性名称和表单的name属性设置的名称是截然不同的!
private String username ; private int age; 用户名:<input type="text" name="username"> 年龄:<input type="text " name="age">
- 如果我设置不一样还能不能用?咱们试试:表单name属性的username改成是user
用户名:<input type="text" name="user">
- 咱们再来看看还能不能把表单的数据残缺地封装JavaBean对象中
- 咱们能够发现:要想可能把表单带过去的数据胜利封装到JavaBean对象上,名字要统一!也就是说:JavaBean属性名要和表单的name的名称统一
- 至于原理,它是通过反射来做的,调用了内省的办法!,咱们看编译后的JSP就明确了。
jsp:getProperty
语法:
- <jsp:getProperty name="对象名" property="属性名"/>
- 该jsp行为非常简略,咱们来应用一下就晓得了。
<%--应用<jsp:getProperty>输入--%> <jsp:getProperty name="person" property="username"/> <jsp:getProperty name="person" property="age"/>
- 成果:
- 原理如下
什么是EL表达式?
表达式语言(Expression Language,EL),EL表达式是用"${}"括起来的脚本,用来更不便的读取对象!
- EL表达式次要用来读取数据,进行内容的显示!
为什么要应用EL表达式?
- 为什么要应用EL表达式,咱们先来看一下没有EL表达式是怎么样读取对象数据的吧!
- 在1.jsp中设置了Session属性
<%@ page language="java" contentType="text/html" pageEncoding="UTF-8"%><html><head> <title>向session设置一个属性</title></head><body><% //向session设置一个属性 session.setAttribute("name", "aaa"); System.out.println("向session设置了一个属性");%></body></html>
- 在2.jsp中获取Session设置的属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title></title></head><body><% String value = (String) session.getAttribute("name"); out.write(value);%></body></html>
- 成果:
- 下面看起来,也没有多简单呀,那咱们试试EL表达式的!
- 在2.jsp中读取Session设置的属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title></title></head><body>${name}</body></html>
- 只用了简简单单的几个字母就能输入Session设置的属性了!并且输入在浏览器上!
- 应用EL表达式能够不便地读取对象中的属性、提交的参数、JavaBean、甚至汇合!
- *
EL表达式的作用
- 首先来看一下EL表达式的语法吧:
${标识符}
- EL表达式如果找不到相应的对象属性,返回的的空白字符串“”,而不是null,这是EL表达式最大的特点!
获取各类数据
获取域对象的数据
- 下面在例子中,咱们曾经体验到了获取Session域对象的数据是如许中央便!其实EL表达式能够让咱们获取各个域范畴的数据
- 在1.jsp中设置ServeltContext属性(也就是application)
<% //向ServletContext设置一个属性 application.setAttribute("name", "aaa"); System.out.println("向application设置了一个属性");%>
- 在2.jsp中获取application的属性
<% ${name}%>
- 和Session一样,也能获取失去!
- 之前咱们来讲ServletContext对象的时候讲过一个办法findAttribute(String name),EL表达式语句在执行的时候会调用该办法,用标识符作为关键字别离从page、request、session、application四个域中查找相应的对象。这也解释了为什么EL表达式能够仅仅通过标识符就可能获取到存进域对象的数据!
- findAttribute()的查找程序:从小到大,也就是page->request->session->application
获取JavaBean的属性
- 以前在JSP页面获取JavaBean的数据是这样子的:
- 1.jsp页面Session存进一个Person对象,设置age的属性为22
<jsp:useBean id="person" class="domain.Person" scope="session"/> <jsp:setProperty name="person" property="age" value="22"/>
在2.jsp中取出Session的属性
<% Person person = (Person) session.getAttribute("person"); System.out.println(person.getAge());%>
- 成果如下
- 当初我应用了EL表达式读取数据又会十分不便了
//等同于person.getAge() ${person.age}
- 下面的代码 等同于调用对象的getter办法,外部是通过反射机制实现的!
获取汇合的数据
- 汇合操作在开发中被宽泛地采纳,在EL表达式中也很好地反对了汇合的操作!能够十分不便地读取Collection和Map汇合的内容
- 为了更好地看出EL表达式的弱小之处,咱们也来比照一下应用EL表达式和不应用EL表达式的区别
- 上面不应用EL表达式输入汇合的元素
- 在1.jsp页面中设置session的属性,session属性的值是List汇合,List汇合装载的又是Person对象
<% List<Person> list = new ArrayList(); Person person1 = new Person(); person1.setUsername("zhongfucheng"); Person person2 = new Person(); person2.setUsername("ouzicheng"); list.add(person1); list.add(person2); session.setAttribute("list",list); %>
- 在2.jsp中获取到session的属性,并输入到页面上
<% List<Person> list = (List) session.getAttribute("list"); out.write(list.get(0).getUsername()+"<br>"); out.write(list.get(1).getUsername()); %>
应用EL表达式又是怎么样的成果呢?咱们来看看!
<%--取出list汇合的第1个元素(下标从0开始),获取username属性--%>${list[0].username}<br><%--取出list汇合的第2个元素,获取username属性--%>${list[1].username}
- 同样也能够有雷同的成果:
- 咱们再来应用一下Map汇合
- 在1.jsp中session属性存储了Map汇合,Map汇合的关键字是字符串,值是Person对象
<% Map<String, Person> map = new HashMap<>(); Person person1 = new Person(); person1.setUsername("zhongfucheng1"); Person person2 = new Person(); person2.setUsername("ouzicheng1"); map.put("aa",person1); map.put("bb",person2); session.setAttribute("map",map);%>
- 看起来如同取出数据的时候是会有点简单,然而有了EL表达式也是十分轻松的!
${map.aa.username}<br>${map.bb.username}
- 成果:
- 如果Map汇合存储的关键字是一个数字,就不能应用"."号运算符了,如下所示
- 对于这种状况,咱们能够应用"[]"的模式读取Map汇合的数据
${map["1"].username}<br>${map["2"].username}
- EL表达式配合JSTL标签能够很不便的迭代汇合,前面讲到JSTL标签的时候会用到!这里就不具体阐明了。
- *
EL运算符
- EL表达式反对简略的运算符:加减乘除取摸,逻辑运算符。empty运算符(判断是否为null),三目运算符
- empty运算符能够判断对象是否为null,用作于流程管制!
- 三目运算符简化了if和else语句,简化代码书写
<% List<Person> list = null;%>${list==null?"list汇合为空":"list汇合不为空"}
- 成果:
EL表达式11个内置对象
EL表达式次要是来对内容的显示,为了显示的不便,EL表达式提供了11个内置对象。
- pageContext 对应于JSP页面中的pageContext对象(留神:取的是pageContext对象)
- pageScope 代表page域中用于保留属性的Map对象
- requestScope 代表request域中用于保留属性的Map对象
- sessionScope 代表session域中用于保留属性的Map对象
- applicationScope 代表application域中用于保留属性的Map对象
- param 示意一个保留了所有申请参数的Map对象
- paramValues示意一个保留了所有申请参数的Map对象,它对于某个申请参数,返回的是一个string[]
- header 示意一个保留了所有http申请头字段的Map对象
- headerValues同上,返回string[]数组。
- cookie 示意一个保留了所有cookie的Map对象
- initParam 示意一个保留了所有web利用初始化参数的map对象
- 上面测试各个内置对象
<%--pageContext内置对象--%><% pageContext.setAttribute("pageContext1", "pageContext");%>pageContext内置对象:${pageContext.getAttribute("pageContext1")}<br><%--pageScope内置对象--%><% pageContext.setAttribute("pageScope1","pageScope");%>pageScope内置对象:${pageScope.pageScope1}<br><%--requestScope内置对象--%><% request.setAttribute("request1","reqeust");%>requestScope内置对象:${requestScope.request1}<br><%--sessionScope内置对象--%><% session.setAttribute("session1", "session");%>sessionScope内置对象:${sessionScope.session1}<br><%--applicationScope内置对象--%><% application.setAttribute("application1","application");%>applicationScopt内置对象:${applicationScope.application1}<br><%--header内置对象--%>header内置对象:${header.Host}<br><%--headerValues内置对象,取出第一个Cookie--%>headerValues内置对象:${headerValues.Cookie[0]}<br><%--Cookie内置对象--%><% Cookie cookie = new Cookie("Cookie1", "cookie");%>Cookie内置对象:${cookie.JSESSIONID.value}<br><%--initParam内置对象,须要为该Context配置参数能力看出成果【jsp配置的有效!亲测】--%>initParam内置对象:${initParam.name}<br>
- 效果图:
注意事项:
- 测试headerValues时,如果头外面有“-” ,例Accept-Encoding,则要headerValues[“Accept-Encoding”]
- 测试cookie时,例
${cookie.key}
取的是cookie对象,如拜访cookie的名称和值,须${cookie.key.name}
或${cookie.key.value}
- 测试initParam时,初始化参数要的web.xml中的配置Context的,仅仅是jsp的参数是获取不到的
- 下面曾经测过了9个内置对象了,至于param和parmaValues内置对象个别都是别的页面带数据过去的(表单、地址栏)!
- 表单页面
<form action="/zhongfucheng/1.jsp" method="post">用户名:<input type="text" name="username"><br>年龄:<input type="text " name="age"><br>喜好:<input type="checkbox" name="hobbies" value="football">足球<input type="checkbox" name="hobbies" value="basketball">篮球<input type="checkbox" name="hobbies" value="table tennis">兵乓球<br><input type="submit" value="提交"><br></form>
- 解决表单页面:
${param.username}<br>${param.age}<br>//没有学习jstl之前就一个一个写吧。${paramValues.hobbies[0]}<br>${paramValues.hobbies[1]}<br>${paramValues.hobbies[2]}<br>
- 成果:
- 当然了,应用地址栏形式提交数据给解决页面也是用param内置对象去获取数据的!
EL表达式回显数据
EL表达式最大的特点就是:如果获取到的数据为null,输入空白字符串""!这个特点能够让咱们数据回显
- 在1.jsp中模仿场景
<%--模仿数据回显场景--%><% User user = new User(); user.setGender("male"); //数据回显 request.setAttribute("user",user);%><input type="radio" name="gender" value="male" ${user.gender=='male'?'checked':'' }>男<input type="radio" name="gender" value="female" ${user.gender=='female'?'checked':'' }>女
- 成果:
EL自定义函数
EL自定义函数用于扩大EL表达式的性能,能够让EL表达式实现一般Java程序代码所能实现的性能
- 开发HTML本义的EL函数
- 咱们有时候想在JSP页面中输入JSP代码,然而JSP引擎会主动把HTML代码解析,输入给浏览器。此时咱们就要对HTML代码本义。
步骤:
- 编写一个蕴含静态方法的类(EL表达式只能调用静态方法),该办法很罕用,Tomcat都有此办法,可在webappsexamplesWEB-INFclassesutil中找到
public static String filter(String message) { if (message == null) return (null); char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuilder result = new StringBuilder(content.length + 50); for (int i = 0; i < content.length; i++) { switch (content[i]) { case '<': result.append("<"); break; case '>': result.append(">"); break; case '&': result.append("&"); break; case '"': result.append("""); break; default: result.append(content[i]); } } return (result.toString());}
- 在WEB/INF下创立tld(taglib description)文件,在tld文件中形容自定义函数
<?xml version="1.0" encoding="ISO-8859-1"?><taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <tlib-version>1.0</tlib-version> <short-name>myshortname</short-name> <uri>/zhongfucheng</uri> <!--函数的形容--> <function> <!--函数的名字--> <name>filter</name> <!--函数地位--> <function-class>utils.HTMLFilter</function-class> <!--函数的办法申明--> <function-signature>java.lang.String filter(java.lang.String)</function-signature> </function></taglib>
- 在JSP页面中导入和应用自定义函数,EL自定义的函数个别前缀为"fn",uri是"/WEB-INF/tld文件名称"
<%@ page language="java" contentType="text/html" pageEncoding="UTF-8" %><%@taglib prefix="fn" uri="/WEB-INF/zhongfucheng.tld" %><html><head> <title></title></head><body>//实现了HTML本义的性能${fn:filter("<a href='#'>点我</a>")}</body></html>
- 成果:
EL函数库(fn办法库)
- 因为在JSP页面中显示数据时,常常须要对显示的字符串进行解决,SUN公司针对于一些常见解决定义了一套EL函数库供开发者应用。
- 其实EL函数库就是fn办法库,是JSTL标签库中的一个库,也有人称之为fn标签库,然而该库长得不像是标签,所以称之为fn办法库
- 既然作为JSTL标签库中的一个库,要应用fn办法库就须要导入JSTL标签!要想应用JSTL标签库就要导入jstl.jar和standard.jar包!
- 所以,要对fn办法库做测试,首先导入开发包(jstl.jar、standard.jar)
- 在JSP页面中指明应用标签库
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
- fn办法库全都是跟字符串无关的(能够把它想成是String的办法)
- fn:toLowerCase
- fn:toUpperCase
- fn:trim
- fn:length
- fn:split
- fn:join 【接管字符数组,拼接字符串】
- fn:indexOf
- fn:contains
- fn:startsWith
- fn:replace
- fn:substring
- fn:substringAfter
- fn:endsWith
- fn:escapeXml【疏忽XML标记字符】
- fn:substringBefore
- 测试代码:
contains:${fn:contains("zhongfucheng",zhong )}<br>containsIgnoreCase:${fn:containsIgnoreCase("zhongfucheng",ZHONG )}<br>endsWith:${fn:endsWith("zhongfucheng","eng" )}<br>escapeXml:${fn:escapeXml("<zhongfucheng>你是谁呀</zhongfucheng>")}<br>indexOf:${fn:indexOf("zhongfucheng","g" )}<br>length:${fn:length("zhongfucheng")}<br>replace:${fn:replace("zhongfucheng","zhong" ,"ou" )}<br>split:${fn:split("zhong,fu,cheng","," )}<br>startsWith:${fn:startsWith("zhongfucheng","zho" )}<br>substring:${fn:substring("zhongfucheng","2" , fn:length("zhongfucheng"))}<br>substringAfter:${fn:substringAfter("zhongfucheng","zhong" )}<br>substringBefore:${fn:substringBefore("zhongfucheng","fu" )}<br>toLowerCase:${fn:toLowerCase("zhonGFUcheng")}<br>toUpperCase:${fn:toUpperCase("zhongFUcheng")}<br>trim:${fn:trim(" zhong fucheng ")}<br><%--将宰割成的字符数组用"."拼接成一个字符串--%>join:${fn:join(fn:split("zhong,fu,cheng","," ),"." )}<br>
- 成果:
- 应用fn办法库数据回显
<% User user = new User(); String likes[] = {"sing"}; user.setLikes(likes); //数据回显 request.setAttribute("user",user);%><%--java的字符数组以","号宰割开,首先拼接成一个字符串,再判读该字符串有没有蕴含关键字,如果有就checked--%><input type="checkbox"${ fn:contains(fn:join(user.likes,","),"sing")?'checked':'' }>唱歌<input type="checkbox"${ fn:contains(fn:join(user.likes,","),"dance")?'checked':'' }>跳舞
- 成果:
什么是JSTL
JSTL全称为 JSP Standard Tag Library 即JSP规范标签库。
JSTL作为最根本的标签库,提供了一系列的JSP标签,实现了根本的性能:汇合的遍历、数据的输入、字符串的解决、数据的格式化等等!
为什么要应用JSTL
- EL表达式不够完满,须要JSTL的反对!在JSP中,咱们后面曾经用到了EL表达式,领会到了EL表达式的弱小性能:应用EL表达式能够很不便地援用一些JavaBean以及其属性,不会抛出NullPointerException之类的谬误!然而,EL表达式十分无限,它不能遍历汇合,做逻辑的管制。这时,就须要JSTL的反对了!
- Scriptlet的可读性,维护性,重用性都非常差!JSTL与HTML代码非常相似,遵循着XML标签语法,应用JSTL让JSP页面显得整洁,可读性十分好,重用性十分高,能够实现简单的性能!
- 在JSP中不举荐应用scriptlet输入,举荐应用JSP标签。
应用JSTL标签库步骤:
- 导入jstl.jar和standard.jar开发包
- 在JSP页面中用tablib指令引入须要用到的JSTL标签
core标签库
- core标签库是JSTL的外围标签库,实现了最根本的性能:流程管制、迭代输入等操作!
- core标签库的前缀个别是c
c:out
- 简略应用一下
<% session.setAttribute("name", "zhongfucheng"); %> //<c:out/>标签反对标签体,default属性上的数据能够写在标签体中 //<c:out value="${name}" escapeXml="true">您要的数据找不着</c:out> <c:out value="${name}" default="您要的数据找不着" escapeXml="true"/>
- 咱们发现下面的代码实现的成果和EL表达式是一样的,它杰出的中央就多了两个属性,default和escapeXml属性。如果咱们用到这两个属性,咱们就应用该标签,如果没有用到这两个属性就用EL表达式就能够了。
- *
c:set
- 该标签有5个属性,用起来有略微有些简单了!当初要记住的就是:var属性操作的是Integer、Double、Float、String等类型的数据,target属性操作的是JavaBean或Map对象的数据,scope代表的是Web域,value是值,property是对象的属性!
应用var属性
- 既然var属性只能操作Integer、Double、String等类型,那么存在var属性就肯定没有property属性(property代表的是对象的成员属性,Integer、String这些类型哪来的成员变量呀)
- 上面的代码流程是这样的:创立了一个name的变量,设置的值为zhongfucheng,范畴是page
<c:set var="name" value="fucheng" scope="page"/> ${name}
- 成果:
- 当然了,set标签也反对标签体,value的值能够写在标签体里边
<c:set var="name" scope="page"> zhongfucheng </c:set>
- 应用var属性和scope属性实现计数器
<%--因为上面变量须要做加法运算,所以要定义进去,不然服务器是不晓得我的变量是Integer类型的--%> <% Integer sessionCount = 0; Integer applicationCount = 0; %> <c:set var="sessionCount" value="${sessionCount+1}" scope="session"/> <c:set var="applicationCount" value="${applicationCount+1}" scope="application"/>
- 成果:
应用target属性
- 应用target属性与之配对的是property属性,target属性只能操作JavaBean或Map对象,property就是对应的成员变量或key了。。
- 既然target属性操作的是JavaBean或Map对象,那么肯定是通过EL表达式来获取到对象了。taget属性如果获取不到数据会抛出异样!应用target属性就肯定没有scope属性(scope属性代表的是保留范畴,target的值都是获取来的,难道你还能扭转人家的范畴?)
<%--创立出JavaBean对象,设置为session范畴的属性--%> <jsp:useBean id="person" class="domain.Person" scope="session"/> <%--获取到person对象,设置age属性的值为32--%> <c:set target="${person}" property="age" value="32"/> ${person.age}
- 成果:
c:remove
remove标签就相当简略了,只有var和scope属性,代表的是删除域范畴的属性
- 上面简略来测试一下吧:
<%--创立出JavaBean对象,设置为session范畴的属性--%> <jsp:useBean id="person" class="domain.Person" scope="session"/> <%--获取到person对象,设置age属性的值为32--%> <c:set target="${person}" property="age" value="32"/> ${person.age} <br> <%--删除session属性--%> <c:remove var="person" scope="session"></c:remove> ${person.age==null?"存在session的person对象被删除了!":"我还在呢!"}
- 成果:
c:catch
该标签次要用来处理程序中产生的异样。
catch标签也非常简略,只有一个var属性,var属性封装了异样的信息!
<%--创立出JavaBean对象,设置为session范畴的属性--%> <jsp:useBean id="person" class="domain.Person" scope="session"/> <c:catch var="message"> <%--target属性只能是EL表达式,当初我是字符串,获取不到对象,必定会抛出异样的!--%> <c:set target="person" property="age" value="32"/> </c:catch> ${message}
- 成果:
c:if
JSTL提供了if标签实现分支语句的实现,test属性是不可或缺的。
var和scope属性我看来如同没什么用的(保留执行后果有什么用?)
- 依据传递过去的参数的不同显示不同的页面!
<%--如果带过去的名字是zhongfucheng,那么能够登陆--%> <c:if test="${param.name=='zhongfucheng'}"> 用户名:<input type="text" name="username"><br> 明码:<input type="password" name="password"><br> <input type="submit" value="登陆"> </c:if> <%--如果带过去的名字是ouzicheng,那么就是注册--%> <c:if test="${param.name=='ouzicheng'}"> 用户名:<input type="text" name="username"><br> 明码:<input type="password" name="password"><br> <input type="submit" value="注册"> </c:if>
- 留神地址栏的参数!
c:choose
if标签没有else的性能,如果须要相似于java中的if else流程就须要应用choose标签。
choose标签须要联结when和otherwise标签一起应用!
<c:choose> <c:when test="${param.name=='zhongfucheng'}"> 你好啊,zhongfucheng </c:when> <c:when test="${param.name=='ouzicheng'}"> 你好啊,ouzicheng </c:when> <c:otherwise> 你是谁啊?别轻易过去! </c:otherwise> </c:choose>
- 成果:
c:forEach
forEach为循环标签,相当于Java中的while和for
- 之前咱们在应用EL表达式获取到汇合的数据,遍历汇合都是用scriptlet代码循环,当初咱们学了forEach标签就能够舍弃scriptlet代码了。
- 向Session中设置属性,属性的类型是List汇合
<% List list = new ArrayList<>(); list.add("zhongfucheng"); list.add("ouzicheng"); list.add("xiaoming"); session.setAttribute("list", list); %>
- 遍历session属性中的List汇合,items:行将要迭代的汇合。var:以后迭代到的元素
<c:forEach var="list" items="${list}" > ${list}<br> </c:forEach>
- 成果:
- 遍历Map对象有略微地不一样,咱们来看一下,var属性保留的不是每个迭代的对象,而是Map.Entry。
<% Map map = new HashMap(); map.put("1", "zhongfucheng"); map.put("2", "xiaohong"); map.put("3", "xiaoming"); session.setAttribute("map",map); %> <c:forEach var="me" items="${map}" > ${me.key} ${me.value}<br> </c:forEach>
- begin默认从0开始、end默认为汇合的最初一个元素、step默认为1
varStatus代表着以后对象被迭代的信息,它有以下的属性。
- index【返回以后是第几个对象,从0开始计数】
- count【曾经遍历多少个对象了,从1开始计数】
- first【是否是第一个】
- last【是否是最初一个】
- current【以后被迭代的对象】
- begin【开始的地位】
- end【最初的地位】
- step【步长】
<c:forEach var="list" items="${list}" varStatus="varStatus" > ${list}您的下标是:${varStatus.index}<br> </c:forEach>
- 成果:
c:forTokens
该标签相似于String类的split()和for循环的一种汇合
它与forEach标签十分类似,都有begin、end、step、items、var、varStatus属性,不同的是forTokens标签的items属性外面是字符串,这个字符串会被delims属性的内容宰割成多个字符串!
<c:forTokens items="zhongfucheng,ouzicheng,xiaoming,xiaohong" var="name" delims="," > ${name} </c:forTokens>
- 效果图:
c:import
import标签相似于JSP行为<jsp:include/>
和JSP指令<%include>
import标签的属性:
- url【指定要蕴含的门路,Internet所有的url都能够】
- context【拜访同一个web容器的其余资源,以"/"结尾】
- var【保留导入的文件的内容,以String类型存储】
- socpe【保留的范畴,默认是page】
- charEncoding【字符编码】
- varReader【保留导入文件的内容,以Reader类型存储】
当然了,import标签性能更加更大!弱小在哪里呢?import标签能够引入Internet网页上的内容,也就是说,csdn也能够引入进来!
- 咱们来用一下把!
<c:import url="http://www.csdn.net" charEncoding="UTF-8" />
- 咱们一看,是没有款式的:
- 打印csdn的源代码:
<c:import url="http://www.csdn.net" charEncoding="UTF-8" var="net"/> CSDN的源码是:<br><br><br><br><br> <c:out value="${net}" escapeXml="true"></c:out>
- 成果:
c:param
- 在JSP页面进行URL的相干操作时,常常要在URL地址前面附加一些参数。<c:param>标签能够嵌套在<c:import>、<c:url>或<c:redirect>标签内,为这些标签所应用的URL地址附加参数。
- <c:param>标签在为一个URL地址附加参数时,将主动对参数值进行URL编码,例如,如果传递的参数值为“中国”,则将其转换为“%d6%d0%b9%fa”后再附加到URL地址前面,这也就是应用<c:param>标签的最大益处。
- *
c:url
url标签非常实用!在浏览器禁用Cookie的时候,咱们之前学Servlet时解决办法是:response.encodeURL()。url标签也能够实现这样的性能,再配合param标签应用,就非常实用了!
- 咱们配合param标签来应用一下吧!
<c:url value="2.jsp" var="url"> <c:param name="name" value="中国!"> </c:param> </c:url> <a href="${url}">我通过了URL地址重写!</a>
- 成果:
c:redirect
redirect标签用于实现Redirect性能,当然了,此标签也可能配合param标签应用!
- 简略应用一下,重定向到2.jsp,带了一个参数:
<c:redirect url="2.jsp" > <c:param name="name" value="zhongfucheng"> </c:param> </c:redirect>
- 在2.jsp中获取到参数
fmt标签库
fmt标签库也叫做国际化标签库。这里就不具体阐明了,等我讲到Web 国际化的时候才讲吧!
fn办法库
fn办法库也叫做EL函数库、fn标签库。这个在解说EL表达式的时候有具体的阐明,可转移到我EL表达式的博文中!
为什么要应用自定义标签?
JSTL标签库只提供了简略的输入等性能,没有实现任何的HTML代码封装,并且某些简单类型转换,或者逻辑解决的时候,JSTL标签库实现不了,须要自定义标签!
编写自定义标签的步骤:
- 编写一个实现Tag接口的Java类【标签处理器类】
- 在WEB-INF目录下创立tld(Tag Library Descriptor)文件,在tld文件中对标签解决类(实现Tag接口的Java类)进行形容
疾速入门
- 指标:应用标签输入客户机的IP地址!
- 依照步骤来:首先编写一个实现Tag接口的Java类
public class showIp implements Tag { @Override public void setPageContext(PageContext pageContext) { } @Override public void setParent(Tag tag) { } @Override public Tag getParent() { return null; } @Override public int doStartTag() throws JspException { return 0; } @Override public int doEndTag() throws JspException { return 0; } @Override public void release() { } }
- 既然要获取到客户机的IP地址,那么request对象是必不可少的。当初问题来了,在Tag重写的办法如同不能间接获取到request对象啊。
- 通过我一番认真的察看,发现了上面这个办法:
@Override public void setPageContext(PageContext pageContext) { }
- 既然能获取到pageContext对象,那么其余8大内置对象还不是随随便便?于是乎,我就定义一个成员变量pageContext,在setPageContext()办法中传递过去的pageContext赋值给我定义的成员变量即可!
private PageContext pageContext = null; @Override public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; }
- 好的,看回咱们的需要:应用标签输入客户机的IP地址。在下面残余5个办法中,最有可能就是在doStartTag()办法中编写代码!
@Override public int doStartTag() throws JspException { //获取到request对象 HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest(); //获取到客户机的ip地址 String ip = httpServletRequest.getRemoteAddr(); //获取输入到浏览器的对象 JspWriter jspWriter = pageContext.getOut(); //上面的异样只能捕捉,因为子类的异样不能比父类多 try { jspWriter.write(ip); } catch (IOException e) { e.printStackTrace(); } return 0; }
- 接着,编写tld文件,形容实现Tag接口的Java类【标签解决类】。
<?xml version="1.0" encoding="ISO-8859-1"?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <tlib-version>1.0</tlib-version> <short-name>zhongfucheng</short-name> <uri>/zhongfucheng</uri> <!-- Invoke 'Generate' action to add tags or functions --> <tag> <name>viewIp</name> <tag-class>tag.showIp</tag-class> <body-content>empty</body-content> </tag> </taglib>
- 上面咱们来测试一下看能不能用
标签解决类具体阐明
看完下面的程序,大部分人都是懵逼的。因为还不晓得它具体是怎么用的,调用程序是什么。
- 首先咱们来看一下Tag接口的源码!
public interface Tag extends JspTag { int SKIP_BODY = 0; int EVAL_BODY_INCLUDE = 1; int SKIP_PAGE = 5; int EVAL_PAGE = 6; void setPageContext(PageContext var1); void setParent(Tag var1); Tag getParent(); int doStartTag() throws JspException; int doEndTag() throws JspException; void release(); }
下面程序的执行流程:
- JSP引擎遇到自定义标签,首先创立标签处理器类的实例对象。
- JSP引擎实例化完标签处理器类后,调用setPageContext()办法,将pageContext对象传递给标签处理器类,使得标签处理器类能够通过pageContext对象与JSP页面进行通信!
- setPageContext()办法执行完后,调用setParent()办法,将以后标签的父标签传递给以后处理器类,如果以后标签没有父标签,则传入null
- 当WEB容器执行到自定义标签的开始标记时,调用doStartTag()办法。
- 当WEB容器执行到自定义标签的完结标记时,调用doEndTag()办法。
- 一般来说,当WEB容器执行完自定义标签后,标签处理器类会驻留在内存中,直至进行WEB利用时,WEB容器才会调用release()办法
咱们当初曾经分明了办法的执行程序了,可Tag接口的源码还有4个变量阿,它们是用来做什么的呢?咱们在编写JSP页面时,常常须要在页面中引入一些逻辑,例如:
- 管制JSP页面某一部分(标签体)是否执行
- 管制整个JSP页面是否执行
- 管制JSP页面内容反复执行
- 批改JSP页面内容输入
- 再看回4个变量的名字,咱们能够发现,这4个变量就是用来做逻辑判断的!
- 咱们来测试一下吧,在doEndTag()办法中,返回的是SKIP_PAGE变量,看下会怎么样!
@Override public int doEndTag() throws JspException { return SKIP_PAGE; }
- 咱们再来看一看成果:
- 如同是没什么区别!咱们再查看一下源代码,发现执行完标签后,前面的代码全都没有执行!
- doStartTag()办法应用的是SKIP_BODY和EVAL_BODY_INCLUDE这两个变量,判断是否执行标签体的内容。
- doEndTag()办法应用的是SKIP_PAGE和EVAL_PAGE这两个变量,判断是否执行剩下页面的内容
- 管制JSP页面内容反复执行和批改JSP页面内容输入前面会有!
- *
tld文件具体阐明
- 首先咱们来看一下tld文件以后用到的内容吧!
<tlib-version>1.0</tlib-version> <short-name>myshortname</short-name> <uri>http://mycompany.com</uri> <tag> <name></name> <tag-class></tag-class> <body-content></body-content> </tag>
咱们一个一个来看:
- shortname举荐应用prefix
- uri就是引入这个标签库应用的uri
- name为标签名
- tagclass为实现类
- bodycontent为标签体的限度,它有4个值: EMPTY【不容许有标签体】,JSP【容许有JSP代码】 ,scriptless【不容许有脚本代码(也就是<%%>),容许有EL表达式,文本,JSP行为】 , tagdepentend【标签体内的JSP代码不会被解析,间接输入文本】
- *
TagSupport类
大部分时候咱们都不须要实现Tag接口来编写自定义标签,TagSupport是Tag的一个模板类,实现了pageContext,parent的getter、setter办法以及一些其余的性能。咱们要做的就是重写doStartTag()和doEndTag()办法
- 上面咱们就来简略应用一下吧:
- 继承TagSupport类,重写doStartTag()办法,比间接实现Tag接口简洁很多!
public class Demo1 extends TagSupport { @Override public int doStartTag() throws JspException { //获取到request对象 HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest(); String method = httpServletRequest.getMethod(); JspWriter jspWriter = pageContext.getOut(); try { jspWriter.write(method); } catch (IOException e) { e.printStackTrace(); } return 0; } }
- 在tld文件中形容一把:
<tag> <name>showMethod</name> <tag-class>tag.Demo1</tag-class> <body-content>empty</body-content> </tag>
- 成果:
带属性的标签
下面咱们编写的自定义标签都没有附带属性的,咱们在应用core标签库的时候,标签个别都带有属性。
其实JSTL标签库的原理就是自定义标签,把自定义标签搞明确了,对JSTL标签库的应用就有更好的了解了!
- 想要自定义标签带有属性也非常简单,只有在标签处理器类上加一个成员变量和setter、getter(),再在tld文件中形容下该属性即可!它的原理是这样的:当标签应用到属性的时候,引擎就会调用它的setter()办法
- 上面我想要实现的性能是:应用标签的人,传入一个字符串格局就能够显示想要的格局日期
- 编写标签处理器类,减少一个成员变量以及对应的setter、getter办法
public class Demo1 extends TagSupport { //创立成员对象,对应的setter、getter办法 private String format = null; @Override public int doStartTag() throws JspException { //创立日期格式化对象 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); //格式化日期并向浏览器输入 try { pageContext.getOut().write(simpleDateFormat.format(new Date())); } catch (IOException e) { e.printStackTrace(); } return 0; } public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } }
- 在tld文件中形容标签和属性,name代表的是属性的名字,required代表的是是否为必须,rtexprvalue代表是否应用EL表达式
<tag> <name>formatDate</name> <tag-class>tag.Demo1</tag-class> <body-content>empty</body-content> <attribute> <name>format</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>
- 咱们来看一下成果:
标签的继承关系
- 在深刻解说之前,咱们先来看一下各种Tag接口、类之间的关系,这样学习上来才不会晕!
IterationTag阐明
- 咱们曾经应用过了Tag接口和TagSupport类了。接下来咱们看一下IterationTag是什么玩意。
public interface IterationTag extends Tag { int EVAL_BODY_AGAIN = 2; int doAfterBody() throws JspException; }
- 从关系图咱们也能够看出,IterationTag接口实现了Tag接口,InterationTag接口和Tag接口最次要的区别就是多了个doAfterBody()办法和EVAL_BODY_AGAIN变量
- 了解起来也很简略:当doAfterBody()返回的是EVAL_BODY_AGAIN变量,那么标签体的内容就始终循环!当然了,TagSupport也实现了Iteration接口,也就是说TagSupport类也能实现Iteration接口的事件!
- 咱们来应用一下吧:
public class Demo1 extends TagSupport { @Override public int doStartTag() throws JspException { try { pageContext.getOut().write("hello"); } catch (IOException e) { e.printStackTrace(); } //执行标签体 return EVAL_BODY_INCLUDE; } @Override public int doAfterBody() throws JspException { //标签体一直循环,直到doAfterBody()返回的是SKIP_BODY return EVAL_BODY_AGAIN; } }
- tld文件中形容,既然标签体有内容,就不能用empty了!
<tag> <name>foreverEval</name> <tag-class>tag.Demo1</tag-class> <body-content>tagdependent</body-content> </tag>
- 留神看横向的滑轮,曾经死循环输入了:
- doAfterBody()中只有返回的是SKPI_BODY就退出循环,执行doEndTag()办法
//定义一个变量,规定标签体循环的次数 int x = 0; @Override public int doStartTag() throws JspException { try { pageContext.getOut().write("hello"); } catch (IOException e) { e.printStackTrace(); } //执行标签体 return EVAL_BODY_INCLUDE; } @Override public int doAfterBody() throws JspException { x++; if (x >= 10) { return SKIP_BODY; } //标签体一直循环,直到doAfterBody()返回的是SKIP_BODY return EVAL_BODY_AGAIN; }
- 当初咱们曾经能管制循环的次数了!
BodyTag阐明
后面咱们曾经应用到了带标签体的自定义标签了,后面的都是只能间接输入而得不到标签体的内容,既然得不到标签体的内容,就更别说批改标签体了!
- 此时,咱们就须要BodyTag接口的反对了!它专门用来解决带标签体的标签,上面咱们来看一下BodyTag的源码!
public interface BodyTag extends IterationTag { /** @deprecated */ int EVAL_BODY_TAG = 2; int EVAL_BODY_BUFFERED = 2; void setBodyContent(BodyContent var1); void doInitBody() throws JspException; }
- BodyTag多了EVAL_BODY_BUFFERED变量【一个曾经标识过期了】,多了setBodyContent和doInitBody()两个办法
其实应用BodyTag非常简略
- 如果doStartTag()办法返回的是EVAL_BODY_BUFFERED,把标签体的内容缓存起来
- 接着调用setBodyContent()办法和doInitBody()办法,封装标签体的内容到BodyContent对象中
- 接着调用doEndTag()办法
- 对于标签体的内容,咱们能够通过getBodyContenet()来获取!
- 再看回下面的关系图,BodyTag实现了IterationTag和Tag接口,如果间接实现BodyTag接口做开发,要实现的办法就太多了。个别咱们应用继承BodyTag的BodyTagSupport来做开发
- *
BodyTagSupport阐明
- 首先来看一下源代码吧:
public class BodyTagSupport extends TagSupport implements BodyTag { protected BodyContent bodyContent; public BodyTagSupport() { } public int doStartTag() throws JspException { return 2; } public int doEndTag() throws JspException { return super.doEndTag(); } public void setBodyContent(BodyContent b) { this.bodyContent = b; } public void doInitBody() throws JspException { } public int doAfterBody() throws JspException { return 0; } public void release() { this.bodyContent = null; super.release(); } public BodyContent getBodyContent() { return this.bodyContent; } public JspWriter getPreviousOut() { return this.bodyContent.getEnclosingWriter(); } }
能够发现:BodyTagSupport次要裁减了以下的内容:
- 把BodyContent间接定义为成员变量,在获取标签体内容的时候就不须要通过getBodyContent()获取了
- 提供获取JspWriter的办法,不须要从pageConext中获取了
- 以上的两个裁减都简化了咱们的代码书写!
protected BodyContent bodyContent; public JspWriter getPreviousOut() { return this.bodyContent.getEnclosingWriter(); }
- 从BodyTag接口中,我就说到了:标签体的内容封装到了BodyContent类中,那么BodyContent类到底是什么?咱们来看一下源码:
public abstract class BodyContent extends JspWriter { private JspWriter enclosingWriter; protected BodyContent(JspWriter e) { super(-2, false); this.enclosingWriter = e; } public void flush() throws IOException { throw new IOException("Illegal to flush within a custom tag"); } public void clearBody() { try { this.clear(); } catch (IOException var2) { throw new Error("internal error!;"); } } public abstract Reader getReader(); public abstract String getString(); public abstract void writeOut(Writer var1) throws IOException; public JspWriter getEnclosingWriter() { return this.enclosingWriter; } }
- 原来BodyContent继承着JspWriter,它与JspWriter最大的区别是:BodyContent类的任何写入的内容并不主动地向页面输入!
- 咱们个别应用BodyContent都应用两个办法:
//将数据转变成Reader对象 public abstract Reader getReader(); //将数据转变成String对象 public abstract String getString();
- 再从关系图咱们能够看初,BodyTagSupport继承了TagSupport类实现了BodyTag接口,能够说:BodyTagSupport有着后面讲的接口和类的所有性能!。
- 上面咱们来应用下BodyTagSupport将标签体的内容转成是小写的:
- 标签处理器类
public class Demo1 extends BodyTagSupport { @Override public int doStartTag() throws JspException { //想要获取到标签体的内容,就要返回EVAL_BODY_BUFFERED变量 return EVAL_BODY_BUFFERED; } @Override public int doEndTag() throws JspException { //获取到标签体的内容 String value = bodyContent.getString(); //将标签体的内容转成小写并输入 try { this.getPreviousOut().write(value.toLowerCase()); } catch (IOException e) { e.printStackTrace(); } return super.doEndTag(); } }
- tld文件:
<tag> <name>BodyContentToLowerCase</name> <tag-class>tag.Demo1</tag-class> <body-content>tagdependent</body-content> </tag>
- 成果: