JSP的本质

JSP本质上就是Servlet, 正常情况下, 它会在第一次被访问的时候被容器转化成Java代码, 然后再从Java代码编译成.class文件, 之后实际就和Servlet没区别了, 也就是多个请求也只有这一个实例, 最终仍然是通过多线程单实例的方式来完成任务. 所以其实就算你写的JSP代码有问题, 也只有在用户访问它的时候才能看到, 项目部署的时候是看不到的. 当然某些容器提供部署的同时完成代码编译加载的功能, 默认是不开启的.

JSP中几种常见的元素

JSP其实就是html中带了一些其他的元素以实现表现层(viewer)的功能, 下面是一些常见JSP元素 :

1. Scriptlet : <%    %> : 里面可以写Java代码, 这里的Java代码在最后会被统一移到生产的类Servlet类的类似serveice的方法里面 :
<% System.out.println(123); %> 2. 表达式 : <%= %> : 里面的内容将作为out.println()的参数, 被打印在页面上 :
<%=
config.getInitParameter("name")
%>
3. 声明 : <%! %> : 里面可以声明类的属性和方法, 用来弥补scriptlet中声明变量的均为局部变量的缺陷
<%!
private static int x = 3; private void print(){
System.out.println(123);
}
%> 4. 指令 : <%@ %> : 向容器提供一些特殊的指示 :
这里例子是page指令的import 和 contentType属性, 还有其他属性, 同时其实指令还有include和taglib, 之后有机会再提.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.LinkedList" %> : 导入你需要在这个Jsp/Servlet 里面使用的包

JSP中的Servlet

上面说到JSP本质上就是Servlet, 那么也就是Servlet里面的一些东西它也有 :

隐式对象

在Scriptlet内部的一些常用的隐式对象(这里要注意的是只能在Scriptlet内部也就是那个类service方法的内部使用, 因为这些变量实际上都是在其内部定义的) :

  1. out : Servlet中需要通过resp.getWriter()来获得.
  2. request, response : Servlet中作为doGet/doPost/... 等方法的参数传入.
  3. session : 即Servlet中的HttpSession, 在Servlet通过req.getSession()来获得.
  4. application : Servlet中通过getServletContext()来获得
  5. config : Servlet中通过getServletConfig()来获得. (没错, JSP也可以设置初始化参数, 具体设置方法在后面提到)
  6. pageContext : 里面包含了其他隐式对象的引用

可以覆盖的方法

下面是可以覆盖的几个方法, 其作用和Servlet相同就不多说了 :

<%!
@Override
public void jspInit(){ } @Override
public void jspDestroy(){ } %>

JSP初始化参数的设置

然后来看看JSP如何设置初始化参数 :

    <servlet>
<servlet-name>indexJsp</servlet-name>
<jsp-file>/test.jsp</jsp-file> <init-param>
<param-name>name</param-name>
<param-value>index.jsp</param-value>
</init-param>
</servlet> <servlet-mapping>
<servlet-name>indexJsp</servlet-name>
<!--<url-pattern>/xixi</url-pattern>-->
<url-pattern>/test.jsp</url-pattern>
</servlet-mapping>

可以看到其实和Servlet大同小异, 要注意的是, 这里如果mapping中url-pattern不用jsp的实际路径而是使用虚拟路径的话, 会出现一个问题, 如果用户知道真实的jsp的路径, 他仍然可以访问, 但是那个jsp并不会获得初始化参数. 必须要使用url-pattern对应的路径才能获得初始化参数. 由于行为逻辑基本与Servlet相同, 所以在执行jspInit的时候已经可以使用getServletConfig()来获得初始化参数了.

属性的设置以及获取 :

类似于Servlet, 我们可以通过以下隐式对象来设置属性 :

  1. application 对应 ServletContext
  2. session 对应 HttpSession
  3. request 对应 HttpServletRequest
  4. 这里多了一个pageContext 也可以用来设置属性, 它设置的属性只在该page内共享, 但它也能设置/查找上面三者的属性, 具体规则就不表了.

JSP的转折

令人遗憾的是, 上面所说的大部分JSP元素, 实际上都是不推荐使用的 : 例如Scriptlet, 表达式 和声明(这三者均属于脚本代码), 因为这些东西使得Web的前后端之间无法很好地分离, 甚至在web.xml中存在禁用这三种元素的标记 :

    <jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<scripting-invalid>true</scripting-invalid>
