JSP4

前言

返回 JSP2

  • 在EL和JSTL代码实现第一版中,为了理解EL和JSTL怎么应用,我们需要做一个案例加以演示,就是模拟做一个学生的查询的功能,并且已经把这个案例基本的内容写出来了。那下面呢,我们在这个学生查询的页面上student.jsp,演示一下这个EL表达式:
  • EL表达式是一套简单的计算规则,用于给JSP标签的属性赋值,也可以直接用来输出。表达式也可以脱离标签单独使用。
  • EL表达式的作用可分为以下几类:
    1.访问Bean的属性
    2.输出简单的运算结果
    3.获取请求参数

1.学生查询功能案例之EL表达式的作用

  • 那么EL表达式,它的作用有点类似于这个jsp表达式,但是又不止于此,就是说EL表达式呢,它可以获取数据,又能够显示数据,它的作用,是比较这个综合的,那么,我们看一下,它的具体的这个功能。EL表达式,它的作用有3个,第一个是访问bean的属性,那这里的bean,指的就是JavaBean,而我们所写的这个案例当中的实体类,Student.java,还有这个Course.java,它们就是JavaBean,所以,这样的数据,发送给jsp,jsp呢,就可以利用这个EL去访问这个Bean的属性。然后第二个作用呢是,它在访问这个属性的同时,可以进行一些运算;再一个呢,EL表达式还可以直接获取请求的参数。那3种功能,咱们挨个来演示,先看第一个。

1.EL表达式访问属性

EL表达式作用之访问bean属性
  • 访问bean的属性,这样吧,咱们直接写,写完之后呢,再稍微总结一下,那么打开eclipse,打开我们所写的这个student.jsp,然后,我们在body里啊,准备写一些内容,主要是演示一下EL表达式,那第一个要演示的内容,写注释吧,第一个要演示的内容是EL表达式。然后呢,我们要分别演示这个表达式的3点用途,那么第一种用途,就是访问bean的属性,那么现在这个FindStudentServlet,它已经给我发送过来,转发过来了这个学生的对象,我就想访问学生对象中的数据,那么如果没有EL表达式, 我们按照以前的方式来做,我们需要写一个jsp脚本,在脚本里,我们需要写request.getAttribute("stu");,是这样吧,然后呢,得到Student以后,再getName啊,getAge啊,这样去处理,那用EL表达式以后,这个事就简单了,那我想用EL呢,获取这个Student对象中的数据,然后把它输出到一个段落里,这里写个段落。

  • 那么,我们先输出什么呢,输出学生的姓名,那这样写,这个EL表达式,它是以$开头,然后呢,紧接着写个大括号,${},这是它的典型的语法特点,${},然后呢,大括号里面,我们怎么写呢,你直接写你要访问的对象,然后呢,对象点属性就可以,那么,Servlet转发过来的Student对象,名字叫什么,看一下FindStudentServlet.java,名字叫stu对吧,

    //将数据绑定到request上
    req.setAttribute(“stu”,s);

  • 那我这里要写对象的名字啊,叫stu.name,即<p>${stu.name }</p>,那这样就可以了,咱们试一下,访问localhost:8080/jsp3/findStudent
    在这里插入图片描述

  • 怎么报错了,我这不好使么,你们报错,是不是把tomcat关了啊,你们是不是关了tomcat,没关,那你重新部署一下, 重启一下。另外还要注意,这个Servlet转发的时候,你看一看,你确实绑定数据了么,那么你绑定数据是叫stu么,数据的值是这个s么,即req.setAttribute("stu", s);,然后,你写了转发这句话么,这得确定啊,这都得写对然后呢,jsp上,你再写stu,访问的是就是名为stu这个对象,点name就是得到这个对象的name属性值,<p>${stu.name }</p>,那这句话,解释一下,它在运行的时候,它相当什么呢,这大概说一下,这个它底层的话,还比较啰嗦,它大概的意思是这样的,它相当于呢,我执行了这样一句话,request.getAttribute("stu").getName();,它相当于执行了这样一句话。当然了,这句话就是一个意思啊,那如果你直接这样写也不对,因为什么呢,request.getAttribute("stu"),得到是Object对吧,不能直接点getName,还得转型,但我大概就这个意思,这个思路,能理解就好了。

  • 所以你看,如果我们自己呢,写脚本,那我们需要写这样一句话,还得强转,麻烦的呢,那我们用EL呢,就很简洁,直接写对象名点属性OK了,那这是一种方式,那我们想获取这个对象的属性值,还有一种方式,再写一个,比如说,我想输出这个学生的年龄,我把这个标题写一下,要不然的话,数据多了有点乱,刚才我们输出的是姓名,那写一下,<p>姓名:${stu.name }</p>,再来一个,年龄,<p>年龄:${stu['age'] }</p>,那当然了,你要想访问年龄的话,也可以像刚才这样写,<p>年龄:${stu.age }</p>,stu.age对吧,这很明显,那还有一种语法,还可以这样写,stu['age'],stu方括号age,这样也可以。那写完以后呢,试一下,刷新,刚才呢,输出的是姓名,张三,当前呢,又输出了年龄:25,就这样:
    在这里插入图片描述

EL表达式作用之访问对象属性
  • 那很明显,咱们第一种方式,比较舒服一点对吧,这个比较自然,所以,一般情况下,我们都会用第一种方式,那第2种方式呢,一般就比较少用了,所以,把第一种方式掌握就可以了,那么,不但如此,你看,咱这个学生内部啊,还有一个比较特别的属性,是什么呢,course,这个course,它不是一个,独立的一个值,它是一个对象对吧,对象里含有别的值,我们要访问它,怎么访问呢,演示一下,其实有的人凭感觉猜也能猜到,我想输出这个课程。比如说,我想输出学生的课程的Id ,那你觉得,这句话得怎么写,stu,我得先得到这个学生对象对吧,然后点course,course属性是个对象,然后呢,点,应该是这样,点啥呢,应是点id,还是点courseId,是什么呢,那注意啊,是点id, 即<p>课程:${stu.course.id }</p> ,那写完之后试一下,看行不行,可以吧,id为1:
    在这里插入图片描述
  • 那这块为什么,就这个对象的属性,是courseId对吧,那为什么我们写点id呢,那你注意,我们写点name,它的底层会调,对应的getName(),我们写age,它底层会调getAge(),我们写id,它会调getId(),明白这意思吧,我们写这个单词,它在底层执行的时候,会调用与之对应的get方法,如果这里面,<p>${stu['courseId'] }</p>,你写的是courseId,它底层在执行时,会调getCourseId(),咱们有getCourseId()么,没有,所以这里要写id,我们写的单词,和谁对应,不是和那个私有的属性对应,适合那个get方法对应,我们是通过get方法,得到的那个值,是这样的,那你注意,其实呢,这个id,有一个特殊的名字,就叫做Bean属性。这块呢,我们需要再深入再理解一下这个JavaBean,理解一下它的属性。
  • 那咱们回到Course.java这个类当中,加以解释啊,在Course这个类当中啊,我们所声明的CourseId,这是一个属性,这个属性呢,叫做什么呢,它叫做对象的属性,我写个注释,这是对象的属性。

    //对象的属性
    private Integer courseId;
    private String name;
    private Integer days;

对Bean属性的解释以及可能遇到的问题
  • 那你注意,它并不能称之为Bean属性,它不是bean属性,那什么是bean属性呢,这个getId()方法,那我们再说说这个getId啊,说一下什么是bean属性,什么叫Bean属性呢,这个你可以呢,有多种角度去解释,这个第一种解释是,我们将get/set方法中的,get和set去掉,并且将得到的剩余的单词,首字母小写,那这个就叫Bean属性,我写一下,就是第一种解释,是去掉get或set,剩下的单词,然后呢,首字母小写。你比如说啊,咱们这个getId方法,我把get去掉,剩个单词Id是吧,首字母小写,id就是Bean属性,CourseId是对象属性,就是这样规定的。那然后呢,还有一种解释,也可以这么讲,就是说,我不去看对象属性,我不去看那个私有的属性,我就看这个类当中的get/set方法,那么我看get/set方法以后,我会猜测,它有什么属性,那你看,我看到了getId,我会以为这个对象中,就应该有个id属性,是这样吧,我看到这个方法,我所直观的认为的,它应该有的属性,就是Bean属性,就是说,怎么说呢,通过get和set方法,所这个分析出来的属性,就你所认为的属性,就是bean属性。
  • 或者呢,你简单这样认为吧,你就认为什么是bean属性呢,就是get/set方法,但又不全,不是完全等于get/set方法,需要把get/set去掉,简单这么理解。那其实也很容易想到啊,就是说tomcat也好,还是说这个,某一项技术也好,还是呢,将来我们所讲的Spring框架也好,那么它们都有能力,去调用我们所写的bean,那么它们在访问 我们所写的bean的数据的时候,它一定是通过get/set方法来访问的,它一定不能够直接访问这个私有的属性,对吧,是吧,框架或者是第3方的某些技术啊,它在访问我们这个对象的时候啊,一定是访问get/set,那换句话讲呢,它一定访问的是bean属性,不是直接访问对象属性,所以这个要明确,不过呢,大家也不要把它当作负担,就是你了解一下,那万一说面试官呢,想跟你深究这个事,你能有这个意识,能稍微解释一下,就好了啊。那平时我们开发时啊,我们不会给自己挖坑啊,说哎,这个叫courseId,这个get方法非得叫getId对吧,咱们一般不会这么干:

//对象的属性
private Integer courseId;

//Bean属性
//1. 去掉get/set剩下的单词,首字母小写
//2. 通过get/set方法所分析出来的属性
public Integer getId() {
	return courseId;
}

  • 一般,我写完属性以后,一生成,是一致的对吧,通常情况下呢,对象的属性和bean的属性是相等的,那这里呢,我举了一个极端的例子,是想说清楚,什么是bean属性,那我们访问的真正是谁,那万一你工作时遇到这种情况,你要知道,这是为什么啊,就这个意思。那我们再回到这个页面上,student.jsp上,那你看,刚才呢,我们写的stu.name,写点name,name是bean属性,那其实呢,底层调的是getName()方法,那个getName()方法,对应的是bean属性,我们写的是age,那调的是getAge方法,是吧,我们写的id,调的是,getId方法,和request.getAttribute("stu").getName();类似啊。那这是,我们要说的第一个话题,如何访问bean的属性,非常容易,点点点,就很方便。
