程序员技术栈每日一篇JavaJava 杂谈

JavaWeb基础(七)-Servlet交互和作用域对象

2018-08-21  本文已影响5人  sixleaves

1.Servlet交互和作用域对象

在JavaWeb基础(六)中我分享了三个主要内容Servet3.0注解的使用Cookie技术实现请求数据共享Session技术实现请求数据共享.在这篇博客中,我主要分享以下两方面的内容。

1.1 Servlet交互

在J2EE规范中, Servlet的交互方式主要有三种。Servlet之所以需要交互是因为我们不可能将所有业务逻辑都揉和在一个Servlet中.所以一个Sevlet一般对应一个功能。多个Servlet之间就涉及到如何进行数据的传递跳转, 这我们又称为Servlet的交互。简而言之,Servlet的交互讲的是Servlet之间的数据传递跳转

1.1.1 Servlet交互方式

Servlet交互方式,主要分为以下两种, 请求转发, URL重定向请求包含

请求转发

FirstServlet

@WebServlet("/request_forward/FirstServlet")
public class FirstServlet extends HttpServlet{
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        PrintWriter pw = resp.getWriter();
        System.out.println("FirstServlet before");
        pw.println("FirstServlet before");
        req.getRequestDispatcher("/request_forward/SecondServlet").forward(req, resp);
        System.out.println("FirstServlet Request =" + req);
        System.out.println("FirstServlet after");
        pw.println("FirstServlet after");
    }
}

SecondServlet

@WebServlet("/request_forward/SecondServlet")
public class SecondServlet extends HttpServlet {
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter pw = resp.getWriter();
        System.out.println("SecondServlet before");
        pw.println("SecondServlet before");
        
        System.out.println("SecondServlet Request =" + req);
        System.out.println("SecondServlet after");
        pw.println("SecondServlet after");
    }
}

浏览器输出

控制台输出

特点研究

我们根据浏览器的输出控制器台输出,结合代码来研究请求转发的特点.
我们从浏览器地址栏请求转发整个过程Servlet的执行先后顺序请求报文变化参数是否可以传递请求转发能够访问的资源来研究。

观察如上图, 在输入http://127.0.0.1:8080/request_forward/FirstServlet后请求报文只有一次,并没有发生变化。说明请求转发的请求只有一次.

更改FirstServlet的代码,请求转发前给path增加name参数,并且存放一个age数据到reqeust域中.

        req.setAttribute("age", 13);
        req.getRequestDispatcher("/request_forward/SecondServlet?name=will").forward(req, resp);

SecondServlet最后先新增如下代码

        pw.println("name = "+req.getAttribute("name")+",age = "+req.getAttribute("age"));

浏览器输出

结论:通过请求转发上填写参数无法共享,但是request对象的数据可以共享

5.请求转发能够访问的资源
尝试通过请求转发访问百度

        req.getRequestDispatcher("www.baidu.com").forward(req, resp);

浏览器输出


可以得出以下结论:请求转发无法跨域访问

尝试通过请求转发访问WEB-INF目录下的资源

        req.getRequestDispatcher("/baidu.html").forward(req, resp);

浏览器输出


可以得出以下结论:请求转发可以访问WEB-INF下的资源文件
总结

URL重定向

URL重定向主要通过response对象的sendRedirect方法实现.其会响应浏览器当前请求, 并让浏览器再发一次请求到新的资源路径。所以称之为重定向.

FirstServlet

@WebServlet("/url_redirect/FirstServlet")
public class FirstServlet extends HttpServlet{
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        PrintWriter pw = resp.getWriter();
        System.out.println("FirstServlet before");
        pw.println("FirstServlet before");
        req.setAttribute("age", 13);
//      req.getRequestDispatcher("/baidu.html").forward(req, resp);
        resp.sendRedirect("/request_forward/SecondServlet");
        System.out.println("FirstServlet Request =" + req);
        System.out.println("FirstServlet after");
        pw.println("FirstServlet after");
    }
}

SecondServlet