</jsp-property-group>
</jsp-config>

一旦设置这个标记之后, 服务器部署正常, 但是一旦访问使用了上述元素的JSP网页, 网页直接HTTP 500. 那我们该使用什么呢? 答案是标准动作 + EL(expression language) + JSTL标记...

标准动作

首先要对bean有个基本的认识, 如果没有认识百度一下吧, 这里我个人由于接触的比较多就不提了, 由于在Java中bean没有在语言层面上实现所以有点麻烦. 这里我使用的Student类的源代码 :

package beanPackage;

public class Student {
private int age;
private String name; public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

JSP中总共有三个bean标准动作 :

1.   <jsp:useBean id="s" class="beanPackage.Student" scope="application" /> 这里scope指的是查找的范围, 默认为page, 可以设置为application, session, request, page分别对应上面提到的jsp中的4中属性设置的方式, 而你属性设置中其实是一个键值对, 这里的键就对应该标签中的id, 由于设置的属性均为Object, 所以还需要有class来识别其class, 而且这里的class必须使用完全限制名, 在上面使用page指令的import属性导入模块是无效的. 要注意的是, 这里之所以要这三个属性, 实际上是因为最终JSP还是要转化为Java代码的. 这句话也就会转化为类似 : beanPackage.Student s = (beanPackage.Student)application.getAttribute("s")的话.
2. <jsp:getProperty name="s" property="name"/> 这里的s对应上面已经获取到的Student的id, property对应Student中的name属性. 这句话会在html中打印这个student的名字.
3. <jsp:setProperty name="s" property="age" value="21"/> 这里s对应上面获取到的Student的id, age对应Student中的age属性. 这句话会将这个Student实例的age改为21.

附加 :

关于useBean

实际上useBean总是会返回一个bean对象, 就用上面的例子来说<jsp:useBean id="s" class="beanPackage.Student" scope="application" />, 如果在application内没有这个Student, 那么它会利用bean的无参数构造函数自动构造一个然后添加到application当中. 基于这个特性, 实际上这个标签的还可以写为 :

  <jsp:useBean id="s" class="beanPackage.Student">
<jsp:setProperty name="s" property="name" value="Fred" />
<jsp:setProperty name="s" property="age" value="21" />
</jsp:useBean>

useBean内部的内容只有当没有在作用域内找到该对象时才会执行.

实际上jsp:useBean之中还可以设置type, 例如你想到生成的对象是Human s = new Student()的时候, 那么这句话可以写为

<jsp:useBean id="s" type="beanPackage.Human" class="beanPackage.Student" scope="application" />

另外如果只有type没有class, 此时如果能在作用域内找到对象, 那么这个对象的引用将是type中指定的类型, 如果不能找到, 直接报错.

关于setProperty

实际上setProperty中还可以设置param :

  <jsp:useBean id="k" class="beanPackage.Student">
<jsp:setProperty name="k" property="name" param="name" />
<jsp:setProperty name="k" property="age" param="age" />
</jsp:useBean>

这个param能直接从request的参数中寻找名字为name和age的参数并且赋值给我们此处的Student实例. 更牛逼的是, 如果你request参数的名字和你实际Property的名字相同的话, 就比如上述例子中, 则可以直接省略param :

  <jsp:useBean id="k" class="beanPackage.Student">
<jsp:setProperty name="k" property="name" />
<jsp:setProperty name="k" property="age" />
</jsp:useBean>

当然最牛逼的还是这个, 使用*, 这种情况下容器会自动迭代所有request中的参数来尝试匹配你的bean的property, 并设置为对应的值.