EL表达式指定数据访问查找范围
  • 那再看,下一个话题呢,说一下,那就是我们写的是对象点属性,就得到了数据,那这个数据是从哪获取的呢,之前解释了,这个数据显然是从request中获取的对吧,这个其实比较片面,那么未必是从request中得到的这个值,那么EL表达式,它取值的目标,有好几个,不是只有request一个,但是呢,它所取值的目标呢,一定是内置对象当中的几个,因为jsp它只能,默认只能访问内置对象对吧,只能访问隐含对象,所以,EL表达式它取值,一定是从隐含对象中取的。那么,有9个隐含对象,并不是每个对象里都有数据,你比如说out,是用来输出的对吧,它不是用来存值的,还有什么呢,比如说response对吧,用来响应的,也不是用来存数据的,所以,这样的对象里肯定没有数据。那么,9个隐含对象,哪些对象是可以存数据呢:

EL表达式指定对象的查找范围:
1.在编写EL表达式时,可以指定查找对应绑定名对象的范围,如在session中查找绑定名为user的对象时,可编写如下代码:
`${ sessionScope/pageScope/requestScope/applicationScope.user.name }`
2.注意:一旦指定了查找范围,那么在该范围内没有找到指定的绑定对象时,不会再去其他的区域中查找。

  • 看书上的介绍,有4个,分别是,page,request,session和application,这4个对象中,都能存值,那当然了,我们通常常用的,还是request和session,那page,我们很少用它来存值,page之代谁呢,this,那this指代谁啊,指代jsp翻译而成的那个Servlet对象对象对吧,那Servlet对象,它可以get/set,可以存数据么,可以的,但我们通常不会那么做,所以,尽管有,但是一般不用,了解一下。总而言之啊,就是说EL表达式,它默认从4个对象中取值,这4个对象分别是,page,request,session和application,而且呢,它是按照顺序来取,它是这样的,EL表达式,你写一个表达式,比如说,我们写了,这个student点name,${stu.name },那么它底层执行的时候,它看一下啊,那page里有没有name,那如果没有再看,request里有没有name,如果没有再看session里有没有,如果没有,再看application明白吧,它是按次序取值的,这块,我们写一下,就补充一下。
  • 这个EL表达式的取值范围,那么EL表达式,它取值的目标,我们称之为取值范围,讲的是它到底是从哪取的值,那么,它默认从4个隐含对象内依次取值,这4个隐含对象,分别是,page,request,session,以及application。那这个规则一介绍,可能有人就会产生一些想法,它想为什么会这样呢,它为啥要这么做呢,这有什么好处呢,解释一下,它这样设计的原因。咱们这样想,如果说它没有这个规则,那么我们想从request里取值,那因为呢,这4个对象都能存值,我只想从request里取,我是不是得写上request对象,是不是这样,因为啥呢,如果你直接写stu.name,那它凭什么会从request里取值呢,对吧,它也不知道,因为4个对象,都能存值啊,那凭什么它会从request里取值呢,你要是没有这个规则的话,你就必须得写上,request.stu.name,是这意思吧,你就得写,那这样就麻烦了,因为考虑到什么呢,全世界的程序员,可能都会用这项技术,每个人写代码时,都多写一个request对吧,这样的话,就耽误时间。
  • 那它的目的,是希望我们在写这个表达式的时候呢,简短一点,最好这个request.stu.name,request前缀别写,那怎么办呢,它就搞出来这么一个机制,然后呢,默认从4个对象中依次取值,这样的话,我们不用写这个对象名了,反正它会看配置里有没有这个,是吧,没有再看request里有没有,没有再看session,以此类推,反正,从哪个对象中取到,就算哪个,所以,它这样设计的目的啊,是为了简化这个表达式,是为了让表达式简短,是这么想法。那当然有人可能会想啊,说哎,让你按次序取值,比如说4个对象里都没有,都没有还好说,都没有就空值呗,对吧,还好说啊,那如果都有呢,如果都有的话,第一个对象取到,就算了,对吧。那有人想,我就想从第3个对象取呢,那这个时候,就得写上对象了,明白吧,就得写上前缀了,所以,默认是这样,是为了简化表达式,如果说真的,4个对象中有重复的值,你就想从后面那个取,你得明确声明到底是哪个对象,再写一下,那么,也可以明确指定取值范围。
  • 那这个取值范围怎么指定呢,这样啊,举个例子,比如我们可以写 requestScope.stu.name,总之啊,你不能直接写request点,你要写requestScope点,Scope是范围的意思,同理啊,你要从session中取值,写sessionScope,以此类推,就写什么什么Scope,那写一下试试啊,那比如说,我就想从session中取这个学生的名字,咱们看一下,别取学生名,咱们换一个吧,比如说取学生的性别,就换一个来取,取个性别,<p>性别:${sessionScope.stu.sex }</p>。那写完以后,你看一下,有数据么,没有,为啥没有啊,因为session中没有值,对吧,咱们都没用session,所以没有,那我们把它改一下,比如说改成requestScope,这回能取到么,改一下,<p>性别:${requestScope.stu.sex }</p>,那因为我们是将数据呢,存到了request里,那我们写成requestScope就可以:
    在这里插入图片描述
  • 看一下,这个可以对吧,那么,这个注意,咱们平时啊,不会这样写,因为平时呢,4个对象中的数据呢,通常不会重复,为什么不会重复呢,因为对象中的数据呢,是我们存进去的,那我们存数据时,避免重复就可以了,明白吧,你不要重复,为啥要重复呢,有人老问这样问题,你看我这两个数据,这俩名字要重复怎么办,我这个重复怎么办,老问这样话题,我是说,没有什么意义,我们写代码时啊,我们一定会规避这个问题,我们写js,我们不可能把两个元素写同样的id,对吧,太愚蠢了,谁这么干啊。我们写这个声明变量啊,甚至做任何处理啊,都不会这么做,通常是会规避的,如果不小心,或者说,某些特殊情况必须要重名的话,那再特殊处理,所以,基本上不会这样用,这了解一下啊,取值范围。

2.EL表达式支持的一些数据运算

  • 以上所说的是,我们如何使用EL表达式,从这个bean中获取数据,那下面我们再说呢,EL表达式的第2个作用,就是呢,它在取到数据以后,可以直接对这个数据进行一些运算,那么支持各种运算:

使用EL表达式进行运算:EL表达式可以做一些简单的计算,可将结果直接输出或给JSP标签的属性赋值
1.算术运算:"+","-","*","/","%",注意,"+" 号只能求和,不能够连接字符串。
2.逻辑运算:"&&","||","!"
3.关系运算:">",">=","<","<=","==","!="
4.empty:用来判断一个字符串是否为空,或者一个集合是否为空,以下四种情况结果为true:空字符串,空的集合,值为null,找不到对应的值。

  • 比如说算术运算,加减乘除余等,都支持,比如说逻辑运算与或非,关系运算,大于,小于,等等,都支持,然后呢,还有个特殊的运算叫,判空运算,empty,是判断某数据是否为空,你比如说啊,一个字符串,那字符串是null,表示空对吧,字符串空串,长度为零也是空啊,比如说集合,为null是空,集合中有集合,new了,但没有任何数据,也是空。判空啊。那这么多运算,我们就挑几个演示一下,不挨个演示了,就差不多,都差不多类似的意思。
  • 那回到这个案例当中student.jsp,我们演示这几种运算,接着写,这是要演示的第二个话题,1.2,这是进行运算,就是支持运算,那先输出这样一个内容,比如说,我想输出呢,学生的年龄加3,就是年龄加3,输出这样一个内容,<p>年龄+3:${stu.age+3 }</p>。那学生年龄呢,是stu.age,加3,直接写就可以了,非常方便,同理呢,减乘除余,都可以直接写,写完以后呢,试一下,加3以后啊,是28。再来啊,比如说呢,我想判断呢,这个学生的年龄,它是不是介于20到30之间,判断,就是介于20到30之间,<p>介于20-30间:${stu.age>20 && stu.age<30 }</p>,那么这个判断呢,我们把这个逻辑运算,关系运算,都用上了,所以呢,一句话,咱们用上两种运算,这样就方便,写完以后呢,试一下,输出结果为true对吧,因为是25介于20到30之间:
    在这里插入图片描述
  • 那么除了这个算术运算,逻辑,关系运算之外,还有一个特殊的判空运算,那么,你想判断谁是否为空,就empty谁,咱们写一下试试啊,比如说啊,我想判断这个,判断什么呢,判断学生的兴趣,是否为空,<p>是否为空:${empty stu.interests }</p>,那我们写empty,后面跟着谁,就是判断谁是否为空,那写完以后呢,再看一下,看这个兴趣是否为空,false,不为空,对吧,是有值的。
    在这里插入图片描述

3.EL表达式获取请求参数

  • 那这是EL表达式,它的第2个作用,就演示完了,那后面呢,我们再做项目时,也会用到,用到时我们再看。那么EL,还有第3个作用,它还能够获取请求参数,如果本次请求当中呢,浏览器给服务器传了参,那很有可能呢,我们在jsp上,想获得这个参数,那么如果说我们不用EL表达式,我们直接写jsp脚本,可以获得,我们得写什么呢,request.getParameterValue(""),你想得到参数,就得调这样的方法。

