文章目录

Servlet

servlet API概述

该API中一共包括四个包:

javax.servlet、javax.http、javax.servlet.annotation、javax.servlet.descriptor

用户的请求会引发servlet容器调用一个servlet的service方法,并给这个方法传入一个servletRequest实例和servletResponse实例,request封装http请求,response封装http响应

servlet容器会为每一个应用创建一个servletContext实例,这个对象用于封装应用程序的环境(配置文件什么的)

同时,servlet容器会为每个servlet创建一个ServletConfig实例,该实例封装的是servlet的配置信息

注意区分ServletContext和ServletConfig,一个是应用程序,一个是servlet,前者的作用范围明显要大于后者

servlet有以下五个主要方法

生命周期方法有三个:

  • init(servletconfig config)
  • service
  • destory
    另外两个为:
  • getServletInfo
    • 返回该servlet的描述
  • getServletConfig
    • 返回由servlet容器传递给init方法的servletconfig对象

线程安全:

一个应用程序中的所有用户共享一个servlet实例,因此不建议使用类级变量(就是全局变量,static修饰的那种)

编写servlet应用

需要注意的一点是:xml文件并不是必须的,可以使用@WebServlet的形式代替web.xml,如下所示,不过要知道的是,在tomcat7才有javax.servlet.annotation这个API

package app01a;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

@WebServlet(name = "ServletConfigDemoServlet", 
    urlPatterns = { "/servletConfigDemo" },
    initParams = {
        @WebInitParam(name="admin", value="Harry Taciak"),
        @WebInitParam(name="email", value="admin@example.com")
    }
)
public class ServletConfigDemoServlet implements Servlet {
    private transient ServletConfig servletConfig;

    @Override
    public ServletConfig getServletConfig() {
        return servletConfig;
    }

    @Override
    public void init(ServletConfig servletConfig) 
            throws ServletException {
        this.servletConfig = servletConfig;
    }

    @Override
    public void service(ServletRequest request, 
            ServletResponse response)
            throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        String admin = servletConfig.getInitParameter("admin");
        String email = servletConfig.getInitParameter("email");
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<html><head></head><body>" + 
                "Admin:" + admin + 
                "<br/>Email:" + email +
                "</body></html>");
    }

    @Override
    public String getServletInfo() {
        return "ServletConfig demo";
    }
    
    @Override
    public void destroy() {
    }    
}

这样也是可以正常运行的:

JSP(一)_java

稍微解释以下@WebServlet的用法

name = "ServletConfigDemoServlet", 
urlPatterns = { "/servletConfigDemo" },

声明了当url为/servletConfigDemo时,应该调用这个servlet,这个其实和xml形式的声明是差不多的

<servlet>
		<servlet-name>Ch3 Beer</servlet-name>
		<servlet-class>
			com.example.web.BeerSelect
		</servlet-class> 

	<servlet-mapping>
		<servlet-name>Ch3 Beer</servlet-name>
		<url-pattern>/SelectBeer.do</url-pattern>
	</servlet-mapping>
</servlet>

这个xml意思是当url为/SelectBeer.do时,调用com.example.web.BeerSelect

可以看到,使用注解的方式更加地简洁

上面有一个关键字transient,这里有必要稍微解释一下:
https://blog.csdn.net/include_heqile/article/details/90453547

保存在servletContext中的对象称为属性,servletContext提供了下面四个方法来用于处理属性:

  • getAttribute
  • getAttributeNames
  • setAttribute
  • removeAttribute

servletContext的作用域比较大,我们可以使用该对象的这一特性,来在web应用的不同servlet中共享信息

在上面我们编写的类是直接继承的Servlet,但是这样做我们需要实他的5个方法,而且还有维护一个servletConfig对象,这样会显得很麻烦,还好我们有GenericServlet,GenericServlet是一个抽象类,它实现了Servlet和ServletConfig,并对Servlet接口中的所有方法提供了默认实现,并提供了用来包装ServletConfig的方法

package app01a;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;package app01a;
import java.io.IOException;