  <jsp:useBean id="k" class="beanPackage.Student">
<jsp:setProperty name="k" property="*" />
</jsp:useBean>

其实这里还有一点, 我的age的类型是int, 但是request传入的所有的参数值都为String, 但是仍然能够匹配, 这是因为容器提供了基本类型和String的自动转换. 另外如果我们实例的类有父类, 父类的属性也会得到匹配.

EL

EL在JSP 2.0 已经成为规范的一部分, 它有自己的语法, 属于另外一类JSP元素. 当然也有忽略EL的方法, 这里不表. 有了标准动作为什么需要EL呢? 就一个很简单的例子, 比如上面的Student有一个性质是Book, 我们想要显示这个Book的name :

<jsp:getProperty name="k" property="book"/>   --> 会打印 book.toString

这时候如果用el的话就是 :

${k.book.name}

el表达式实际上很简单 : 任何语句都包含在${}当中, 然后可以用.和[]来取值, 用点时, 左边只能是map或者JSP四个作用域中的某个作用域的属性(某个bean), 而右边必须是map的键或者是bean的Property. []则使用空间更大, 左边可以放map, bean, list 和 数组. 这里你可以直接用index来获取list或者array中的某个值. 例如${list[1]} 或者 ${list["1"]}, 这两者是一样的, 最终都会转化为1, 然后寻找list中的第二个. 如果是在四个作用域内已经设置过的属性(某个bean), 实际上并不需要使用xxxScope.xxxBean.xxxProperty或者是xxxScope["xxxBean"].xxxProperty, 直接xxxBean.xxxProperty即可. 查找顺序是 page, request, session, context依次查找.

el中的隐式对象 :

  1. pageScope, requestScope, sessionScope, applicationScope这4个分别对应4个作用域下的保存属性的map
  2. param, paramValues 这是http请求参数的map, 后者用于存在多个同名参数情况.
  3. header, headValues 这是请求首部的map
  4. cookie 关于cookie的map, Servlet提供的是一个数组, 所以这里比Servlet要方便.
  5. initParam 这个不是Servlet的初始化参数而是context的初始化参数, 也就是整个应用的初始化参数的map.
  6. pageContext 这个不是map, 这个和上面scriptlet中那个一样. 可以用这个访问Servlet的初始化参数 : ${pageContext.servletConfig.getInitParameter("name")}

这里还有内容暂时不提, 还有el函数和jstl没有提, 先留个坑, 以后有时间再填...

JSP中的包含与转发

JSP中有两种包含方式 :

1. <%@ include  file="the_jspFile_path"%> : 这种方式的包含实际上发生在jsp被转换成java代码之前, 也就是说, 将被包含的jsp直接替换到该语句的位置, 然后将形成的新的的jsp转化为java代码, 然后编译加载并提供服务. 所以这种包含也称之为静态包含.

2. <jsp:include page="the_jspFile_path" /> : 这种方式的包含实际上不算包含, 当请求来临时, 主jsp文件和被包含jsp文件各自被转换编译加载后, 由主jsp文件形成的类运行时调用被包含jsp文件形成的类, 个人感觉可以理解为请求被包含的jsp文件形成的类在运行时帮助主jsp文件形成的类完成一部分页面的构造工作. 所以这种包含也称之为动态包含. 这种包含还可以有参数.

<jsp:include page="the_jspFile_path"><jsp:param name="param_name" value="param_value" />
</jsp:include> 对于被包含页面而言, 请求参数中增加了上述添加的这个.

处于效率考虑, 个人感觉没有特殊要求的话, 统一使用第一个比较好...

JSP标准动作中还存在一个用于转发的动作, 该动作和 :

<jsp:forward page="the_page_path" />
实际上该动作等同于
req.getRequestDispatcher("/WEB-INF/jsp/login/login.jsp").forward(req, resp);
是在服务器端进行的, 用户并不能看到url的变化. 同时该动作不需要担心写在jsp中部会导致转发之后之前加载的页面会打印出来, 因为发生这个动作时容器会清空缓冲区, 理论上只要没有使用flush()显式地将缓冲区内容打印到页面都行. **当然之前提到的jsp include动作中的增加参数语句在这里同样试用.**

关于JSP的更多相关文章

  1. myeclipse学习总结一(在MyEclipse中设置生成jsp页面时默认编码为utf-8编码)

    1.每次我们在MyEclispe中创建Jsp页面,生成的Jsp页面的默认编码是"ISO-8859-1".在这种情况下,当我们在页面中编写的内容存在中文的时候,就无法进行保存.如下图 ...

  2. jsp前端实现分页代码

    前端需要订一page类包装,其参数为 private Integer pageSize=10; //每页记录条数=10 private Integer totalCount; //总记录条数 priv ...

  3. jsp中出现onclick函数提示Cannot return from outside a function or method