使用EL表达式获取请求参数值:
1.`${param.username}`等价于`request.getParameter("username");`
2.`${paramValues.city}`等价于`request.getParameter("city");`

  • 那么,我们也可以呢,写EL表达式获取参数,那么用EL获取参数怎么获取呢,是这样的,固定的,param开头点参数名,即${param.username},如果呢,是多个参数,是一组参数,是个多选,你要这样写,paramValues点参数名,即${paramValues.city},点前面这个单词固定,后面是参数名,是动态的,看情况。那下面,我们演示一下,不过呢,你看,我们这个案例,有参数么,没有参数,那没有参数怎么办呢,加个参数,那加参数怎么加呢,这个似乎有点麻烦了,按我们正常来讲,我们要加个参数,得写个表单吧,你要传参得有表单么,那我们还得写一个,再写一个网页,写个表单,有点麻烦,那这里呢,是我想尽快的演示这个场景,怎么办呢,还有这个,就是怎么说呢,变通的办法,这样,我们在访问这个Servlet时,我们,我们敲的路径,之前是这样敲的,是/findStudent,是这样吧,这样敲的,我们可以呢,用这个路径啊,做些文章,怎么做文章呢,我在路径上再敲啊 ,问号,比如说,比如说什么呢,这个user=xxx,可以这样敲,即/findStudent?user=xxx
  • 那你想,我在路径上认为的加了个问号,加了个key=value,那这东西能不能传给服务器,可不可以呢,肯定是可以的,因为什么呢,我们即便是写表单,如果表单是get请求,是不是也这么干的啊,我现在没有表单,我自己敲上去,和有表单加上去是一样,就get请求,可以的,如果你不信的话,可以试一下,试试啊。你看,你打开浏览器,我们要试的话,你得按一下F12,F12看一下,F12/Network,然后呢,我们就在地址栏敲啊,比如说问号user等于,等于什么呢,比如说,tarena,随便写一个,即localhost:8080/jsp3/findStudent?user=tarena,问号user等于什么什么,回车,回车以后,我们看Network当中的这个请求,看一下,这个请求是不是get请求,是get请求,那么请求路径上,是不是有这个这个参数,user=tarena,有,再往后看,你看后面这个,叫Query String Parameters,这个参数是不是也可以传过来,user: tarena,和那个表单发送数据的时候的get请求一模一样,是吧,所以这样是可以的。
  • 而且以后啊,我们在做某些功能的时候,就会这么干,我们在写某些路径时,就会直接写死一个参数,那以后遇到时,详细再说,先这样,那回到程序中来,当前,咱们地址栏已经有了参数了,我想呢,在jsp上,获得这个参数,用EL表达式来获取,那这是我们要演示的第3个内容,就是获取请求参数。那我再写个段落,把这个参数呢,输出再看一下,<p>参数:${param.user }</p>。那么这个写完以后,咱们看一下结果,参数:tarena,因为呢,咱们当前呢,传的参数是一个参数,所以呢,获取的时候,是param点参数名,不用paramValues
    在这里插入图片描述
  • 那这个EL表达式,它就3种作用,说完了,很容易,后面在做项目时,还会再练习。然后呢,说完EL以后,我们再去看JSTL标签。那么EL表达式呢,它负责这个取数,负责显示数据,那么对数据呢,它不能做处理,而JSTL呢,它能对数据加以处理,那么,它包含了很多,很多种处理方案,多种处理方案,比如说循环啊,比如说判断啊,比如说格式化啊,等等,都可以,那么这个JSTL的功能呢,非常的多,咱们没有办法呢,挨个都演示,那我们只演示几个常见的,核心的内容,那么在演示之前,我们对于JSTL呢,再加以解释,加以说明。

4.完整代码实现student.jsp

<%@page pageEncoding="utf-8"%>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>查询学生</title>
	</head>
	<body>
		<!-- EL表达式 -->
		<!-- 1.1访问Bean的属性 -->
		<p>姓名:${stu.name }</p>
		<p>年龄:${stu['age'] }</p>
		<p>课程:${stu.course.id }</p>
		<!-- EL表达式的取值范围:
			它默认从4个隐含对象内部依次取值:	
			page->request->session->application
			也可以明确指定取值范围:
			如:requestScope.stu.name -->
		<!-- <p>性别:${sessionScope.stu.sex }</p> -->
		<p>性别:${requestScope.stu.sex }</p>
		<!-- 1.2支持运算 -->
		<p>年龄+3:${stu.age+3 }</p>
		<p>介于20-30间:${stu.age>20 && stu.age<30 }</p>
		<p>是否为空:${empty stu.interests }</p>
		<!-- 1.3获取请求参数 -->
		<p>参数:${param.user }</p>
	</body>
</html>

2.学生查询功能案例之JSTL标签的作用

JSPT标签的发展

  • JSTL(JSP Standard Tag Library):JSP标准标签库,JSP标签是Sun公司定义的一套标准,由Apache组织基于这套标准开发的一套标签库后又转给Sun公司,被称为JSTL,成为了JavaEE5.0的核心。
  • 首先呢,什么是JSTL,JSTL它是这样一组单词的缩写,JSTL(JSP Standard Tag Library),那么翻译过来,是叫做JSP标准标签库,那你注意,其实啊,这个JSTL标签这项技术,并不是Sun定义的,其实最早是Apache定义的,然后呢,Apache定义这套标签以后啊,它一看,这好啊,有了这个东西以后呢,再也不用去写这个JSP标签了,所以大家基本上都不用Sun,那个原生的那个JSP标签了,都用这个JSTL标签了,Sun呢,感觉有点失落,哈,有点打脸了,我开发 一项技术,没人用,都用Apache的了,对吧,我是正宗的,这个太不像话了,不太好。
  • 所以呢,Sun是这么干的啊,它找Apache说,你看,你干的不错,是吧,这标签很好,我想把它作为标准,以后咱们就标准化它怎么样,给我吧,Apache就把这个标签给它了,那行,Apache组织还是挺这个,怎么说呢,还是挺不错的,就是说它是致力于开源,希望呢,这项技术有长远的发展,所以说,只要这技术能发展好,给谁都无所谓,给Sun了,Sun呢,把这个内容拿过来,形成了一套标准,那么,JavaEE5.0当中呢,就包含了这项内容,所以说JSTL啊,它目前已经是JavaEE中的一个核心的内容了。不管谁做的吧,总之是核心的,是标准的东西,我们工作时一定会用,只要你用JSP,就一定会用这个东西,很关键很重要。那正是因为啊,这个,这项技术是由Apache开发的,它默认没有涵盖到这个jdk当中,默认也没有涵盖到JavaEE的那个包内,明白吧,是一个独立的包,所以呢,我们想用这个JSTL呢,还得单独引个包,单独导个包,那这个包啊,tomcat就没有了,你想引入tomcat的包,不行了,我们就得用maven了。

引入JSTL标签依赖及常见问题

  • 那下面呢,我们就导这个包,导个包。那用maven导包的话,我们得打开maven服务器搜对吧。搜一下,那我们打开浏览器,我们访问一下这个maven服务器,阿里云的Maven服务器,那么搜什么呢,就搜jstl就行,然后回车,那么搜索到的结果有很多很多,其实呢,一般的都能用,但也不绝对,有的确实有问题,以前我就遇到这个坑啊,遇到会有问题的情况( 在jsp页面中引入jstl标签库报错问题 ),那一般呢,我们就找那些个名字比较简单的,或者说呢,和这个Servlet或Apache什么,沾边的那种,那这里用哪一个呢,用这个就行,就是组名和项目名都叫jstl,就这个就可以了,都小写的jstl,然后呢,有众多的版本,那么 ,最后一个版本是1.2是吧,它默认选中的就是1.2,你选择jstl-1.2.jar,然后呢,右侧,你就能够看到这个,这个包,这段代码,那把这段代码复制一下:
    在这里插入图片描述

<dependency> 
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

  • 复制好以后,打开eclipse,把它粘贴到我们这个项目中的配置文件里,展开了这个jsp3这个项目,打开pom.xml,然后呢,这样吧,咱们把这个tomcat关了,因为一会要重启,不重启不生效,导包以后要重启,所以我先关了。打开pom.xml,然后呢,不要直接copy进来,拷进来之前还得自己再补充一个标签叫,<dependencies></dependencies>,你一敲de就有提示,然后呢,我们把刚才的那段代码,粘贴到dependencies之内,粘贴过来以后,一保存,这个就下载了,保存以后呢,如果下载成功的话,默认没有什么反应,对吧,没有什么提示,但如果失败了<dependencies>这块会报叉对吧。
  • 那如果失败了,怎么办呢,得把这句话删了,<dependency>...jspt...</dependency>删了,这就行么,也不行,因为你导失败的话,它不 是说完全失败,它肯定是下载的时候,中间失败了,它往往是下载了一些文件,但是不全,那我们需要把之前那个残缺的文件删掉,要把缓存删掉,那这个文件缓存到哪去了呢,它缓存在maven.m2之下对吧,那点.m2在哪呢,那咱们从这个eclipse能看出来,Java Resources之下,有Libraries,Libraries之下,有Maven DependenciesMaven Dependencies之下,有这个jstl-1.2.jar这个包对吧,即便是失败了,这个包也会看到,只不过它这个不全:
    在这里插入图片描述
  • 然后呢,这个jar包的后面,有它的路径的提示,在jsp3项目之下,Libraries,它这里面有Maven Dependencies,这里面有这个包,你失败了也能看到这个包的名字,后面呢,jstl-1.2.jar - C:\Users\adminitartor\.m2\repository\jstl\jstl\1.2,就是它存放的目录,啊,那么如果你失败的话,你需要打开点.m2的repository,即.m2\repository,你把谁删掉呢,把jstl,整个目录删掉,即\jstl\jstl\1.2,重新导入,因为,我们从maven导包的这个本质,是从maven服务器下载一个包过来,那下载的过程中有可能网络有问题,有可能会失败,所以,出现问题要知道怎么解决。
  • 那jstl,这项技术很关键,能够改善jsp,这个开发的体验,当然,也是一项非常主流的技术,就是jsp的开发,离不开这项技术,然后呢,因为它早先是apache做的,它是一个独立的包,我们要用的话 呢,还得单独导包,我们通过maven呢,把这个包导进来,那注意,导完包以后呢,这个项目,你需要重新部署,重启才OK,导包的时候一定要重新部署,那么部署的时候分为4步,那么eclipse呢,会把这个webapp拷过去,第2步呢,把它改个名,第3步呢,它会把classes拷过去,最后一步,eclipse呢,还会把这个,我们导的包也拷过去,包也拷过去,现在我们导了一个包,可以看一下,看看是不是这样。你看我的tomcat在D盘,然后呢,找到我的tomcat,那tomcat/wtpwebapp,这里面有个项目叫jsp3,jsp3之内,你看WEB-INF之下,多出了一个文件夹叫lib,那么lib之下,就有jstl这个包,所以呢,一定要有这个意识 ,就是说,我们导了包以后,那我们编码时,这个代码才会编译不报错,我们才可以调用这个包内的东西。

