什么是JSP?
JSP全名为Java Server Pages,java服务器页面。JSP是一种基于文本的程序,其特点就是HTML
和Java代码共同存在!
为什么需要JSP?
JSP是为了简化Servlet的工作出现的替代品,Servlet输出HTML非常困难,JSP就是替代Servlet输出HTML的
JSP还有必要学吗?
在MVC中,JSP属于展示层,但是JSP却又可以写一定的业务,甚至跑去做数据层的事情,这样开发中就会变得无比混乱,也增加了开发的困难程度,所以将展示层与业务层分开就成为了主流,也就是我们说的前后端分离,但是事无绝对,确实一些比较老的项目仍然在跑jsp,不管你会不会写,你总得碰到能看懂吧,如果已经接近找工作,确实还是以比较流行的技术学习比较好,但是若作为学生,时间还是比较富裕的,很多本科也必然都会讲,学习一下也是挺好的,况且JSP与Servlet也是息息相关的,我认为,学它就是为了知道为什么以后会用别的技术代替它(狗头保命),废话有点多了,还是有一点需要的朋友可以简单看一看,希望给你能有一点帮助
Tomcat访问任何的资源都是在访问Servlet!,当然了,JSP也不例外!JSP本身就是一种Servlet。为什么说JSP本身就是一种Servlet呢?
其实JSP在第一次被访问的时候会被编译为HttpJspPage类(该类是HttpServlet的一个子类)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>简单使用JSP</title>
</head>
<body>
<%
String s = "HelloWorld";
out.println(s);
%>
编译过程是这样子的:
浏览器第一次请求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 = "HelloWorld";
out.println(s);
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() {
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
作用:用于配置JSP页面,导入资源文件
格式:<%@ 指令名称 属性名1=属性值1 属性名2=属性值2 … %>
JSP行为(JSP Actions)是一组JSP内置的标签,只书写少量的标记代码就能够使用JSP提供丰富的功能,JSP行为是对常用的JSP功能的抽象和封装。
JSP内置的标签称之为JSP行为,是为了能够和JSTL标签区分开来。(叫做JSP标签也行)
上面已经提及到了,include指令是静态包含,include行为是动态包含。其实include行为就是封装了request.getRequestDispatcher(String url).include(request,response)
include行为语法是这样的:
<jsp:include page=""/>
静态包含:<%@ include file="被包含页面"%>
动态包含:<jsp:include page="被包含页面" flush="true">
<%@ 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>
jsp行为包含文件就是先编译被包含的页面,再将页面的结果写入到包含的页面中(1.jsp)
当然了,现在有静态包含和动态包含,使用哪一个更好呢?答案是:动态包含。
动态包含可以向被包含的页面传递参数(用处不大),并且是分别处理包含页面的(将被包含页面编译后得出的结果再写进包含页面)
【如果有相同名称的参数,使用静态包含就会报错!】!
当使用<jsp:include>和<jsp:forward>
行为引入或将请求转发给其它资源时,可以使用<jsp:param>
行为向这个资源传递参数
在Servlet中我们使用request.getRequestDispatcher(String url).forward(request,response)进行跳转。其实forward行为就是对其封装!
我们来看一下forward的语法
<jsp:forward page=""/>
<%@ 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值为aaa
<jsp:forward page="head.jsp">
<jsp:param name="username" value="aaa"/>
</jsp:forward>
<%
String ss = request.getParameter("username");
%>
获取到的参数是:
<%=ss%>
directive的中文意思就是指令。该行为就是替代指令<%@%>
的语法的
· <jsp:directive.include file=""/> 相当于<%@include file="" %>
· jsp:directive.page/ 相当于<%@page %>
· jsp:directive.taglib/ 相当于<%@taglib %>
使用该指令可以让JSP页面更加美观!
使用scriptlet行为<jsp:scriptlet>
替代<%%>是同样一个道理
JSP还提供了操作javaBean对象的行为,暂时记住JSP提供了javaBean行为来操作简单类即可!后面详细解释:
<jsp:useBean id=""/>
<jsp:setProperty name="" property=""/>
<jsp:getProperty name="" property=""/>
JSP引擎在调用JSP对应的jspServlet时,会传递或创建9个与web开发相关的对象供jspServlet使用。JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用
变量名 | 真实类型 | 作用 |
---|---|---|
pageContext | PageContext | 当前页面共享数据,还可以获取其他八个内置对象 |
request | HttpServletRequest | 一次请求访问的多个资源(转发) |
session | HttpSession | 一次会话的多个请求间 |
application | ServletContext | 所有用户间共享数据 |
response | HttpServletResponse | 响应对象 |
page | Object | 当前页面(Servlet)的对象 this |
out | JspWriter | 输出对象,数据输出到页面上 |
config | ServletConfig | Servlet的配置对象 |
exception | Throwable | 内置对象exception是java.lang.Exception类的对象 |
到目前为止,我们已经学了4种属性范围了。
page【只在一个页面中保存属性,跳转页面无效】
requet【只在一次请求中保存属性,服务器跳转有效,浏览器跳转无效】
session【在一个会话范围中保存属性,无论何种跳转均有效,关闭浏览器后无效】
application【在整个服务器中保存,所有用户都可以使用】
4个内置对象都支持以下的方法:
avaBean就是一个普通的java类,也称之为简单java对象--POJO(Plain Ordinary Java Object),是Java程序设计中一种设计模式,是一种基于 Java 平台的软件组件思想
JavaBean遵循着特定的写法,通常有以下的规则:
有无参的构造函数
成员属性私有化
封装的属性如果需要被外所操作,必须编写public类型的setter、getter方法
上面的文字看起来好像很高大上,javaBean其实非常简单,常见的学生类,书籍类就是按照特定写法、规则编写的一个JavaBean对象
使用javaBean的好处:封装,重用,可读!
JaveBean你可以理解为一辆货车,在你的java端和web页面进行数据传递的载体,你当然可以每个变量单独传递,或者使用集合传递,但是javabean可以使你的数据更有可读性,方便开发时明确变量的意义,也使其他阅读你代码的人能直接你的意图
如果bean类与数据库联合使用,一张表使用bean类,可以使你的代码更加简洁高效,易于理解,现在大多数框架都会使用这种机制。
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("admin");
System.out.println(person.getName());
%>
</body>
但是我们使用<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>
JavaBean中无参的构造函数改成有参的,会出现异常,这是因为<jsp:useBean>
的内部原理是 new了一个无参的构造函数
※ JSP:setProperty
<jsp:setProerty name="对象名称" property="属性名" param="参数名" value="值">
四种模式
<jsp:setProperty name="对象名称" property="*"/>自动匹配
<jsp:setProperty name="对象名称" property="属性名称"/>指定属性
<jsp:setProperty name="对象名称" property="属性名称" param="参数名称"/>指定参 数【很少用】
<jsp:setProperty name="对象名称" property="属性名称" value="内容"/>指定内容【很 少用】
当我们没有学习到<jsp:setProperty>
时,我们获取表单的信息,然后导入到javaBean对象中是这样的一种情况:
<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的名称一致
是通过反射来做的,调用了内省的方法!
※ JSP:getProperty
<jsp:getProperty name="对象名" property="属性名"/>
<%--使用<jsp:getProperty>输出--%>
<jsp:getProperty name="person" property="username"/>
<jsp:getProperty name="person" property="age"/>
EL:Expression Language 表达式语言
它的作用就是替换和简化jsp页面中java代码的编写
EL表达式支持简单的运算符:加减乘除取摸,逻辑运算符。empty运算符(判断是否为null),三目运算符
empty运算符可以判断对象是否为null,用作于流程控制!
三目运算符简化了if和else语句,简化代码书写
<%
List<Person> list = null;
%>
${list==null?"list集合为空":"list集合不为空"}
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对象
<%--模拟数据回显场景--%>
<%
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表达式完成普通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/ideal.tld" %>
<html>
<head>
<title></title>
</head>
<body>
//完成了HTML转义的功能
${fn:filter("<a href='#'>点这里</a>")}
</body>
</html>
在JSP页面中指明使用标签库
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
JSTL全称为 JSP Standard Tag Library 即JSP标准标签库
JSTL作为最基本的标签库,提供了一系列的JSP标签,实现了基本的功能:集合的遍历、数据的输出、字符串的处理、数据的格式化等等!
EL表达式可以很方便地引用一些JavaBean以及其属性但是仍然不够完美,它不能遍历集合,做逻辑的控制。
Scriptlet的可读性,维护性,重用性都十分差!JSTL与HTML代码十分类似,遵循着XML标签语法,使用JSTL让JSP页面显得整洁,可读性非常好,重用性非常高,可以完成复杂的功能!
在JSP中不推荐使用scriptlet输出,推荐使用JSP标签
<%@ taglib %>
core标签库是JSTL的核心标签库,实现了最基本的功能:流程控制、迭代输出等操作!
core标签库的前缀一般是c
常用的三个JSTL标签
属性:
test 必须属性,接受boolean表达式
如果表达式为true,则显示if标签体内容,如果为false,则不显示标签体内容
<%--如果带过来的名字是admin,那么可以登陆--%>
<c:if test="${param.name=='admin'}">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登陆">
</c:if>
<%--如果带过来的名字是admin888,那么就是注册--%>
<c:if test="${param.name=='admin888'}">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="注册">
</c:if>
它相当于java代码的switch语句
使用choose标签声明,相当于switch声明 使用when标签做判断,相当于case 使用otherwise标签做其他情况的声明,相当于default
<c:choose>
<c:when test="${param.name=='admin'}">
欢迎管理员
</c:when>
<c:when test="${param.name=='user'}">
欢迎用户
</c:when>
<c:otherwise>
识别不出你是谁
</c:otherwise>
</c:choose>
forEach为循环标签,相当于Java中的while和for
之前我们在使用EL表达式获取到集合的数据,遍历集合都是用scriptlet代码循环,现在我们学了forEach标签就可以舍弃scriptlet代码
向Session中设置属性,属性的类型是List集合
向Session中设置属性,属性的类型是List集合
遍历session属性中的List集合,items:即将要迭代的集合。var:当前迭代到的元素
<%
List list = new ArrayList<>();
list.add("admin");
list.add("zhangsan");
list.add("lisi");
session.setAttribute("list", list);
%>
=====================================================
<c:forEach var="list" items="${list}" >
${list}<br>
</c:forEach>
Map对象有稍微地不一样保存的不是每个迭代的对象,而是Map.Entry
<%
Map map = new HashMap();
map.put("1", "tom");
map.put("2", "jack");
map.put("3", "jack”);
session.setAttribute("map",map);
%>
<c:forEach var="me" items="${map}" >
${me.key} ${me.value}<br>
</c:forEach>
特别说明:本篇中 第二 第三篇部分内容转载来自 java3y 所写jsp第四篇内容,在作者基础上摘出片段,附上链接:
https://juejin.im/post/5a7919045188257a76630a23
如果内容中有什么不足,或者错误的地方,欢迎大家给我留言提出意见, 蟹蟹大家 !^_^
如果能帮到你的话,那就来关注我吧!(系列文章均会在公众号第一时间更新)