@WebServlet("/url_redirect/SecondServlet")
public class SecondServlet extends HttpServlet {
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter pw = resp.getWriter();
        System.out.println("SecondServlet before");
        pw.println("SecondServlet before");
        System.out.println("SecondServlet Request =" + req);
        System.out.println("SecondServlet after");
        System.out.println("name = "+req.getAttribute("name")+",age = "+req.getAttribute("age"));
        pw.println("name = "+req.getAttribute("name")+",age = "+req.getAttribute("age"));
        pw.println("SecondServlet after");
    }
}

浏览器输出

控制台输出

特点研究

如上图,请求报文的请求行地址就发生了改变, 说明这是第二次请求.
所以URL重定向的请求是两次
        resp.sendRedirect("https://www.baidu.com");

浏览器输出.没法录制动图,自己尝试下


结论: URL重定向可以跨域访问

访问WEB-INF

        resp.sendRedirect("/WEB-INF/views/login.html");

结论: URL重定向不能访问WEB-INF目录下的资源
总结

可见URL重定向和请求转发的6个特点都是相反的.

请求包含

请求包含主要用在JSP中, 用在Servlet中没有多大意义。因为我们的每个Servlet一般有不同职责, 并不能够包含再一起。而JSP页面可以进行复用,这时候就可以用包含.
语法:request.getRequestDispatcher(path).include(req, resp);

如何选择请求转发和URL重定向

请求转发和URL重定向都是用于Web组件(Servlet和JSP)的跳转。

1.2 作用域对象

Servlet三大作用域对象和其操作

Servlet的三大作用域对象其中两个, 我们已经在之前讲过, 分别是requestsession.之所以说他们是作用域是因为保存在他们中的数据是有作用域范围, request是在同一个请求对象间, session是在一次会话间, 又因为它们都是Servlet下的规范,所以称为Servlet作用于对象。说了那么多, 最后一个Servlet作用域对象是ServletContext对象.

ServletContext

ServletContext是一个接口, 表示Web应用对象。在JSP中将其称为application.

代码演示Servlet三大作用域

@WebServlet("/scope/servlet")
public class ScopeServlet extends HttpServlet {
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        final String NUM_IN_REQUEST = "NUM_IN_REQUEST";
        final String NUM_IN_SESSION = "NUM_IN_SESSION";
        final String NUM_IN_APPLICATION = "NUM_IN_APPLICATION";
        Integer numInRequest = (Integer) req.getAttribute(NUM_IN_REQUEST);
        if (null == numInRequest) {
            req.setAttribute(NUM_IN_REQUEST, 1);
        }else {
            req.setAttribute(NUM_IN_REQUEST, numInRequest+1);
        }
        
        HttpSession session = req.getSession();
        Integer numInSession = (Integer) session.getAttribute(NUM_IN_SESSION);
        if (null == numInSession) {
            session.setAttribute(NUM_IN_SESSION, 1);
        }else {
            session.setAttribute(NUM_IN_SESSION, numInSession+1);
        }
        ServletContext app = req.getServletContext();
        Integer numInApplication = (Integer) app.getAttribute(NUM_IN_APPLICATION);
        if (null == numInApplication) {
            app.setAttribute(NUM_IN_APPLICATION, 1);
        }else {
            app.setAttribute(NUM_IN_APPLICATION, numInApplication+1);
        }
        PrintWriter pw=resp.getWriter();
        pw.println("request num = " + req.getAttribute(NUM_IN_REQUEST));
        pw.println("session num = " + session.getAttribute(NUM_IN_SESSION));
        pw.println("application num = " + app.getAttribute(NUM_IN_APPLICATION));
        
    }
}

第一次访问

第二次刷新

打开另外一个窗口,地址相同

Servlet三大作用域总结

作用域对象 作用域类型 作用域范围 表示
request HttpServletRequest 当前请求对象 每次请求都是一个请求对象
session HttpSession 一次会话的多个请求之间 每次会话都是表示一个session对象
application ServletContext 作用于整个Web应用, 可以实现多次会话之间的数据共享 表示一个web应用

1.3 JSP

JSP又称为动态网页, 其专门为页面渲染而生。就要聊聊最初的Servlet

最初的Servlet

责任分离

出于以上的问题, 就将Servlet里负责HTML页面代码的责任进行了分离。让Servlet专注于业务逻辑。JSP技术就顺理成章的诞生了, JSP技术就是专门为了编写HTML代码。

JSP基本语法

注释。

语法:<%-- 注释内容 --%>