@WebServlet(name = "GenericServletDemoServlet", 
    urlPatterns = { "/generic" },
    initParams = {
        @WebInitParam(name="admin", value="Harry Taciak"),
        @WebInitParam(name="email", value="admin@example.com")
    }
)
public class ServletConfigDemoServlet extends GenericServlet {
    
    private static final long serialVersionUID = 62500890L;

    @Override
    public void service(ServletRequest request, 
            ServletResponse response)
            throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        String admin = servletConfig.getInitParameter("admin");
        String email = servletConfig.getInitParameter("email");
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<html><head></head><body>" + 
                "Admin:" + admin + 
                "<br/>Email:" + email +
                "</body></html>");
    }
}

使用额GenericServlet类的代码要比上面的直接实现Servlet接口的代码要干净很多

我们不需要自己保存ServletConfig对象,因为GenericServlet帮我们做了,我们也不需要实现所有的生命周期方法

我们需要做的仅仅是在service方法中编写我们的实现即可

现在我们来谈一谈Servlet API中的第二个包,javax.servlet.http

javabean

javabean属性

simple

get
set
就是普通成员变量的set和get方法

indexed

也是get和set只不过成员变量是数组

public class JavaBean2 {
	int[] dataSet= {1 2 3 4 5 6};
	public void JavaBean2() {
	}

	public void setDataSet(int[] x) {
		dataSet=x;
	}

	public void setDataSet(int index int x) {
		dataSet[index]=x;
	}

	public int[] getDataSet() {
		return dataSet;
	}

	public int getDataSet(int x) {
		return dataSet[x];
	}
}

分为两种,一种是个对数组中的单个元素进行操作,另一种是对整个数组进行操作

bound

事件相关

import java.beans.*;

public class JavaBean3 {
	String ourString= "Hello";
	private PropertyChangeSupport changes = new PropertyChangeSupport(this);
	public void setString(String newString) {
		String oldString = ourString;
		ourString = newString;
		changes.firePropertyChange("ourString", oldString, newString);
	}
	public String getString() {
		return ourString;
	}
	public void addPropertyChangeListener(PropertyChangeListener l) {
		changes.addPropertyChangeListener(l);
	}
	
	public void removePropertyChangeListener(PropertyChangeListener l) {
		changes.removePropertyChangeListener(l);
	}
}

使用PropertyChangeSupport对象来监听javabeans的属性值的变化
将变化的属性名、旧属性值、新属性值作为参数传给监听者

Constrained

Constrained意为受约束的

Constrained属性的特点是当该属性的值要发生变化的时候,可以使用一个事件否决者对象来否决该属性值的改变

import java.beans.*;
public class JavaBean4 {
	private PropertyChangeSupport changes=new PropertyChangeSupport(this);
	private VetoableChangeSupport vetos=new VetoableChangeSupport(this);
	int ourPriceInCents;

	public void setPriceInCents(int newPriceInCents) throws PropertyVetoException {
		int oldPriceInCents=ourPriceInCents;
		vetos.fireVetoableChange("priceInCents", 
			new Integer(oldPriceInCents),
			new Integer(newPriceInCents));
		ourPriceInCents=newPriceInCents;
		changes.firePropertyChange("priceInCents",
			new Integer(oldPriceInCents),
			new Integer(newPriceInCents));
	}

	public void addVetoableChangeListener(VetoableChangeListener l) {
		vetos.addVetoableChangeListener(l);
	}
	public void removeVetoableChangeListener(VetoableChangeListener l) {
		vetos.removeVetoableChangeListener(l);
	}
	public void addPropertyChangeListener(PropertyChangeListener l) {
		changes.addPropertyChangeListener(l);
	}
	public void removePropertyChangeListener(PropertyChangeListener l) {
		changes.removePropertyChangeListener(l);
	}
}

关键对象VetoableChangeSupportveto [ˈviːtəʊ],意为否决

注意代码:

vetos.fireVetoableChange("priceInCents", 
			new Integer(oldPriceInCents),
			new Integer(newPriceInCents));

fireVetoableChange方法用于通知事件否决者对象名为priceInCents的属性的值要发生改变了,原来的值为oldPriceInCents,改编后的新值为newPriceInCents

