05 JSP(上)

JSP(上)

标签(空格分隔): JavaWeb


认识JSP

为什么需要JSP

我们在Servlet程序中可以通过PrintWrite进行输出,打印到web页面上。那么我们其实也可以通过这种方法打印html语句。但是这种方法是在是太麻烦了,也比较愚蠢。因为我们不但要在Servlet中写Java代码,还要通过这种方式写网页的静态样式,整个Servlet程序的代码将非常臃肿,编写和维护都将非常困难!

实际上在很多动态网页中,绝大部分内容都是固定不变的,只有局部内容需要动态产生和改变。并且对大量静态内容的美工设计和相关HTML语句的编写,未必是程序员所擅长的。因此为了弥补 Servlet 的缺陷,SUN公司在Servlet的基础上推出了JSP(Java Server Pages)技术作为解决方案。

什么是JSP

JSP是简化Servlet编写的一种技术,它将Java代码和HTML语句混合在同一个文件中编写,只对网页中的要动态产生的内容采用Java代码来编写,而对固定不变的静态内容采用普通静态HTML页面的方式编写。

JSP页面是由HTML语句和嵌套在其中的Java代码组成的一个普通文本文件,JSP 页面的文件扩展名必须为.jsp。JSP文件就像普通的HTML文件一样,它们可以放置在WEB应用程序中的除了WEB-INF及其子目录外的其他任何目录中,JSP页面的访问路径与普通HTML页面的访问路径形式也完全一样。

JSP运行原理

WEB容器(Servlet引擎)接收到以.jsp为扩展名的URL的访问请求时,它将把该访问请求交给JSP引擎去处理。

每个JSP 页面在第一次被访问时,JSP引擎将它翻译成一个Servlet源程序,接着再把这个Servlet源程序编译成Servlet的class类文件,然后再由WEB容器(Servlet引擎)像调用普通Servlet程序一样的方式来装载和解释执行这个由JSP页面翻译成的Servlet程序。

可以在WEB应用程序正式发布之前,将其中的所有JSP页面预先编译成Servlet程序。

写一个JSP

新建一个 JSP 页面, 在 body 节点内的 <% %> 即可编写 Java 代码.

<body>
    <% 
        Date date = new Date();
        System.out.print(date); 
    %>
</body>

隐式对象

什么叫隐式对象

<%%>中,一般我们必须声明一个对象,然后才能使用。隐式对象就是不用声明也能使用的对象!

其实也不是没声明,而是在jsp生成的Servlet文件中默认声明好的对象。

public void _jspService(HttpServletRequest request, HttpServletResponse response)
        throws java.io.IOException, ServletException {

    PageContext pageContext = null;
    HttpSession session = null;
    ServletContext application = null;
    ServletConfig config = null;
    JspWriter out = null;
    Object page = this;

    //...

    //使用  <% %> 编写的代码在此位置. 可以用到 request, response, pageContext, session
    //application, config, out, page 这 8 个隐含对象. (实际上还可以使用一个叫 exception 的隐含对象)

}

9个隐式对象

①. request: HttpServletRequest 的一个对象。

String name = request.getParameter("name");

②. response: HttpServletResponse 的一个对象(在 JSP 页面中几乎不会调用 response 的任何方法)。

System.out.println(response instanceof HttpServletResponse);

③. pageContext: 页面的上下文,是 PageContext 的一个对象。 可以从该对象中获取到其他 8 个隐含对象。也可以从中获取到当前
页面的其他信息。(学习自定义标签时使用它)

ServletRequest req = pageContext.getRequest();
System.out.println(req == request);

④. session: 代表浏览器和服务器的一次会话,是 HttpSession 的一个对象. 后面详细学习。

System.out.println(session.getId);

⑤. application: 代表当前 WEB 应用。是 ServletContext 对象。

System.out.println(application.getInitParameter("user"));

⑥. config: 当前 JSP 对应的 Servlet 的 ServletConfig 对象(几乎不使用)。若需要访问当前 JSP 配置的初始化参数,需要通过映射的地址才可以。

映射 JSP:

  <servlet>
    <servlet-name>hellojsp</servlet-name>
    <jsp-file>/hello.jsp</jsp-file>
    <init-param>
        <param-name>test</param-name>
        <param-value>testValue</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>hellojsp</servlet-name>
    <url-pattern>/hellojsp</url-pattern>    
  </servlet-mapping>

⑦. out: JspWriter 对象。调用 out.println() 可以直接把字符串打印到浏览器上。JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存。

⑧. page: 指向当前 JSP 对应的 Servlet 对象的引用,但为 Object 类型,只能调用 Object 类的方法(几乎不使用)。

⑨. exception: 在声明了 page 指令的isErrorPage="true"时, 才可以使用。

<%@ page isErrorPage="true" %>

JSP的基本语法

JSP模版元素

在JSP页面中没有嵌套在<%%>之间的的静态HTML内容被称之为JSP的模版元素。在静态的HTML内容之中可以嵌套JSP的其他各种元素来产生动态内容和执行业务逻辑。

JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。

JSP表达式

JSP表达式(expression)提供了将一个java变量或表达式的计算结果输出到客户端的简化方式,它将要输出的变量或表达式直接封装在<%=%>之中。

Current time: <%= new java.util.Date() %> 

JSP表达式中的变量或表达式的计算结果将被转换成一个字符串,然后被插入进整个JSP页面输出结果的相应位置处。
JSP表达式中的变量或表达式后面不能有分号(;),JSP表达式被翻译成Servlet程序中的一条out.print(…)语句。

JSP脚本片断

在JSP页面中编写的一条或多条Java代码需要嵌套在<%%>中,嵌套在<%%>之间的Java代码被称之为脚本片段(Scriptlets)

在JSP脚本片断中,可以定义变量、执行基本的程序运算、调用其他Java类、访问数据库、访问文件系统等普通Java程序所能实现的功能。
在JSP脚本片断可以直接使用JSP提供的隐式对象来完成WEB应用程序特有的功能。

JSP脚本片断中的Java代码将被原封不动地搬移进由JSP页面所翻译成的Servlet的_jspService方法中,所以,JSP脚本片断之中只能是符合Java语法要求的程序代码,除此之外的任何文本、HTML标记、其他JSP元素都必须在脚本片断之外编写。

在一个JSP页面中可以有多个脚本片断(每个脚本片断代码嵌套在各自独立的一对<% 和 %>之间),在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。如:

<%
    int x = 3;
%>
<p>这是一个HTML段落</p>
<%
    out.println(x);
%>

并且,多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<%%>之中的情况。

也就是说单个脚本片断中的Java语句可以是不完整的。但是!多个脚本片断组合后的结果必须是完整的Java语句!

脚本片断中的Java代码将被原封不动地搬移进由JSP页面所翻译成的Servlet的_jspService()方法中,脚本片断之外的任何文本、HTML标记以及其他JSP元素也都会被转换成相应的Java程序代码插入进_jspService()方法中,且脚本片断和其他JSP元素的插入位置与它们在JSP页面中的原始位置相对应。

因此可以写成下面这样:

<% 
    String ageStr = request.getParameter("age");
    Integer age = Integer.parseInt(ageStr);

    if(age >= 18){
%>
        成人...     //在脚本片段之外的模版元素,如果age>=18,会显示它
<%
    }else{
%>
        未成人...     //在脚本片段之外的模版元素,如果age>=<18,会显示它
<%
    }
%>

JSP声明

JSP声明Java代码封装在<%!%>之中,它里面的代码将被插入进Servlet的_jspService方法的外面。
(几乎不这么用,了解即可)

JSP中的属性

属性与参数

我们可以把attribute(属性)理解为资源字典。

而把Parameter(参数)理解为”由用户给服务器的一种东西”。

经常这样写:

    XXXX.setAttribute("a", A);
    XXXX.getAttribute("b");

把setAttribute理解为给资源字典XXXX增加一个条目
把getAttribute理解为从XXXX字典中取出一个键为”b”的条目的”值”

attribute属性是WebApp内部各部分/组件之间交互信息使用的通信手段。jsp之间或与Servlet之间进行交互时,使用Attribute传递消息。通过getAttribute()和setAttribute()来共享request范围内的数据。attrubute中的数据是Object类型的,通过attribute传递的数据只会存在于web容器内部,仅仅是请求处理阶段。

Parameter,是用户和WebApp进行交互的时候,特别是用户要提交信息给WebApp的时候,传递信息的手段。,参数为页面提交的参数,包括:表单提交的参数、URL重写(就是xxx?id=1中的id)传的参数等,因此这个并没有设置参数的方法(没有setParameter),而且接收参数返回的不是Object,而是String类型

request.getAttribute()方法返回request范围内存在的对象,request.getParameter()获取http请求提交过来的数据。
一般的Web应用,基本上是post方式的传递,用getParameter取值。对于自己控制的,可以通过request.setAttribute和getAttribute实现值的传递。

与属性相关的方法

public Object getAttribute(String name):获取指定的属性

public Enumeration getAttributeNames():获取所有的属性的名字组成的Enumeration对象

public void removeAttribute(String name):移除指定的属性

