• 一个例子搞懂Servlet&JSP


    <?xml version="1.0" encoding="ISO-8859-1"?>
    
    <web-app 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-app_2_4.xsd"
    	version="2.4">
    	
    	<servlet>
    		<servlet-name>AllInOneServlet</servlet-name>
    		<servlet-class>com.cdai.web.j2ee.AllInOneServlet</servlet-class>
    	</servlet>
    
    	<servlet-mapping>
    		<servlet-name>AllInOneServlet</servlet-name>
    		<url-pattern>/j2ee</url-pattern>
    	</servlet-mapping>
    	
    	<!-- Servlet mappings END -->	
    
    </web-app>
    package com.cdai.web.j2ee;
    
    import java.io.DataOutputStream;
    import java.io.IOException;
    
    import javax.servlet.RequestDispatcher;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @SuppressWarnings("serial")
    public class AllInOneServlet extends HttpServlet {
    
    	public AllInOneServlet() {
    		System.out.println("Servlet constructed");
    	}
    
    	@Override
    	public void init() {
    		System.out.println("Servlet init");
    	}
    	
    	@Override
    	public void destroy() {
    		System.out.println("Servlet destory");
    	}
    	
    	@Override
    	public void service(HttpServletRequest request, HttpServletResponse response)
    	        throws ServletException, IOException {
    		
    		System.out.println("Servlet served");
    
    		// 1.Get parameter from HTTP header
    		String goWhere = request.getParameter("goto");
    		
    		if (goWhere == null || "1".equals(goWhere)) {
    			
    			// 2.Set response header
    			response.setContentType("text/html");
    			
    			// 3.Get useful info from TCP & HTTP header
    			System.out.println(
    					"Request from: " + request.getRemoteAddr() + ":" + 
    					request.getRemotePort() + " by method " + 
    					request.getMethod());
    			
    			// 4.Print html(out is built-in object in JSP)
    			DataOutputStream out = new DataOutputStream(response.getOutputStream());
    			out.writeUTF("Hello Servlet");
    			out.writeUTF("<br>");
    			out.close();
    		}
    		else if ("2".equals(goWhere)) {
    			RequestDispatcher dispather = request.getRequestDispatcher("/main.jsp?param1=java");
    			request.setAttribute("param2", "servlet");
    			dispather.forward(request, response);
    		}
    		else if ("3".equals(goWhere)) {
    			response.sendRedirect("http://www.google.com?newwindow=1&q=java&oq=java");
    		}
    		
    	}
    	
    }
    <!-- 1.Compile instruction -->
    <%@page 
    	import="java.util.concurrent.atomic.*, com.cdai.web.j2ee.TestBean" 
    	contentType="text/html;charset=utf-8" 
    %>
    
    <!-- 2.Declaration: member variable and method -->
    <%! 
    
    	private AtomicInteger count = new AtomicInteger(1);
    	private ThreadLocal<Integer> curCountStorage = new ThreadLocal<Integer>();
    	
    	private int getCount() {
    		int curCount = count.getAndIncrement();
    		curCountStorage.set(curCount);
    		return curCount;
    	}
    %>
    
    <!-- 3.JSP code & 4.Built-in object -->
    <%
    	Object curCount = session.getAttribute("count");
    	if (curCount == null) {
    		curCount = getCount();
    		session.setAttribute("count", curCount);
    	}
    	
    	out.println(request.getParameter("param1") + " - " + request.getAttribute("param2"));
    %>
    
    <br> This is main.jsp. You're the <%=curCount%> visitor.
    
    <!-- 5.Runtime action -->
    <jsp:useBean id="testBean" class="com.cdai.web.j2ee.TestBean" scope="page"/>
    <br>Message in TestBean is: <jsp:getProperty property="message" name="testBean"/>

    这是一个很简单的例子,通过http://127.0.0.1:8080/ssh/j2ee可以访问到AllInOneServlet。通过传给它
    不同的goto参数,可以控制它是:(1)自己生成一个hello servlet的响应页面(2)转发到main.jsp生成
    一个统计访问量的页面(3)重定向到Google首页。通过这个例子,让我们来一起来搞懂Servlet和JSP
    这两个J2EE开发中最基础的组件。


    一、Servlet基础

    1.Servlet的生命周期

    根据日志输出,当发送HTTP请求到Servlet时,Tomcat才创建Servlet。首先执行的是Servlet的构造函数,
    之后是init()方法,然后才是service()方法。如果没有覆写service(),那么它会根据HTTP请求是Get还是
    Post来调用doGet()和doPost()方法。

    这一个Servlet会一直存在,被处理各个HTTP请求的线程调用,因此Servlet要尽量含有避免synchronized
    代码。最后,当Tomcat移出了Servlet时会调用destory()方法(应用被卸载或Servlet文件发生变化)。

    2.读请求头,设置响应头

    通过Servlet的API可以很方便的从TCP和HTTP数据包中读出很多有用的信息,底层已经帮我们解析好了。
    以后有空会写一个简单的Web服务器,模拟一下J2EE容器的一些基本功能。

    3.读URL中参数

    这也是Web开发中最常用的方法,通过getParameter()方法可以取出URL中的参数。

    4.重定向和转发

    很重要的两个概念。重定向一般是返回给浏览器一个外部的URL,让它去那里请求,所以浏览器实际上
    请求了两次才得到最终的内容。而转发一般是在当前Web应用内部,从一个组件转发到另一个组件(比如
    从Servlet到JSP),主要用于组件间协同工作。可以通过setAttribute来传递一些数据。


    二、JSP基础

    JSP看似比Servlet内容多而且复杂,其实学习JSP时只要关注两件事:
         哪些代码是编译时用的,哪些是运行时执行。
         各种标签在编译成Servlet代码后变成了什么样。

    1.指令

    指令是编译期间执行的代码,常用的有:page、include、taglib。语法是<%@page ... %>。
    在这个例子中通过page指令的import设置JSP引用的Java类,这样JSP编译成Servlet时才不会
    有编译错误。

    2.声明

    声明的变量和方法最终编译成Servlet中的成员变量和方法。语法是<%! ... %>。这里的代码
    都会生成在service()方法外,所以声明的变量是编译后Servlet的成员变量,而且可以声明方法。
    这是声明与后面将要介绍的普通JSP代码的区别。

    3.动作

    动作是运行期执行的代码。<jsp:include>、<jsp:forward>、<jsp:useBean>等等。<jsp:include>
    是动态引入其他文件,如果代码不执行到这里就不会引入,一定要与include指令区分开。
    <jsp:forward>与Servlet中的forward方法功能相同,而<jsp:useBean>、<jsp:property>稍后在JSP
    转换成的Servlet源文件中会看到它们的真身。

    4.JSP代码

    编译后会生成到service()方法中,因此在这里声明的变量是局部变量,也就不能在这里声明方法了。
    语法是<% ... %>。

    5.内置对象

    out、page、session、application、config、exception、request、response、pageContext。
    可以在JSP中直接使用。这些内置对象没有什么神秘的,在Servlet中都是可以获得到的,只不过
    在JSP中它们都有了简短的名字,所以用起来很方便而已。

    6.JSP表达式

    插入一个简单的Java代码得到一个值,语法是<%= ... %>。

    下面就要揭开本例中JSP神秘的面纱了,在Tomcat的work目录中我们可以找到JSP转成的Servlet源文件文件。
    package org.apache.jsp;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.jsp.*;
    import java.util.concurrent.atomic.*;
    import com.cdai.web.j2ee.TestBean;
    
    public final class main_jsp extends org.apache.jasper.runtime.HttpJspBase
        implements org.apache.jasper.runtime.JspSourceDependent {
    
     
    
    	private AtomicInteger count = new AtomicInteger(1);
    	private ThreadLocal<Integer> curCountStorage = new ThreadLocal<Integer>();
    	
    	private int getCount() {
    		int curCount = count.getAndIncrement();
    		curCountStorage.set(curCount);
    		return curCount;
    	}
    
      private static java.util.List _jspx_dependants;
    
      public Object getDependants() {
        return _jspx_dependants;
      }
    
      public void _jspService(HttpServletRequest request, HttpServletResponse response)
            throws java.io.IOException, ServletException {
    
        JspFactory _jspxFactory = null;
        PageContext pageContext = null;
        HttpSession session = null;
        ServletContext application = null;
        ServletConfig config = null;
        JspWriter out = null;
        Object page = this;
        JspWriter _jspx_out = null;
        PageContext _jspx_page_context = null;
    
    
        try {
          _jspxFactory = JspFactory.getDefaultFactory();
          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("<!-- 1.Compile instruction -->\r\n");
          out.write("\r\n");
          out.write("\r\n");
          out.write("<!-- 2.Declaration: member variable and method -->\r\n");
          out.write("\r\n");
          out.write("\r\n");
          out.write("<!-- 3.JSP code & 4.Built-in object -->\r\n");
    
    	Object curCount = session.getAttribute("count");
    	if (curCount == null) {
    		curCount = getCount();
    		session.setAttribute("count", curCount);
    	}
    	
    	out.println(request.getParameter("param1") + " - " + request.getAttribute("param2"));
    
          out.write("\r\n");
          out.write("\r\n");
          out.write("<br> This is main.jsp. You're the ");
          out.print(curCount);
          out.write(" visitor.\r\n");
          out.write("\r\n");
          out.write("<!-- 5.Runtime action -->\r\n");
          com.cdai.web.j2ee.TestBean testBean = null;
          synchronized (_jspx_page_context) {
            testBean = (com.cdai.web.j2ee.TestBean) _jspx_page_context.getAttribute("testBean", PageContext.PAGE_SCOPE);
            if (testBean == null){
              testBean = new com.cdai.web.j2ee.TestBean();
              _jspx_page_context.setAttribute("testBean", testBean, PageContext.PAGE_SCOPE);
            }
          }
          out.write("\r\n");
          out.write("<br>Message in TestBean is: ");
          out.write(org.apache.jasper.runtime.JspRuntimeLibrary.toString((((com.cdai.web.j2ee.TestBean)_jspx_page_context.findAttribute("testBean")).getMessage())));
          out.write('\r');
          out.write('\n');
        } catch (Throwable t) {
          if (!(t instanceof SkipPageException)){
            out = _jspx_out;
            if (out != null && out.getBufferSize() != 0)
              out.clearBuffer();
            if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
          }
        } finally {
          if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
        }
      }
    }
    怎么样?对照着JSP的源码来看,是不是毫无神秘之处。


    三、Cookie和Session

    在这个例子中我们使用Session,借助Cookie保存一个Session ID在浏览器端。这种Cookie也叫做会话Cookie。
    在同一个Chrome进程的打开多个不同页面访问http://127.0.0.1:8080/ssh/j2ee?goto=2都能够获得Session
    中保存的数据,从而达到了使无状态的HTTP看起来好像有状态一样。




    四、多线程安全

    由于每个Servlet只有一个实例,被所有请求线程共享,所以在Servlet中要尽量避免代码同步、资源竞争,
    否则服务器的响应速度会很慢。除了Servlet,还要注意一些会被共享的内置对象,比如在一个用户的所有
    请求内被共享的Session对象,也是有可能发生并发问题的。有共享,就会有并发,所以在J2EE各个层的
    开发中,Servlet/JSP -> Service -> DAO -> Database都要注意并发问题。

  • 相关阅读:
    Raft协议备注
    领域建模笔记
    Spark编程模型
    Spark如何删除无效rdd checkpoint
    Parquet 列式存储格式
    SpringBoot中ConditionalOnClass注解的原理
    SpringBoot定制Sevlet容器原理
    分析JDK的动态代理原理
    简述SpringCloud底层原理
    简述SpringCloud框架
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157816.html
Copyright © 2020-2023  润新知