关于JSTL依赖说明及引用c.tld

  • 那么我们项目正式运行时,它jstl-1.2.jar,运行在tomcat之内,tomcat也需要这个包,这个包是要被部署过去的,如果说呢,你是手动导的包,别忘了处理一下,处理这个包。那导完包以后,下一步怎么办呢,那这个jstl这个包下,它这个标签有很多,然后呢,它在配置文件里,对标签加以了声明,那这个配置文件有多份,或者说呢,这个标签有好几类,有好几套,那我们想用哪一类标签,我们需要引入,那先不着急引入,我们先看一下它那个,那个包下的标签是怎么声明的 ,那我展开呢,Libraries`Maven Dependencies,然后呢,我看到了这里面有jstl-1.2.jar,然后呢,我把这个jar包展开,展开以后呢,它里面有很多内容,那我们要看什么呢(我这个模式是改一下,Project Explorer界面,右上方点下三角View Menu/Package Presentation/Flat,改为包模式),我这个jstl-1.2.jar下面有很多这个包,我们不看这所有的包,看什么呢,看最后一个目录叫META-INF,那么META-INF展开以后,它下面是不是有很多tld文件啊,那你注意,每一个tld文件,就是一个配置文件,那这个配置文件之内呢,声明了一类标签,那一类标签有很多个,那我们要说的是哪个呢,是这个c.tld`当中的标签。

  • 那还是那句话,这个标签太多了,没法都说,那我们只说几个,只说最核心的几个,那这个c.tld,是jstl的核心标签,打开这个c.tld看一下,这里标签有很多,然后呢,我们也是从中呢,只挑几个来解释,然后呢,还有别的tld文件,那么我们想用哪一个tld文件,我们需要引入它。那你看c.tld,它这个12行,<uri>http://java.sun.com/jsp/jstl/core</uri>,这叫uri是吧,uri是什么呢,uri就是资源的名字,对吧,就是名字,uri呢,就是这个c.tld,在网络上的名字,那么如果我们想用这个c.tld的话,需要把它引入进入,需要写uri这个名字,这个名字太长了,一般记不住啊,copy一下,一会好用,那当然了,还有其他的tld文件,里面也有不同的名字,你想用哪一套,就把这个copy一下,引入进去,也可以引入多套,都可以。

  • 那这个tld文件,它的后缀是tld,但其实呢,它里面的东西是xml,就不知道为啥叫tld,其实叫xml就可以了,它就是xml。然后呢,一个tld文件,声明了一组标签,一套标签,我们称其为一个标签库,现在呢,我们想引入c.tld,这个标签库,名字已经copy完了,那我们再打开student.jsp,那我想在student.jsp上引入这个c.tld这个标签库,那引入的话,我需要在这个网页一开头引入,在一开头,<!doctype html>之前,那么引入这个标签的话,怎么引入,用一个指令,这指令叫什么呢,叫taglib,<%@taglib uri="" prefix=""%>,即<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

    <%@page pageEncoding="utf-8"%>
    <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <!doctype html>
    <html>...</html>

  • 那么这个指令有两个属性,一个叫uri,一个叫prefix,那你看这个指令,它的名字叫taglib,tag是标签的意思,lib是library的缩写,就是库的意思,标签库,然后呢,uri写上你要引入的那个标签库的名字,就是刚才我们复制的那句话,http://java.sun.com/jsp/jstl/core,粘贴到这来,然后你看这个标签库的命名,它是以这个网站命名的,什么java.sun.com,然后呢,后面是core,核心标签库,那用网站命名啊,是为了避免说重复,因为一个公司,它的域名,肯定别人不会用对吧,别人也不敢占用,所以这个不会重复啊。然后呢,后面是prefix,是前缀,我们在使用这个标签时啊,因为不同的库有很多标签,为了把这个标签区分开来,我们一般呢,习惯于加个前缀,是为了区分不同库的标签的,那哪个库的前缀是什么呢,从哪个tld文件里可以看到,那我再回到c.tld文件,那11行short-name,就是缩写,就是前缀,它前缀就是c,其实你不用看也可以啊,其实呢,这个tld文件的名字,就是前缀,一种习惯,习惯用前缀命名。


  <description>JSTL 1.1 core library</description>
  <display-name>JSTL core</display-name>
  <tlib-version>1.1</tlib-version>
  <short-name>c</short-name>
  <uri>http://java.sun.com/jsp/jstl/core</uri>

  • 所以呢,回到student.jsp,那么前缀应该写成c,然后呢,一会我们再写标签时,以c为前缀,语法是c冒号标签名,是这样的,就是,jsp标签呢,它毕竟要有别于html,不要混淆,所以c为前缀,有区别啊。那么引入了这个jsp标签以后,下面我们在页面上演示,那这样,我就直接在el表达式之前演示吧,省的我还得往后拖拽,这样省点事,就在这写,这是我们要演示第二个内容,JSTL标签,那我们只演示这个核心标签库当中的3个,第一个呢,是if标签,用来判断,第二个呢,是choose标签,也是判断,那choose和if差不多,一个意思,但是有点小的区别,这个区别一会说一下,还有一个,第3个呢,是forEach,用来循环的。那我们在写这个网页上的逻辑的时候啊,最常用的就是判断和循环,所以这个呢,必须要讲,当然,jstl还有很多其他的功能,那我们后面讲项目时,用到哪一个再讲,用不到就算了,工作时呢,再去看,太多了。
学生性别判断功能之JSTL标签if
  • 然后呢,我们讲每一个标签的时候,它标签上其实有很多属性,书上也没有写全,那我们也不是每一个属性都讲,我们只讲常用的属性,那么,后面做项目时,如果说哪个属性又需要用到,我们再讲,所以,书上呢,说的比较啰嗦,咱们就挑这个重要的,常用的讲一下,项目中用到时,再讲,这样。那首先呢,我们演示一下,这个if标签,用来判断,我直接呢,在student.jsp里写,比如说呢,我想在这个地方,输出呢,学生的性别,那么,我想输出学生性别也很容易,写个el表达式就可以,它就会输出那个值,不过呢,它输出的值是什么呢,它输出的是M或F对吧,是一个字母,但你想啊,我们平时给用户看,能让他看M,F么,不能,是希望他看到汉字对吧,男或女,那你看怎么办,我们得到的是M或F,我们希望它输出的是男和女,怎么办,就得判断对吧,如果说这个值是M,我就显示男,如果是F就显示女,做个判断,所以判断在这种场景下会用,那咱们用一下试试啊。
  • 那我在这写个判断,这样写,<c:if test=""></c:if>,这个if标签啊,是一个双标签,c:if,开头,c:if结尾,那么if标签之上,有一个属性,叫test,必须得写,那么test属性里面写什么呢,写判读条件,比如说,如果性别等于M,怎么怎么样,然后呢,双标签之内,写上要显示,要输出的结果,那当等于M时,我们输出男,是这样,这句话的意思是什么呢,当test当中的条件成立的时候,我们在后面输出男, 那这个条件,我们需要,肯定是要取到,性别那个值对吧,还要对值加以判断,那你看,谁有能力取值,还能对值判断呢,el表达式,因为el表达式有3个功能,第一个,获取bean属性对吧,第2个能运算对吧,运算就包括了这个判断了,然后第3个呢,是这个能取请求参数。所以呢,test里面,我们需要写el表达式,写什么呢,${stu.sex=='M'},即<c:if test="${stu.sex=='M' }">男</c:if>
  • 那等于M时输出男,那等于F时输出女对吧,这句话怎么办呢,再写一遍,换个条件而已,<c:if test="${stu.sex=='F' }">女</c:if>,那么这个if标签,它只有if,没有else,所以我们只能写if,没有else,那没关系,我们把条件写成不同的,就可以了,写完以后呢,试一下,我刷新浏览器看一下,性别男,就这样。
    在这里插入图片描述
学生性别判断功能之JSTL标签choose
  • 那这是if标签的用法,非常简单,下面呢,我们再介绍第二个标签的作用,另外一个标签叫choose,那if和choose的区别是什么呢,if标签没有else,那有些时候,我们希望它有else,这个choose可以有,那choose标签也是判断,标签之内有子标签,有when和otherwise,这个when呢,可以写多个,每一个when相当于一个if,第一个when相当于if,再写一个,相当于是else if,最后otherwise相当于是else,是这样的,

<c: choose>
	<c: when test = ""></c: when>
	...
	<c: otherwise></c: otherwise>
</c: choose>

  • 那这个标签,它的优点是,它有else,它又分支,那缺点是什么呢,比较麻烦对吧,没有if那么简单,那if是简单不假,但没有else,就各有利弊,但我们在使用时啊,我们一般呢,我个人还是喜欢用if,没有else也问题不大,我们往往可以把条件,调成相反的条件对吧,相当于else,也可以,所以,一般呢,我们做项目时用if就可以了,那这个choose,我们还是演示一下,比如说,我用这个标签,也是演示输出性别,我们看一下它的作用。那我们在后面接着写,那刚演示的是第一个if标签,然后呢,我们再演示第2个标签,是choose标签,那么我还是呢,输出性别,是用这个choose标签来输出,咱们输出看一下,

	<p>
		性别:
		<c:choose>
			<c:when test="${stu.sex=='M' }">男</c:when>
			<c:otherwise>女</c:otherwise>
		</c:choose>
	</p>

  • 那这个when,里面的when相当于if ,otherwise相当于else,这个when呢,可以写多份,when的里面呢,这个test里,还是写条件,然后呢,标签之内写显示的数据,那我写个条件,就是${stu.sex=='M'},等于M时呢,输出男,否则呢,输出女。那写完以后,打开浏览器,然后刷新一下,那输出的还是男,这两种判断的方式,就是方式不同,但是呢作用相同,那么至于呢,开发时你喜欢用哪一个,随便。
    在这里插入图片描述