只要事件否决者对象队列中任意以一个事件否决者对象否定了该事件的发生,那么就会抛出PropertyVetoException异常,这样一来,代码的执行就会中断,后面的代码就不会执行了,那么oldPriceInCents属性的值也就改变不了了

如果没有对象进行否决的话,oldPriceInCents属性的值就会根据下面的代码做出改变

事件监听者和事件否决者这两种对象一个用于响应事件,另一个用于否决事件

javabeans的事件模型

事件状态对象

与事件有关的状态信息一般都封装在一个事件状态对象中 这种对象必须是java.util.EventObject类的子类 按设计习惯 这种事件状态对象类的名应以Event结尾

事件状态对象示例: 鼠标移动事件

import java.awt.Point;
public class MouseMovedExamEvent extends java.util.EventObject {
	protected int x;
	protected int y;
	public void MouseMovedExampleEvent(Component source, Point location) {
		super(source);
		x = location.x;
		y = location.y;
	}
	public Point getLocation() {
		return new Point(x y);
	}
}

事件监听者接口与事件监听者

JavaBeans事件模型中 事件操纵方法都被定义在继承了java.util.EventListener接口的事件监听者接口中

事件监听者接口的命名一般以Listener结尾

事件监听者接口继承自java.util.EventListener接口,并声明事件监听者需要实现的方法

然后事件监听者实现事件监听者接口中的方法

import java.beans.*;
//定义事件状态对象类
public class MouseMovedExampleEvent extends java.util.EventObject {
	// 在此类中包含了与鼠标移动事件有关的状态信息
}

//定义了鼠标移动事件的事件监听者接口
interface MouseMovedExampleListener extends java.util.EventListener {
	//在这个接口中定义了鼠标移动事件监听者所应支持的方法
	void mouseMoved(MouseMovedExampleEvent mme);
}
//定义事件监听者
class ArbitraryObject implements MouseMovedExampleListener {
	public void mouseMoved(MouseMovedExampleEvent mme) {
		//代码省略
	}
}

MouseMovedExampleListener事件监听者接口

ArbitraryObject事件监听者

事件监听者需要注册到事件源的监听者队列中才可对特定事件进行监听,因此事件源需要为事件监听者提供注册和注销方法

在实际编程中,事件监听者的注册和注销方法必须使用标准的设计格式 :

public void add<ListenerType>(<ListenerType> listener);

public void remove<ListenerType>(<ListenerType> listener);

ListenerType为事件监听者对象的类型

事件源类示例:

import java.util.*;

//首先定义了一个事件监听者接口
public interface ModelChangedListener extends java.util.EventListener {
	void modelChanged(EventObject e);
}

//定义事件监听者
class ModelChangedEventObject implements ModelChangedListener {
	public void modelChanged(EventObject e) {
		//代码省略
	}
}

//接着定义事件源类
public class EventExam {
	//定义了一个储存事件监听者的数组
	private Vector listeners = new Vector(); //上面设计格式中的<ListenerType>在此处即是下面的ModelChangedListener
	//把监听者注册入listeners数组中
	public void addModelChangedListener(ModelChangedListener mcl) {
		listeners.addElement(mcl);
	}

	//把监听者从listeners中注销
	public void removeModelChangedListener(ModelChangedListener mcl) {
		listeners.removeElement(mcl);
	}



	protected void notifyModelChanged() {
		//事件源对象使用本方法通知监听者发生了modelChanged事件
		Vector l;
		EventObject e = new EventObject(this);
		//首先要把监听者拷贝到l数组中 冻结EventListeners的状态以传递事件
		//这样来确保在事件传递到所有监听者之前,已接收了事件的目标监听者的对
		//应方法暂不生效
		synchronized(this) {
			l = (Vector)listeners.clone();
		}
		for (int i = 0; i < l.size(); i++) {
			//依次通知注册在监听者队列中的每个事件监听者发生了modelChanged
			//事件,并把事件状态对象e作为参数传递给监听者队列中的每个监听者
			((ModelChangedListener)l.elementAt(i)).modelChanged(e);
		}
	}
}

事件适配器类