输出。用字符输出流输出

语法: <%= "sweetcs" %> 等价于 out.write("sweetcs")

脚本代码段。(定义service方法中的代码)

语法:

<% 
代码1; 
代码2; 
%>

定义类成员。

语法: <%! private int age; %>

代码演示

<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <%-- 这是一行注释 --%>
    <%! private int age = 13; 
        public ArrayList<String> names = new ArrayList<String>();
    %>
    <%= "Hello JSP By SweetCS" %>
    <%
        names.add("AAA");
        names.add("BBB");
        names.add("CCC");
    %>
    <%= names %>
</body>
</html>

JSP原理剖析

看完上述语法和代码你也许还不是懂。没事, 要理解这是怎么回事, 我们得结合底层来理解。


观察其目录结构,我们可以发现我们hello.jsp是先被翻译hello_jsp.java, 再被编译hello_jsp.class
/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/7.0.90
 * Generated at: 2018-08-20 17:14:11 UTC
 * Note: The last modified time of this file was set to
 *       the last modified time of the source file after
 *       generation to assist with modification tracking.
 */
package org.apache.jsp.views.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.ArrayList;

public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {

  private int age = 13; 
    public ArrayList<String> names = new ArrayList<String>();
    
  private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();

  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

  private volatile javax.el.ExpressionFactory _el_expressionfactory;
  private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }

  public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
    if (_el_expressionfactory == null) {
      synchronized (this) {
        if (_el_expressionfactory == null) {
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        }
      }
    }
    return _el_expressionfactory;
  }

  public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
    if (_jsp_instancemanager == null) {
      synchronized (this) {
        if (_jsp_instancemanager == null) {
          _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        }
      }
    }
    return _jsp_instancemanager;
  }

  public void _jspInit() {
  }

  public void _jspDestroy() {
  }

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

    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.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("\n");
      out.write("\n");
      out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
      out.write("<html>\n");
      out.write("<head>\n");
      out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
      out.write("<title>Insert title here</title>\n");
      out.write("</head>\n");
      out.write("<body>\n");
      out.write("\t");
      out.write('\n');
      out.write('   ');
      out.write('\n');
      out.write('   ');
      out.print( "Hello JSP By SweetCS" );
      out.write('\n');
      out.write('   ');

        names.add("AAA");
        names.add("BBB");
        names.add("CCC");
    
      out.write('\n');
      out.write('   ');
      out.print( names );
      out.write("\n");
      out.write("</body>\n");
      out.write("</html>");
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

JSP三大指令

JSP指令很多,但是实际开发能用到只有少数.主要分为以下三大类指令.JSP中的指令都具备以下两个特点。

page指令

page指令。用于表示JSP页面的相关配置信息。比如响应编码的设置, 导入哪些包.

常用属性

<%@ page 
    language="java" 
    contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"
    import="java.util.ArrayList"
%>

include指令

include指令主要用来做静态包含。后面再结合动态包含补充介绍。

taglib指令

taglib指令后面分享jstl在细讲.其主要为了用来引入标签库

JSP的9大内置对象

JSP中内置了9个对象, 这9个对象是可以直接访问,而无序创建的。因为JSP本质是Servlet, 既然其本质是Servlet, 为了便于访问Servlet常用的对象, JSP中就有了9大对应的对象,如下表。

内置对象 内置对象对应的类型 描述 对应代码
request HttpServletRequest 请求对象
response HttpServletResponse 响应对象
pageContext PageContext 当前JSP作用域对象 pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
session HttpSession 会话对象, sessioin ="true" session = pageContext.getSession();
exception Throwable 异常类型, isErrorPag="true"
application ServletContext 当前web应用对象 pageContext.getServletContext();
config ServletCofig 当前web.xml配置对象 config = pageContext.getServletConfig();
out JspWriter 字符输出流对象 out = pageContext.getOut();
page Object 当前Servlet对象 final java.lang.Object page = this;

JSP的四大作用域

Servlet有三大作用域(requestsessionapplication), 又因为JSP本质也是Servlet所以.JSP也有Servlet中的三大作用域, 并且JSP还多了一个作用域就是pageContext, 表示当前JSP作用域.

上一篇 下一篇

猜你喜欢

热点阅读