学生兴趣遍历功能之JSTL标签forEach
  • 那最后呢,我们再说一个标签,是forEach,那么这个标签,是用来遍历的,它可以遍历数组或集合,遍历之后呢,能够输出,就循环,输出一些内容,那么写一下,这个student.jsp,我们能够接收到这个学生对象,而学生对象当中有一个数组对吧,兴趣,那我就想呢,获取到兴趣,遍历它,把每一个兴趣输出一下看看,那这是我们要演示的第3个jstl标签,就是forEach标签,那我在这,再写一个段落,段落之内,我想循环输出各种兴趣。那这个标签,它也是一个双标签,它就叫forEach,那这个标签之上,有一些属性,首先呢,有一个属性,它叫items,items当中写上你想遍历的数据,那么我想遍历的是兴趣,那这个兴趣的数据,我们需要通过el获取到,然后呢,由forEach标签来遍历,来处理。
  • 所以呢,items里面,我们要写一个el表达式,${stu.interests },那这样写,表示我要遍历的是数组,那每次遍历,我们能够从中得到一个数据,那为了便于我们引用这个数据,我们最好给数据呢,取个名字,那如果给每次所得到的数据取个名字呢,很容易,我们在标签上呢,再写一个属性叫var,var呢,就声明变量名,变量名叫什么都行,比如说我就叫i吧,那我遍历的是这样的一个数组,${stu.interests },每次遍历,我能从数组中得到一个字符串,然后字符串的变量名叫i,那我想呢,每次循环输出i这个变量,那怎么输出呢,得在forEach之内,每次循环输出一下,那咱们用什么能输出呢,用什么可以输出这个变量呢,用el就可以,用el能取数,还能输出对吧,你给它指定变量,它也可以输出,所以,用el就可以了。

	<p>
		<c:forEach items="${stu.interests }"  var="i">
			${i }
		</c:forEach>
	</p>

  • 写完以后,看一下,我们刷新浏览器,循环输出了这个兴趣,篮球足球排球,没问题:
    在这里插入图片描述
完整代码实现student.jsp
<%@page pageEncoding="utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core"  prefix="c" %>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>查询学生</title>
	</head>
	<body>
		<!-- 2. JSTL标签 -->
		<!-- 2.1 if标签 -->
		<p>
			性别:
			<c:if test="${stu.sex=='M' }">男</c:if>
			<c:if test="${stu.sex=='F' }">女</c:if>
		</p>
		<!-- 2.2 choose标签 -->
		<p>
			性别:
			<c:choose>
				<c:when test="${stu.sex=='M' }">男</c:when>
				<c:otherwise>女</c:otherwise>
			</c:choose>
		</p>
		<!-- 2.3 forEach标签 -->
		<p>
			<c:forEach items="${stu.interests }"  var="i">
				${i }
			</c:forEach>
		</p>

		<!-- EL表达式 -->
		<!-- 1.1访问Bean的属性 -->
		<p>姓名:${stu.name }</p>
		<p>年龄:${stu['age'] }</p>
		<p>课程:${stu.course.id }</p>
		<!-- EL表达式的取值范围:
			它默认从4个隐含对象内部依次取值:	
			page->request->session->application
			也可以明确指定取值范围:
			如:requestScope.stu.name -->
		<!-- <p>性别:${sessionScope.stu.sex }</p> -->
		<p>性别:${requestScope.stu.sex }</p>
		<!-- 1.2支持运算 -->
		<p>年龄+3:${stu.age+3 }</p>
		<p>介于20-30间:${stu.age>20 && stu.age<30 }</p>
		<p>是否为空:${empty stu.interests }</p>
		<!-- 1.3获取请求参数 -->
		<p>参数:${param.user }</p>
	</body>
</html>

3.JSTL标签的运行原理

  • 那就这3个标签,还有其他的标签,用到时再演示,那么还有很多标签,那将来我们工作时,那些标签不会用,怎么办,没关系啊,这样,讲一下这个标签的运行原理,然后呢,通过原理,原理你懂了,那么,你去自己看那个标签,也能看懂,所以下面,我们讲一下,这个标签的工作原理,那我们再打开那个c.tld文件去看,通过这个文件,能够看到一些这个线索。打开c.tld,前面呢,<?xml version="1.0" encoding="UTF-8" ?>,这是版本声明,根元素没什么好说的,然后呢,8到12行:是对整个文件的声明,描述啊,文件名啊,版本号啊,这个前缀啊,名字啊,是文件的整个描述,基本描述,

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
    
  <description>JSTL 1.1 core library</description>
  <display-name>JSTL core</display-name>
  <tlib-version>1.1</tlib-version>
  <short-name>c</short-name>
  <uri>http://java.sun.com/jsp/jstl/core</uri>

  <validator>
    <description>
        Provides core validation features for JSTL tags.
    </description>
    <validator-class>
        org.apache.taglibs.standard.tlv.JstlCoreTLV
    </validator-class>
  </validator>

  <tag>
    <description>
        Catches any Throwable that occurs in its body and optionally
        exposes it.
    </description>
    <name>catch</name>
    <tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
        <description>
Name of the exported scoped variable for the
exception thrown from a nested action. The type of the
scoped variable is the type of the exception thrown.
        </description>
        <name>var</name>
        <required>false</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
  </tag>
  ...
  <tag>
    <description>
	Simple conditional tag that establishes a context for
	mutually exclusive conditional operations, marked by
	&lt;when&gt; and &lt;otherwise&gt;
    </description>
    <name>choose</name>
    <tag-class>org.apache.taglibs.standard.tag.common.core.ChooseTag</tag-class>
    <body-content>JSP</body-content>
  </tag>
  <tag>
    <description>
	Simple conditional tag, which evalutes its body if the
	supplied condition is true and optionally exposes a Boolean
	scripting variable representing the evaluation of this condition
    </description>
    <name>if</name>
    <tag-class>org.apache.taglibs.standard.tag.rt.core.IfTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
        <description>
The test condition that determines whether or
not the body content should be processed.
        </description>
        <name>test</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
	<type>boolean</type>
    </attribute>
    <attribute>
        <description>
Name of the exported scoped variable for the
resulting value of the test condition. The type
of the scoped variable is Boolean.        
        </description>
        <name>var</name>
        <required>false</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
        <description>
Scope for var.
        </description>
        <name>scope</name>
        <required>false</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
  </tag>
...
</taglib>

  • 再往后是validator,是一个校验器,用来校验呢,这个语法对不对,然后呢,再往后是这个,就是每一个标签的声明了,那比如说,23行,声明个标签,标签名叫catch,再往后看,但这个没有演示,没有讲,你可以去看,它的这个描述,看它大概的功能,但是得翻译一下,但这个catch标签一般用不上,很少用,然后呢,43行开始,另外一个标签叫choose,刚才讲的就是这个,然后呢,还有比如说,54行,if,等等, 总之呢,这个tld文件里呢,声明了很多标签,然后呢,我们随便找一个来看,比如说,就看这个if标签,其中呢,name是标签名,后面呢,跟着有一个标签叫tag-classtag-class是什么呢,是类,那其实呢,这个if标签,它的背后就是一个类,那虽然说,我们在写jstl标签的时候,那么页面上不用写这个java代码了,但其实呢,这个标签本身就是java代码,它的本质就是java,那这个if标签,它所对应的类叫做,IfTag,比如说那个,choose标签,对应的类叫做ChooseTag,有规律。
  • 所以每一个标签呢,其实它是对应一个类的,那这个类,什么时候会调用呢,是怎么执行的呢,那把这个逻辑关系说一下,加深我们对这个标签的理解。那么我以这个if标签为例,来讲这个话题,这个标签呢,我写到了jsp这个页面上,那么jsp是服务端的组件,它运行在tomcat之内,所以呢,首先呢,我把这个tomcat画一下,假设啊,这个大方块就是tomcat,然后呢,tomcat里有它自己的这个通信组件,这是tomcat,那比如说呢,有用户呢发出请求,想访问服务端的jsp,那么,服务器呢首先,它会找到这个jsp,比如说呢,刚才我们写的是student.jsp,那服务器呢,会找到这个jsp啊,找到以后,服务器并不是会立刻运行这个jsp,运行不了,它是怎么处理的呢,它得怎么处理,jsp怎么运行呢,得翻译,是吧,那么jsp要被翻译成Servlet,才能运行是这样吧,它会被翻译成一个Servlet,这我就简写了,比如说翻译成StuServlet,这有一个翻译的过程。
    在这里插入图片描述
  • 然后呢最终,tomcat调用的是这个Servlet,由Servlet做出响应,那么有人可能会有这样的疑问,说它jsp啊,翻译成Servlet,它是现在这个硬盘上写了一个文件对吧,它写文件一点会用到I/O的,那这个过程肯定是比较耗时的,那既然是一个耗时的行为,它每次都翻译的话,是不是效率会比较低呢,是的,但其实呢,你不用太担心呢,它不是每次都翻译,我们这个jsp啊,翻译一次之后,那个类有了,下次就不用再翻译了,不是每次都翻译,只翻译一次就够了啊。那jsp啊,它是被翻译成了Servlet,而Servlet被执行了,那么jsp当中,我们缩写的标签,jstl标签也一定也被翻译了,所以这个jstl标签,什么时候运行的呢,它是在jsp被翻译时,它被翻译了,然后呢,Servlet被调用时运行的,是这样的。
  • 那我们说一下,这个翻译的这个规则,和这个jstl标签,一些的这个关系,我们以这个if标签为例,那我们在jsp上,要写if标签,首先要写这样一句话,我们需要在顶部写上什么呢,写个指令叫<%@taglib%>,是这样吧,你得写这指令,指令上还得写什么呢,uri,是这样吧,prefix,得写这些内容,总之啊,我们想写标签,首先我们需要在jsp上,写上这个指令,然后呢,我们才去写这个标签的,那比如说,我们刚才写了这个if标签,<c:if>,我简写了。那下面就探讨一下,这个if标签是如何被翻译的,这块我标一下,这是一个翻译的过程,在这个过程之内,标签也将被翻译,是这样的,这个翻译是tomcat做的,所以呢,标签也是由tomcat翻译的,那么tomcat有能力翻译这个标签么,有,因为我们把那个jstl那个包,放到tomcat之内了对吧,它能够获取到这个,那个配置文件,它能从包内得到配置文件,它知道这个标签对应那个类,是怎么回事,它知道。
  • 那么tomcat是这么干的啊,首先呢,它看前缀c,根据前缀,它能不能在taglib,它能不能找到对应的taglib呢,是不是可以,我们就是,tomcat根据前缀c,能够找到prefix,根据prefix能够找到uri,这个uri就是,我们要调的那个,标签库,对吧,名字,于是呢,tomcat就能够找到,对应的标签库,c.tld文件,那么因为,我们把jstl标签那个包,放到了tomcat之内,部署过去了,所以呢,tomcat之内就有这个包,包里就有这个,就有这个库,就有c.tld文件,这个文件之内,刚才我们也看了,它有一些内容,从8到12行,有很多内容,其中呢包括,这个uri,它包括uri,然后呢,后面从几十行开始,是一个一个标签的声明,啊,比如说,它声明了一个tag:

<tag>
	<name>if</name>
	<tag-class>IfTag</tag-class>
</tag>

  • tag之内,它声明了,是叫name是吧,那if标签的name就叫if,然后呢,这个标签还对应了一个类,后面还接着声明了一个tag-class, 然后呢,那个if标签的类叫做IfTag,在c.tld文件中,我们也看到了,总之啊,我们一部署项目,这个项目之内,就有了这个c.tld文件,那么tomcat在翻译标签的时候,它就会去读这个配置文件,从配置文件当中,它就知道,这个if标签,它到底对应哪个类,是这样一个目的。所以呢,它是根据c,找前缀,根据前缀,找uri,根据uri确定是,哪个tld文件,找到tld文件之后,它在这个文件之中呢,找谁呢,找这个if标签,找到if标签以后,标签知道了,标签对应的类,知道了吧,就知道了。那知道这个类以后,它就调这个类的方法,它会把这个标签呢,翻译成Servlet当中的一句话,这句话就是对这个类的调用,那翻译成什么呢,在这写一下,它会翻译成这样一句话,new IfTag().doTag()
  • 还需要说一点,这个,不管是哪个标签,它所对应的类,都实现了同样的接口,都有同样的父类,所以呢,它们的方法是一样的,那么所有的标签类,都有一个方法叫doTag,那么翻译时,tomcat会调用这个类的doTag方法,它会这样调,new IfTag().doTag(),它是这样执行的,new IfTag().doTag()。最终呢,我们在jsp上写了一个标签if,而这个标签最终被翻译到这个StuServlet这个类当中,成为了new IfTag().doTag(),这样一句话,那你这个标签对应的是哪个类,这里面,就翻译成哪个类的doTag方法,规定的规则就这样,那这个过程呢,我标一下,就主要是看这个翻译标签的过程,第一步,通过前缀找prefix;第2步,通过prefix,找到这个uri;第3步,根据uri知道了,哪个文件;第4步在文件之内,去找名为if的标签;找到标签以后,第5步,去找标签对应的类;第6步,将这个标签翻译成一句话,是对这个类的调用,这样一句话,最终标签,变成了new IfTag().doTag()。我们写的标签,最终变成这句话,new IfTag().doTag(),这样的。那这是jstl的一个运行的过程,运行的原理。
  • 那么,在最后再概况一下。就是jstl标签,被tomcat翻译成,翻译成什么呢,Servlet当中的一句话,哪句话呢,即,就是new 标签类().doTag(),就这样一句话,那么你的标签对应哪个类,它就翻译成这样一句话,就完了,它是这么做的。然后呢,程序在最终运行的时候,tomcat调的是new IfTag().doTag(),那执行的不是这个标签,执行的是这句话,new IfTag().doTag(),由这句话,向浏览器输出一些内容,所以本质上呢,还是java,jstl的本质还是java。

4.JSTL自定义标签的实现与应用

  • 那么尽管呢,我们讲了原理,那可能还有的标签呢,你还是看的时候,这个费劲,可能你对标签的理解呢,还是不到位,所以工作时,如果我们用其他的标签的话,可能你还是不太会用,然后呢,有的人呢,还又这个,不太愿意去查手册,不太擅长去去搜索一些东西解决问题,那怎么办呢,咱们再讲一讲这个标签,怎么讲呢,讲一下自定义标签,这个标签,你像Apache可以写,Sun可以写,我们其实自己也可以写,它允许我们自己定义标签,那如果说我们自己都会定义标签了,那么这个标签呢,别人做好以后,你去看,就能看懂了,所以下一个话题,我们讲一讲呢,我们怎自己去定义标签。不过呢,这个其实,我们工作时,通常是不用自己去定义标签的,因为jstl自带标签基本上够用了,太多了,没有必要自己定义,那我们讲这个案例,主要是希望你加深对标签的理解,希望将来你遇到新的标签的话,能够自学,把它学会了,这个意思。

  • 因此呢,这个自定义标签这件事,相对来说呢,这个代码是次要的,就是主要是思路,因此呢,我就重新写一遍了这个,怎么自定义标签 ,不重新写一遍了,太麻烦了,太罗嗦,那怎么办呢,就是,我给你呢,之前就写好了一套代码,我们把之前写好的代码,拿过来看一下,拿过来一运行,体会一下,就好了,然后,讲一讲呢,它里面的代码的逻辑,逻辑你理清了就可以了。那之前呢,写好的这个自定义标签,有一个servlet课前准备资料,文件夹叫servlet-doc,里面有一个目录叫,自定义标签,当然中文有可能乱码,那乱码的话,咱们还得把它改一下,不然的话呢,我们从里面拷东西,拷不出来了,那这样吧,我们把这个文件夹名改一下,改成英文的,别改中文了,改成英文的就可以了,改成叫,就叫tag吧,改一下,要不然,一会copy代码时,拷不出来了,乱码。

  • 然后,我们打开这个目录,看一下它里面有什么,有两个东西,一个呢,是java文件,一个呢,是tld文件,SysdateTag.javas.tld,那么,我们想开发自己的标签,并不麻烦,我们只需要写一个类,并写一个tld文件,加以配置就可以,只需要写着两个内容即可,那写这两个内容的规则是什么,介绍一下,然后演示。我们想自己定义标签呢,第一步,我们需要写一个类,那这个类,标签类有规则,它们要继承同一个父类,这个父类叫SimpleTagSupport,那任何标签,都要继承这个父类;然后呢,我们在写这个子类的时候,我们要重写父类的doTag方法,然后呢,在doTag方法里,处理我们想要处理的逻辑;逻辑处理好以后,最后,我们写配置文件,我们自己写一个tld文件,加以配置就可以了。那么写一个类啊,并不麻烦,主要是配置文件比较啰嗦,我们说一下这个流程,这个套路。那这样,我先说一下,那之前写好的这个类啊,这个标签,它的作用是什么,它的名字叫SysdateTag对吧,Sysdate是系统时间对吧,那这个标签的作用是什么呢,是用来向浏览器输出当前的系统时间的,是这个功能,用来向浏览器呢,输出当前的系统时间,那我们做好以后,那怎么用呢,先说一下它怎么用啊,先知道它的用途。

  • 它是这样用的,我们在使用它的时候呢,是这样,前缀呢,是s,因为你看,它的tld文件,以s开头,s.tld是吧,一般呢,前缀和文件名相同,然后呢,那标签类叫SysdateTag.java,标签名叫sysdate,然后呢,这个标签呢,是个单标签,所以直接可以结束了,结束以后呢,它会在浏览器上,输出一个当前的服务器时间,这个时间,是这样的格式,2017年,5月份,多少号,18号,然后呢,16点03分,26秒,是这样的,那么如果呢,你对这个时间的格式不满意,可以格式化,如果你想格式化它,怎么办呢,这样写,我们可以呢,在这个标签之上,再写一个属性,叫format,format里呢,写上你想要的格式,比如说我想要这样的格式,就是yyyy/MM/dd,那最终我的得到的就是什么呢,2017,然后呢,05/18。

    <s:sysdate/>
    2017/05/18 16:03:26
    <s:sysdate format="yyyy/MM/dd"/>
    20170518

  • 总之啊,这个标签是一个自定义的标签,它默认可以向浏览器输出呢,2017/5/18 16:03:26,这样格式的数据,当前的服务器时间,那我们还可以通过属性format,对它的格式加以这个限制,加以改变。那么了解了这个标签的功能以后,下面来说一下,那这个标签的这个,它是怎么开发的,那这样,我们把这个代码呢,粘贴到我们的,项目中来去看,这样方便,首先呢,把这个java文件,SysdateTag.java,复制一下,那复制好以后,打开你的eclipse,把它粘贴到哪去呢,看一下,把它粘贴到jsp3里面去,放到src/main/java/web之下,放这来,还没完呢,除了这个类之外,不还有个tld文件,对吧,还得粘贴那个tld文件,你把那个s.tld文件也复制一下,然后呢,s.tld文件放到哪去呢,有要求,我们把s.tld呢,放到WEB-INF之下,放这来。粘贴过来以后,这个代码准备好了,就可以用,也可以看,这样吧,咱们先不讲这个代码怎么写的,咱们先用一下,那么因为呢,你改了代码,所以呢,我们最好,我们需要把这个项目呢,重新部署一下,重启tomcat,把这个项目重新部署一下,重启tomcat。

  • 那么,重启以后,我想在student.jsp上使用它,那么我们想用这个s.tld这个库,我们需要呢,在这个网页上,引入这个库,我们需要再写一个taglib,我们再写一个taglib,那么这个自定义标签库的名字,这个前缀是s,然后,库的名字,我写的比较简单,能记住,库的名字是这样的,斜线,/lhh-tags,即<%@taglib uri="/lhh-tags" prefix="s" %>,我们在这块呢,引入了这个库以后就能用了,那么咱们用一下,先体会一下,然后,再讲解它怎么写,我在哪用呢,在后面,forEach标签以后,这算是第4个内容,2.4,是自定义标签,我就直接写这个标签啊,<p><s:sysdate/></p>,单标签,先不写属性,它会按照默认的格式,输出一个系统时间:

      >`<!-- 2.4 自定义标签 -->`
      `<p><s:sysdate/></p>`
    