当事件源发出一个事件,而有几个事件监听者对象都可接收该事件,但只有指定的监听者对象可以做出反应时,就要在事件源与事件监听者之间插入一个事件适配器类,由适配器类来指定事件应该是由哪些事件监听者来响应,再由它来转发事件

JSP使用JavaBeans

相关操作指令

<jsp:useBean>

用于在JSP页面中实例化一个JavaBeans组件

这个实例化的JavaBeans组件对象将可以在这个JSP程序的其它地方被调用

语法:

<jsp:useBean id="name" scope="page|request|session|application" typeSpec /> 
或者
<jsp:useBean id="name" scope="page|request|session|application" typeSpec /> 
body 
</jsp:useBean>

id属性用于在同一JSP程序中识别不同的JavaBeans组件实例

scope是作用范围

typeSpec可能是如下的四种形式之一:

  • class="className"
  • class="className" type="typeName"
  • beanName="beanName" type="typeName"
  • type="typeName"

获取JavaBeans实例对象的属性使用<jsp:getProperty>或者在jsp程序中调用JavaBeans对象的getXXX()方法

获取nameName(javabeans类名)的JavaBeans对象的name属性的值
<jsp:getProperty name="Name" property="name" />

相对的还有set,setProperty有两种形式

1、<jsp:setProperty id="Name" property="*" /> 

2、<jsp:setProperty id="Name" property="propertyNumber" value="string" /> 

前者的功能是根据已提交的表单中的数据 设置这个JavaBean中相应(JavaBeans属性的名称和表单对象的名称要相同)的属性值

后者的功能是把这个JavaBeans的指定的属性设为指定的值

这本破书(《jsp高级编程 2001版》)真他妈坑爹,把成员变量的名字写成大写的,而且还和usebean指令上的id的名字一样,一个是Hello,一个是hello,我把代码贴出来,保证你找半天也找不出来到底错在哪

jsp:

<%-- 
File Name:useBean.jsp 
Author:fancy 
Date:2001.3.26 
Note:use javabean to say hello world! 
--%> 
<jsp:useBean id="hello" scope="page" class="test.HelloWorld" /> 
<jsp:getProperty name="hello" property="Hello" /> 
<br> 
<% 
hello.setHello("Are you want to talk to me?"); 
%> 
<%=hello.getHello()%>

java:

package test;
public class HelloWorld {
	String Hello="hello world I am fancy!";

	public void setHello(String name) {
		Hello=name;
	}
	public String getHello() {
		return Hello;
	}
}

而且英文语法还是错的:
"Are you want to talk to me?"

这什么玩艺儿?Are you kidding me? 明明该用do的,却用了个are,扯淡

如果直接使用它的代码会报如下错误

Cannot find any information on property [Hello] in a bean of type [test.HelloWorld]

把Hello换成其他的名字,并将首字母小写即可解决

getProperty只有在当前运行环境不支持EL(expression language)的时候才有用,如果当前环境支持EL,就使用EL而不要使用getProperty,因为EL要方便得多

<jsp:useBean id="asdasd" scope="page" class="test.HelloWorld" /> 
EL: ${asdasd.hello} 
<br> 

JSP(一)_html_02

scope

application

作用范围是整个JSP应用程序

运行一下下面的示例代码就懂了

jsp:

<%-- 
File Name:useCounter.jsp 
Author:fancy 
Date:2001.3.26 
Note:use javabean to say hello world! 
--%> 

<jsp:useBean id="counter" scope="application" class="test.Counter" /> 
<br> 
hello, you are the
<% 
out.print(counter.getCount()); 
counter.addCount(); 
%>th visitor

java:

package test;
public class Counter {
	int Count=1;

	public void addCount() {
		Count++;
	}
	public int getCount() {
		return Count;
	}
}

session

session作用范围其实就是Session对象的作用范围

jsp:

<%-- 
File Name:beanPage1.jsp
Author:fancy 
Date:2001.3.26 
Note:use Counter to calculate how many pages this user have visited 
--%> 
<jsp:useBean id="counter" scope="session" class="test.Counter" /> 
<br> 
first page
<br> 
hello, you have been visited
<% 
out.println(counter.getCount()); 
counter.addCount(); 
%>pages