public void setAttribute(String name,Object value):设置属性
`

域对象和属性范围

pageContext,request,session,application对象都可以调用上面这些方法。
这四个对象也被称为域对象。

可以简单的理解为JSP中有四个字典:

范围变量名一般Servlet中我们对它的称呼
当前JSP文件(页面)可见pageContext不存在的
同一服务器请求可见request请求
一次会话(一个人)可见sessionsession
整个WebApp可见applicationServletContext上下文参数
  • pageContext
    存储在pageContext对象中的属性仅可以被当前JSP页面的当前响应过程中调用的各个组件访问,例如,正在响应当前请求的JSP页面和它调用的各个自定义标签类。

  • request
    存储在request对象中的属性可以被属于同一个请求的所有Servlet和JSP页面访问,例如使用PageContext.forward和PageContext.include方法连接起来的多个Servlet和JSP页面。

  • session
    存储在session对象中的属性可以被属于同一个会话的所有Servlet和JSP页面访问。

  • application
    存储在application对象中的属性可以被同一个WEB应用程序中的所有Servlet和JSP页面访问(只要不移除)。

请求重定向与转发

语法

请求转发:

  1. 调用 HttpServletRequest 的 getRequestDispatcher() 方法获取 RequestDispatcher 对象调用 getRequestDispatcher() 需要传入要转发的地址
  2. 调用 HttpServletRequset 的 forward(request,response) 进行请求的转发
String path = "/testServlet";
RequestDispatcher requestDispatcher = request.getRequestDispatcher(path);
requestDispatcher.forward(request,response);

请求重定向:

  1. 直接调用response.sendRedirect(path)方法,path为要重定向的地址
String path = "/third/testServlet";
response.sendRedirect(path);

直观认识

test.html

写一个界面,点击连接,分别转到 ForwardServlet 和 RedirectServlet 上

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--这里一定注意地址的写法,加上当前项目地址-->
<a href="/third/ForwardServlet">Forward</a>
<br><br>
<a href="/third/RedirectServlet">Redirect</a>
</body>
</html>
ForwardServlet

创建ForwardServlet,当跳转到当前的ForwardServlet中,转发的方式跳转到指定的路径文件。注意如下:

  1. 在Servlet3.0中,可以在配置文件中进行映射,而是用注解的方式,标注注册名和路径
  2. 超链接是通过get请求的,因此在doGet方法中写。
//在Servlet3.0中,可以在配置文件中进行映射,而是用注解的方式
@javax.servlet.annotation.WebServlet(name="ForwardServlet",urlPatterns="/ForwardServlet")
public class ForwardServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    }

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        //打印
        System.out.println("ForwardServlet's doGet");
        /**
         * 请求的转发:
         *
         * 1. 调用 HttpServletRequest 的 getRequestDispatcher() 方法获取
         *    RequestDispatcher 对象调用 getRequestDispatcher() 需要传入要转发的地址
         * 2. 调用 HttpServletRequset 的 forward(request,response) 进行请求的转发
         */
        String path = "/testServlet";
        RequestDispatcher requestDispatcher = request.getRequestDispatcher(path);
        requestDispatcher.forward(request,response);
    }
}
RedirectServlet

创建RedirectServlet,跳转到当前RedirectServlet后,以重定向的方式跳转的路径文件。

注意:
重定向相当于重新请求路径,因此要注意路径的格式,要么直接写文件名,不加”/”,要么加上路径”/项目名/文件名”

@WebServlet(name = "RedirectServlet",urlPatterns = "/RedirectServlet")
public class RedirectServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("RedirectServlet's doGet");

        /**
         * 执行请求的重定向
         *  直接调用response.sendRedirect(path)方法,path为要重定向的地址
         */
        String path = "/third/testServlet";
        response.sendRedirect(path);

    }
}
testServlet
@WebServlet(name = "testServlet",urlPatterns = "/testServlet")
public class testServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("testServlet's doGet");
    }
}
结果
  • 转发:
    当我们点击Forward的时候,web页面跳转到http://localhost:8080/third/ForwardServlet页面,但是控制台输出为:
ForwardServlet's doGet
testServlet's doGet
  • 重定向:
    当我们点击Redirect的时候,web页面跳转到http://localhost:8080/third/testServlet页面,但是控制台的输出为:
RedirectServlet's doGet
testServlet's doGet

也就是说在请求转发时,页面是跳转到ForwardServlet中并获取了testServlet中的内容,而请求重定向中则是跳转到testServlet中。

本质区别

请求的转发是只发出了一次请求,而请求重定向则发出了两次请求。

举一个例子:

请求的转发:

小张最近手头紧,朝小A借500块钱,小A说“没问题,你等会”。但是小A其实也没有钱,小A自己管朋友小B借了500块钱,然后把钱借给小张。也就是说小A将小张的借钱请求直接转给了小B,对于小张来说,只借了一次钱

请求的重定向:

小张最近手头紧,朝小C借500块钱,小C说“不好意思,我也没钱”。于是小张又去找朋友小D,小D借给了小张500块钱。小张分别朝小C和小D两个人借钱,小C和小D没有产生关系

具体来说:

  • 请求的转发:地址栏初次发出的请求的地址。
    请求的重定向:地址栏不再是初次发出的请求地址。地址栏为最后响应的那个地址。

  • 请求转发:在最终的Servlet中,request对象和中转的request是同一个对象。也就是testServlet中可以获取到ForwardServlet中的属性Attribute
    请求的重定向:在最终的Servlet中,request对象和中转的request是不同一个对象。testServlet中获取不到RedirectServlet中的属性Attribute,因为不是同一个请求对象了

  • 请求的转发:只能转发给当前Web应用的资源
    请求重定向:可以重定向到任何资源

  • 请求的转发:"/"代表的是当前web应用的根目录
    请求重定向:"/"代表的是当前web站点的根目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值