在这里插入图片描述

  • 写完以后看一下,它输出了啊,2017/05/18 16:10:40,默认格式是这样,然后呢,如果你觉的这个格式不好,可以改,我们可以在标签上,写一个属性叫format,来自定义格式,随便定义,<p><s:sysdate format="yyyy-MM-dd"/></p>,那当我自定义完格式以后,再看,刷新浏览器以后,格式就变了:
    在这里插入图片描述
  • 那么总之,经过简单的测试,我们发现呢,这个自己写的标签,也是可以用的,那能用基础上,我们说一下,这个自己开发标签的这个思路,第一步,我们需要自己写一个类,那我自己写的类呢,叫SysdateTag.java,一般你的标签是什么功能,就叫什么什么Tag,那既然我这个是输出系统时间,我就叫SysdateTag,那我们打开这个类看一下,这个类呢,它继承于SimpleTagSupport,那么任何的标签类都要继承于这,然后呢,标签要处理业务,它需要呢,重写父类的方法,重写父类的doTag方法;那么,在doTag方法里面,我们主要是想向浏览器输出一个服务器时间,所以呢,这啊,我就创建了一个当前的时间,new Date,创建时间以后呢,对时间需要格式化,我就格式化了,SimpleDateFormat,格式化了,那么格式化的话,我们需要给它一个默认的格式,但是呢,这个格式,还允许用户改,所以呢,我把它做成了一个,一个这个变量,然后呢,通过set方法可以改变它的值。
  • 那么我们想让这个用户,传入一个参数,去改变这个值,其实有多种方式,第一种方式,我们想改变对象中的数据,我们可以呢,在构造时传入这个参数,可以,第二个呢,我们可以在调方法时传入参数,但是呢,对于这个标签类不适用,因为什么呢,这个标签类,它不是我们自己去调用的,它是这个翻译之后,它是由tomcat去调用的,那tomcat呢,它毕竟是这个程序,它比较笨,它只会调无参的构造器,所以呢,你想在构造器加参数不行,然后再一个,我们所看到的这个方法,doTag方法,这个方法是从父类继承过来的,它是不可以有参数的对吧,就这样,所以这个没有参数,所以呢,我们想给这个对象传参呢,就只剩最后一种方式,我们需要通过set传。那所以呢,这里呢,format,我把它做成了这个成员变量,然后呢,通过set可以传入它的值,改变它的值:

private String format = "yyyy/MM/dd HH:mm:ss";
public String getFormat() {
	return format;
}

public void setFormat(String format) {
	this.format = format;
}

  • 那么它的默认值呢是,这样的一个格式,yyyy/MM/dd HH:mm:ss,那你传入别的值,可以把它覆盖。那么我们调用SimpleDateFormat,可以把这个date格式化为一个字符串String,格式化完以后,最关键的一步,是我们需要,把这个字符串,输出到这个浏览器上,那因为这里是这个标签类,是类,我们需要自己去写代码去输出,那要输出的话,我们得想到,要获取某些对象来输出,那在这个Servlet里也好,还是在jsp里也好,我们想输出东西,就得用什么呢,输出流对吧,那个Writer。那其实啊,这个SysdateTag.java,它最终呢,是运行在那个Servlet当中的,因为刚才我们讲了那个原理,就是你在jsp上写的标签,最终呢,翻译成类似于这样的一句话new IfTag.doTag(),那这句话呢,是在Servlet调用时执行的,是这样的。

  • 所以呢,我们要获取这个输出流,在这个Servlet当中呢,往外输出东西,那获取输出流啊,这块呢,有点麻烦,因为你看,咱们这个方法呢,没有提供参数,所以呢,没法直接从参数中获取到,我们想要的东西,那怎么办呢,这个父类,给我们提供了一个有用的东西,父类有个方法叫getJspContext,那jsp返回的是JspContext,那这个类呢,咱们以前没有讲过,但是我们说过另外的一个类叫PageContext,这个PageContext是9个内置对象之一对吧,是一个管理者对吧,通过它,能够得到其他的8个对象,然后呢,这个类PageContext,和这个方法getJspContext所返回的类,是一个父子关系,PageContext extends JspContext,PageContext,它是这个类JspContext的子类,关系是这样的,然后呢,你看,父类这个方法getJspContext(),返回的是JspContext,声明是这样,但是呢,它实际实现时返回的是PageContext,所以你看,我调这个getJspContext()方法之后,我是不是可以把它强转为子类型PageContext,可以吧,就是这个getJspContext()方法,它声明返回父类型,然后呢,实际在实现时,它实际返回的是子类型PageContext,声明返回父类型,实际返回子类型,然后呢,我们得到,得到以后可以强转为子类型,是可以的。

  • 这个叫多态,就是说多态,主要是为了省事,你比如说,我们平时说话,也习惯于这样讲,说这个东西,那个东西,其实呢,东西可以是其他一切物体的父类,是吧,所以,我们这样说方便,那我说这个东西,你知道它是个鼠标,那我说它是鼠标的时候呢,我其实又把它这个东西,强转为鼠标了,所以其实就是这么一个,转换的这个这个思路,所以你看,getJspContext()这个方法,它返回的是父类型,声明返回的是父类型,但实际返回的是子类型,我们得到以后强转一下,就可以,多态。那么如果对这个地方,还是感觉,比较不舒服,感觉比较别扭,那这个继承啊,多态啊,尤其是这个多态,其实,并没有从根本上去理解它,可能还是有的地方,没有理解到位,或者是,即便是你理解了,时间久了,也就忘了,需要复习。

  • 总而言之吧,就可以这样写这句话,PageContext ctx = (PageContext)getJspContext();,我们通过这样的方式,能够得到PageContext,就把这个套路记住也可以,得到了PageContext以后,它是一个管理者,通过它能够得到其他的8个对象,所以,我们可以调这个getOut()方法,得到这个JspWriter,然后呢,通过Writer,输出呢,这个时间,就完了:

    JspWriter out = ctx.getOut();
    out.println(now);

  • 然后呢,在这里强调了一下,我们输出完时间以后呢,这块呢,不能关闭流,为啥呢,因为jsp上,不止是当前一个标签,还有别的标签对吧,它们共用一个流,你在这关闭了,别的标签用不了,所以,这里面不能关闭流,最后,这个tomcat会统一关闭的,你就别关闭了。那这是这个类的书写方式,那么关键点就在于什么呢,就是怎么获取PageContext,从而获取这个输出流,好用,这是一方面。再一方面呢,如果说我们这个类当中啊,需要一些参数,需要别人传入参数,那怎么传,我们需要声明成员变量,通过set方法来传,那说白了,这个set方法是什么呢,就是bean属性。

  • 那这个类写完以后呢,还不能直接用,需要进行配置,只有你把配置好了,tomcat才能找到它,才能调用它,所以呢,我们还写了配置文件,看一下那个配置文件,就是s.tld。那这个s.tld文件的结构啊,这个我也记不住,我们写的时候呢,可以参考,你可以参考c.tld,它怎么写的,我们照搬就是了,照葫芦画瓢就可以了,那这也是我照着c.tld呢,抄的,然后呢,我们要知道每一个标签呢,大概的意思,然后能够自己改,就好了。那么c.tld,咱们看一下c.tld,它的第一行呢是声明这个文件的版本,<?xml version="1.0" encoding="UTF-8" ?>,那这句话,我也不用改,抄过来;然后呢,第2行空了,我也抄过来,第3行到第6行,它是根元素,这也不用动,抄过来,<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">,所以你看,我的s.tld,是吧,1到6行,一模一样,是吧,没什么好说的,然后呢,再看c.tld,接下来呢,8到12行,是对整个文件的一些基本的声明:


  <description>JSTL 1.1 core library</description>
  <display-name>JSTL core</display-name>
  <tlib-version>1.1</tlib-version>
  <short-name>c</short-name>
  <uri>http://java.sun.com/jsp/jstl/core</uri>

  <description>这是我自己的标签库</description>
  <display-name>My Tag</display-name>
  <tlib-version>3.1</tlib-version>
  <short-name>s</short-name>
  <uri>/lhh-tags</uri>

  • 那这些声明,我都拷过来,并且加以呢,修改,你看我改的,8到12行,那第一个标签叫description描述,我描述说,这是我自己的标签,随便写,写啥都行,然后呢,是这个display-name,是展示名,随便写,我写成My Tag,然后呢,版本tlib-version,这个版本呢,你写成什么都行,但按正常来说,比如说我这是第一版,应该写1.0对吧,下一版,1.1,大版本就2.0,2.1,这样的,但是一般呢,我们国内的企业呢,有一个套路,一般不愿意写1.0,为啥呢,因为大家一看,1.0,这是第一版,第一版意味什么呢,bug比较多,大家不敢用明白吧,所以说,这个写1.0,人家都不爱用,所以一般我们写一个高比较高的版本,我这写了个3.1是吧,这都套路。以前呢,以前我们开发一个软件,第一版啊,是5.6,第一版就5.6,卖给客户,客户一看5.6,这以前应该有5个版本了啊,他以为有5个版本了,应该很稳定,就买了,如果他知道是1.0的话,我估计是够呛,但是国内企业很多都这样,咱也不说啥了啊,都是套路啊,所以呢,这里我写的是3.1啊,不是1.0。
  • 然后,后面呢,short-name就是前缀啊,这个文件的前缀是s, 我就写s了,uri呢,是资源名,怎么写都行,不用非得说写成网站,你写成什么都行,我这里呢,写个简短的,/lhh-tags,好记。这是一些配置,随便配,然后呢,往后,这个tag标签,就是声明一个,一个标签,然后呢里面有很多子元素,子标签,像description,描述这个标签的作用,那我们自己写可以写汉语来描述,直观一点,我说了,这个标签是用来,输出服务器的时间的,非常直观,然后呢,这个标签的名字name,我叫sysdate,使用时要写这个名字就可以了,标签类tag-class,是web.SysdateTag,这是标签类,然后,后面还有啊,后面有一些内容,不是很好理解了,还有个标签呢,叫body-content,当然这个标签,都是我从那个c.tld,拷过来的,它有什么,我就拷什么,然后呢,我是照着改的,那当然,还差了一点啊,c.tld啊,它除了这个描述以为,除了tag以外,还有个validator,这是一个校验类,这校验类呢,不用也可以,咱们没有精力写它,就不写了,没关系,只要你保证你写的东西对就行了。
  • 然后呢,你看,这个tag之下,有描述description,有名字name,有类名tag-class,那body-content,是声明这个标签 ,标签内部可以写什么东西,标签的内部可以写什么东西,就是它内容是什么,那因为我这个标签,是个单标签,咱们刚才用不是单标签么,对吧,单标签没有内容,所以内容为空,body-content是empty啊,那如果不为空的话,如果是个双标签的话,写什么呢,一般写jsp,就是jsp中所有的东西,比如说el表达式啊,比如说标签啊,它都支持,一般就写成jsp,就这样的。除此意外呢,这个类还有一个属性叫format,通过format,我们可以呢,传入格式,那这个属性你要加以描述,tomcat才会调,你不描述,它不会调,所以呢,我们描述的这个属性啊,描述属性叫attribute,attribute之内,然后呢,description描述,说这个属性是什么意思呢,用来设置时间格式,然后呢,有name,name叫format,是声明属性名,是什么属性名,写format。
  • 然后呢,后面啊,还有两个标签,一个叫requred需要,意思是这个属性,是不是必须的,是不是一定要写的,那我这写false,就不是必须的,可以不写对吧,可以不传入对吧,因为你不传入的话,就是默认值么,那就是false,可以不写;然后呢,还有一个啊,这个更特别了啊,叫rtexprvalue,这么一个单词,这好几个单词的拼一起了,它是rt是return缩写,expr是表达式expression的缩写,value是值,连在一起的意思是,就是说,是否可以呢,用el表达式,给这个属性赋值,可以,你用什么赋值,只要赋值就可以,无所谓,所以呢,我写了true,你可以用el给这个属性赋值,可以的啊。总之啊,这个标签,就是c.tld里写什么,那我们就这个照着写,就照着弄就好了,那大概了解下,如果工作时,确实是你要写的话,你要照着扒就是了,但通常不用我们写,因为,其实jstl自带的标签,差不多够用。
  • 那这个自定义标签啊,大概就说完了,刚才也演示完了,那么主要啊,就是通过说这件事,希望你深入体会一下,我们所讲的这个jstl的原理,另外呢,熟悉一下,这个jstl,这个tld文件当中的,配置的内容,然后万一将来要写,或者说,万一将来你需要学习其他的标签的话,能看懂,那如果你自己都知道,该怎么去定义标签的话,别人写好的标签,你去看他的这个内容,虽然是英文的,你翻译一下,也能大概看懂,这样就好学了,这样的话呢,其他的标签,你就会用了。那这个后面呢,就是我们做项目时,还是有的时候会用上,用上时,再看。那现在这个EL表达式和jstl标签,大概就介绍完了,那到目前为止,我们已经学会了不少东西了,和web项目相关的,比如说Servlet,我们会写了,jsp也会写了,然后呢,并且呢,我们是利用mvc模式去写的,对吧,是有一个标准的模式的,然后呢,jsp上面的,这个java代码,我们可以用标签来代替,我们也可以呢,提高了jsp中的这个开发的效率,所以当前阶段的主要的知识,我们就介绍的差不多了。