与上面的不同之处,就是把scope的值由application换成了session

这样一来,只要换了浏览器,counter就会重新计数

request

这个作用范围和request对象的作用范围是一致的

当一个JSP程序使用<jsp:forward>操作指令定向到另外一个JSP程序或者是使用<jsp:include>操作指令导入另外的JSP程序,那么第一个JSP程序会把Request对象传送到下一个JSP程序,而属于Request ScopeJavaBeans组件对象也将伴随着Reques对象送出,被第二个JSP程序接收

通过下面的示例程序可以理解request作用范围,beanPage3.jspbeanPage4.jsp所持有的bean对象是同一个对象

RequestBean.java:

package test;
public class RequestBean {
	String url="index.jsp";

	public void Counter() {

	}
	public void setURL(String strURL) {
		url=strURL;
	}
	public String getURL() {
		return url;
	}
}

beanPage3,jsp:

<%-- 
File Name:beanPage3.jsp 
Author:fancy 
Date:2001.3.26 
Note:use RequestBean to pass info before two different jsp page 
--%> 
<jsp:useBean id="reqBean" scope="request" class="test.RequestBean" /> 
call page: beanPage3.jsp 
<% 
reqBean.setURL("beanPage3.jsp"); 
%> 
<br> 
<br> 
<jsp:include page="beanPage4.jsp" flush="true" />

beanPage4,jsp:

<%-- 
File Name:beanPage4.jsp 
Author:fancy 
Date:2001.3.26 
Note:use RequestBean to pass info before two different jsp page 
--%> 
<jsp:useBean id="reqBean" scope="request" class="test.RequestBean" /> 
callee page: beanPage4.jsp 
<br> 
this page is called by
<% 
out.println(reqBean.getURL()); 
%>

JSP(一)_其他_03

page

page在四种作用范围中是最小的

它只作用于当前页面

只要当前jsp页面一执行结束,那么作用范围为page的bean实例化对象也就不复存在了

看下面这个示例程序:

jsp:

<%--
File Name:date.jsp 
Author:fancy 
Date:2001.4.1 
Note:use JspCalendar bean to show the time info. 
--%> 
 <html>  
<jsp:useBean id='clock' scope='page' class='test.JspCalendar'/> 
<ul> 
	<li>Day of month: ${clock.dayOfMonth}
	<li>Year: ${clock.year}
	<li>Month: ${clock.month}
	<li>Time: ${clock.time}
	<li>Date: ${clock.date}
	<li>Day: ${clock.day}
	<li>Day Of Year: ${clock.dayOfYear}
	<li>Week Of Year: ${clock.weekOfYear}
	<li>era: ${clock.era}
	<li>DST Offset: ${clock.dstOffset}
	<li>Zone Offset: ${clock.zoneOffset}
</ul> 
</body> 
</html>

java:

package test;
import java.util.*;
public class JspCalendar {
	Calendar calendar = null;
	public JspCalendar() {
		calendar = Calendar.getInstance(TimeZone.getTimeZone("PST"));
		Date trialTime = new Date();
		calendar.setTime(trialTime);
	}
	public int getYear() {
		return calendar.get(Calendar.YEAR);
	}

	public String getMonth() {
		int m = getMonthInt();
		String[] months = new String [] {
			"January", "February", "March",
			"April", "May", "June",
			"July", "August", "September",
			"October", "November", "December"
		};
		if (m > 12)
			return "Unknown to Man";

		return months[m - 1];
	}
	public String getDay() {
		int x = getDayOfWeek();
		String[] days = new String[] {
			"Sunday", "Monday", "Tuesday", "Wednesday",
			"Thursday", "Friday", "Saturday"
		};
		if (x > 7)
			return "Unknown to Man";
		return days[x - 1];
	}