    在使用Myeclipse10部署完项目后,原先不出错的项目,会有红色的叉叉,JSP页面会提示onclick函数错误 Cannot return from outside a function or m ...

  4. jsp页面无法识别el表达式的解决方案

    今天在写一个springmvc的小demo时,碰到一个问题,在jsp页面中书写为${user.username}的表达式语言,在浏览器页面中仍然显示为${user.username},说明jsp根本不 ...

  5. 浅谈JSP中include指令与include动作标识的区别

    JSP中主要包含三大指令,分别是page,include,taglib.本篇主要提及include指令. include指令使用格式:<%@ include file="文件的绝对路径 ...

  6. 浅谈JSP注释

    HTML注释 JSP文件是由HTML尿急和嵌入的Java程序片段组成的,所以在HTML中的注释同样可以在JSP文件中使用.注释格式:<!--注释内容--> <!-- 欢迎提示信息! ...

  7. JSP 标准标签库(JSTL)

    JSP 标准标签库(JSTL) JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能. JSTL支持通用的.结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签, ...

  8. Nginx服务器之Nginx与tomcat结合访问jsp

    本文使用linux centos系统 本文概述: JSP是一种动态网页技术标准.使用的方式是在HTML文件中插入程序段和JSP标记,而形成JSP文件.使用JSP开发WEB应用可以跨平台开发.但jsp需 ...

  9. 基于jsp+servlet图书管理系统之后台万能模板

    前奏: 刚开始接触博客园写博客,就是写写平时学的基础知识,慢慢发现大神写的博客思路很清晰,知识很丰富,非常又价值,反思自己写的,顿时感觉非常low,有相当长一段时间没有分享自己的知识.于是静下心来钻研 ...

  10. JSP页面跳转的几种实现方法

    使用href超链接标记      客户端跳转 使用JavaScript               客户端跳转 提交表单                        客户端跳转 使用response ...

随机推荐

  1. js进阶 11-3 jquery中css属性如何操作

    js进阶 11-3  jquery中css属性如何操作 一.总结 一句话总结:通过css()方法 1.attr和css是有交叉的,比如width,两者中都可以设置,那么他们的区别是什么? 其实通俗一点 ...

  2. 阿里云Java镜像:基本配置和总体说明

    本文中的内容,来自阿里云镜像系统自带的说明.  下次不想用镜像了,感觉也没有节省多少工作量. 一.软件 1.nginx /usr/local/nginx 2.tomcat-7 /var/tomcat/ ...

  3. 【u024】没有上司的舞会

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就 ...

  4. mysqlbinlog命令使用

    常用参数:--start-datetime=datetime 从二进制日志中第1个日期时间等于或晚于datetime参量的事件开始读取.datetime值相对于运行mysqlbinlog的机器上的本地 ...

  5. TensorFlow 辨异 —— tf.add(a, b) 与 a+b(tf.assign 与 =)、tf.nn.bias_add 与 tf.add

    1. tf.add(a, b) 与 a+b 在神经网络前向传播的过程中,经常可见如下两种形式的代码: tf.add(tf.matmul(x, w), b) tf.matmul(x, w) + b 简而 ...

  6. 微信小程序开发实战视频教程发布

    昨日(9月23),腾讯终于发布了没有APPid,无需申请也可以进行微信小程序开发的视频教程了,我在在第一时间尝试并发布了这7个小视频教程,入门足够了.... 各位免费拿去,慢慢享用: 链接: http ...

  7. JDBC连接数据库中CallableStatement执行有参存储过程及注解其他

    Oracle的建有参存储过程的过程 procedure pro_01(v_01 in number,v_02 out varchar2) as begin select name into v_02 ...

  8. nil和Nil和NULL的判断

    ,nil和Nil和NULL的判断 开 发过程中,我们通过http请求,后台返回json数据,而有时数据里某一字段的值为null-,然后我们把此值赋值给 NSArray,NSdictionary,或是N ...

  9. KVC设置系统自带属性,不管是不是私有的属性

    KVC   可以设置系统自带属性,不管是不是私有的属性:    1,   2,  

  10. Input ANR处理流程

    ANR时间区别便是指当前这次的事件dispatch过程中执行findFocusedWindowTargetsLocked()方法到下一次执行resetANRTimeoutsLocked()的时间区间. ...