完整代码实现

1.在项目jsp3下的src/main/java/web包下的SysdateTag.java:
package web;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class SysdateTag extends SimpleTagSupport {
	private String format = "yyyy/MM/dd HH:mm:ss";
	public String getFormat() {
		return format;
	}
	
	public void setFormat(String format) {
		this.format = format;
	}
	
	@Override
	public void doTag() throws JspException, IOException {
		//创建服务器时间
		Date date = new Date();
		//格式化时间
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		String now = sdf.format(date);
		//将时间输出给浏览器
		//PageContext extends JspContext。
		//该方法声明返回JspContext,
		//但是实现时返回的是PageContext。
		//所以可以将其强转为PageContext,
		//从而获取其他8个隐含对象。
		PageContext ctx = (PageContext)getJspContext();
		JspWriter out = ctx.getOut();
		out.println(now);
		//此处一定不能关闭流,因为其他的标签
		//也要用这个流。
	}
}
2.在项目jsp3下的src/main/WEB-INF目录下的s.tld:
<?xml version="1.0" encoding="UTF-8" ?>

<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">
    
  <description>这是我自己的标签库</description>
  <display-name>My Tag</display-name>
  <tlib-version>3.1</tlib-version>
  <short-name>s</short-name>
  <uri>/lhh-tags</uri>
  
  <tag>
    <description>用来输出服务器的时间</description>
    <name>sysdate</name>
    <tag-class>web.SysdateTag</tag-class>
    <!-- 声明该标签可以包含哪些内容 -->
    <body-content>empty</body-content>
    <attribute>
        <description>用来设置时间的格式</description>
        <name>format</name>
        <!-- 是否必须给这个属性赋值 -->
        <required>false</required>
        <!-- 是否可以用EL给此属性赋值 -->
        <rtexprvalue>false</rtexprvalue>
    </attribute>
  </tag>
</taglib>
3.在项目jsp3下的src/main/java/web包下的 HelloTag.java:
package web;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class HelloTag extends SimpleTagSupport{
	private String msg;
	private int qty;	
	
	public HelloTag() {
		System.out.println("HelloTag()");
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		System.out.println("setMsg() " + msg);
		this.msg = msg;
	}

	public int getQty() {
		return qty;
	}

	public void setQty(int qty) {
		System.out.println("setQty() " + qty);
		this.qty = qty;
	}

	@Override
	public void doTag() throws JspException, IOException {
		System.out.println("doTag()");
		/* 可以通过继承自SimpleTagSupport类提供的方法
		 * 来获得pageContext。
		 * pageContext提供了获得其它所有隐含对象的
		 * 方法。
		 */
		PageContext pc = 
				(PageContext)getJspContext();
		JspWriter out = pc.getOut();
		
		for(int i = 0; i < qty; i ++){
			out.println(msg + "<br/>");
		}
	}
}
4.在项目jsp3下的src/main/WEB-INF目录下的mytag.tld:
<?xml version="1.0" encoding="UTF-8" ?>

<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.1</tlib-version>
  	<short-name>t1</short-name>
  	<uri>test</uri>
  	
  	<tag>
    <name>hello</name>
    <tag-class>web.HelloTag</tag-class>
    <body-content>empty</body-content>
    <attribute>
        <name>msg</name>
        <!-- true表示该属性必选 -->
        <required>true</required>
        <!-- true表示该属性可以动态赋值 -->
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
    		<name>qty</name>
    		<required>true</required>
    		<rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>
</taglib>
5.在项目jsp3下的src/main/webapp目录下的student.jsp:
<%@page pageEncoding="utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core"  prefix="c" %>
<%@taglib	uri="/lhh-tags"  prefix="s" %>
<%@taglib	uri="test"  prefix="t1" %>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>查询学生</title>
	</head>
	<body>
		<!-- 2. JSTL标签 -->
		<!-- 2.1 if标签 -->
		<p>
			性别:
			<c:if test="${stu.sex=='M' }">男</c:if>
			<c:if test="${stu.sex=='F' }">女</c:if>
		</p>
		<!-- 2.2 choose标签 -->
		<p>
			性别:
			<c:choose>
				<c:when test="${stu.sex=='M' }">男</c:when>
				<c:otherwise>女</c:otherwise>
			</c:choose>
		</p>
		<!-- 2.3 forEach标签 -->
		<p>
			<c:forEach items="${stu.interests }"  var="i">
				${i }
			</c:forEach>
		</p>
		<!-- 2.4 自定义标签 -->
		<p><s:sysdate  format="yyyy-MM-dd"/></p>
		<p><t1:hello msg="Hello World" qty="${1 + 3}"/></p>		
		
		<!-- EL表达式 -->
		<!-- 1.1访问Bean的属性 -->
		<p>姓名:${stu.name }</p>
		<p>年龄:${stu['age'] }</p>
		<p>课程:${stu.course.id }</p>
		<!-- EL表达式的取值范围:
			它默认从4个隐含对象内部依次取值:	
			page->request->session->application
			也可以明确指定取值范围:
			如:requestScope.stu.name -->
		<!-- <p>性别:${sessionScope.stu.sex }</p> -->
		<p>性别:${requestScope.stu.sex }</p>
		<!-- 1.2支持运算 -->
		<p>年龄+3:${stu.age+3 }</p>
		<p>介于20-30间:${stu.age>20 && stu.age<30 }</p>
		<p>是否为空:${empty stu.interests }</p>
		<!-- 1.3获取请求参数 -->
		<p>参数:${param.user }</p>
	</body>
</html>

5.总结

JSTL标签概要

写在后面

文中如有侵权行为,请联系me。。。。。。。。。。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值