	public int getMonthInt() {
		return 1 + calendar.get(Calendar.MONTH);
	}
	public String getDate() {
		return getMonthInt() + "/" + getDayOfMonth() + "/" + getYear();
	}
	public String getTime() {
		return getHour() + ":" + getMinute() + ":" + getSecond();
	}
	public int getDayOfMonth() {
		return calendar.get(Calendar.DAY_OF_MONTH);
	}
	public int getDayOfYear() {
		return calendar.get(Calendar.DAY_OF_YEAR);
	}
	public int getWeekOfYear() {
		return calendar.get(Calendar.WEEK_OF_YEAR);
	}
	public int getWeekOfMonth() {
		return calendar.get(Calendar.WEEK_OF_MONTH);
	}
	public int getDayOfWeek() {
		return calendar.get(Calendar.DAY_OF_WEEK);
	}

	public int getHour() {
		return calendar.get(Calendar.HOUR_OF_DAY);
	}

	public int getMinute() {
		return calendar.get(Calendar.MINUTE);
	}

	public int getSecond() {
		return calendar.get(Calendar.SECOND);
	}
	public int getEra() {
		return calendar.get(Calendar.ERA);
	}
	public String getUSTimeZone() {
		String[] zones = new String[] {
			"Hawaii", "Alaskan", "Pacific",
			"Mountain", "Central", "Eastern"
		};# 
		int index = 10 + getZoneOffset();

		if (index <= 5) {
			return zones[10 + getZoneOffset()];
		} else {
			return "Only US Time Zones supported";
		}
	}
	public int getZoneOffset() {
		return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);
	}
	public int getDstOffset() {
		return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);
	}
	public int getAMPM() {
		return calendar.get(Calendar.AM_PM);
	}
}

由于作用范围设置的是page,所以每次刷新jsp页面得到的时间都不同

JavaBeans 封装数据库操作

创建一个可以访问数据库的JavaBean组件,然后在我们的jsp中去调用它

这本书可以说是写得很垃圾了
form表单都写错了

购物车示例代码

cats.html:

<html> 
<head> 
 <title>carts</title> 
</head> 
<body bgcolor="white"> 
<font size = 5 color="#CC0000"> 
<form method="POST" action="carts.jsp"> 
<BR> 
Please enter item to add or remove: 
<br> 
Add Item: 
<SELECT NAME="item"> 
	<OPTION>Beavis & Butt-head Video collection 
	<OPTION>X-files movie 
	<OPTION>Twin peaks tapes 
	<OPTION>NIN CD 
	<OPTION>JSP Book 
	<OPTION>Concert tickets 
	<OPTION>Love life 
	<OPTION>Switch blade 
	<OPTION>Rex Rugs & Rock n' Roll 
</SELECT> 
<br> <br> 
<INPUT TYPE=submit name="submit" value="add"> 
<INPUT TYPE=submit name="submit" value="remove"> 
</form> 
</FONT> 
</body> 
</html> 

carts.jsp:

<html> 
<jsp:useBean id="cart" scope="session" class="test.DummyCart" /> 
<jsp:setProperty name="cart" property="*" /> 
<% 
 cart.processRequest(request); 
%> 
<font size = 5 COLOR="#CC0000"> 
<br> You have the following items in your cart: 
<ol> 
<% 
String[] items = cart.getItems(); 
for (int i=0; i<items.length; i++) { 
	%> 
	<li> <%= items[i] %> 
	<% 
} 
%> 
</ol> 
</font> 
<hr> 
<%@ include file ="carts.html" %> 
</html>

DummyCart.java:

package test;
import javax.servlet.http.*;
import java.util.Vector;

public class DummyCart {
	Vector<String> v = new Vector<String>();
	String submit = null;
	String item = null;

	private void addItem(String name) {
		v.addElement(name);
	}
	private void removeItem(String name) {
		v.removeElement(name);
	}
	public void setItem(String name) {
		item = name;
	}

	public void setSubmit(String s) {
		submit = s;
	}
	public String[] getItems() {
		String[] s = new String[v.size()];
		v.copyInto(s);
		return s;
	}

	public void processRequest(HttpServletRequest request) {
		// null value for submit - user hit enter instead of clicking on
		// "add" or "remove"
		if (submit == null)
			addItem(item);
		if (submit.equals("add"))
			addItem(item);
		else if (submit.equals("remove"))
			removeItem(item);

		// reset at the end of the request
		reset();
	}
	
	// reset
	private void reset() {
		submit = null;
		item = null;
	}
}