当前位置: 代码迷 >> JavaScript >> 一个事例搞懂Servlet&JSP
  详细解决方案

一个事例搞懂Servlet&JSP

热度:309   发布时间:2012-08-29 08:40:14.0
一个例子搞懂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都要注意并发问题。

  相关解决方案