--安装篇
简介:
WebLogic 是一套基于JAVA功能强大的电子商务套件,提供了许多功能强大的中间件以方便编程人员编写的JSP、SERVLET 等电子商务应用,可以为企业提供一个完整的商务应用解决方案。对于开发人员 WebLogic 可以在 www.bea.com 免费下载一套完整的 WebLogic,并得到一个限制了 IP 的license,用于学习和开发基于这个套件的代码。而要是需要正式的投入使用的话,那么就必须支付一定的费用获取没限制的license。由于这个套件基于这种发布方式,对于一般网站开发人员可以很轻易地得到 WebLogic 用于学习开发,当然投入使用是另一回事。
我获得的 WebLogic5.10是两个大大的zip文件,一个是WebLogic程序包,一个是资料文档。因为是基于JAVA,虽然在下载时有区分操作系统,但是我得到的那套经过实践发现在WINNT和LINUX都可以运行,下面主要是以LINUX的为例,WINNT的安装方法也差不多。
安装前准备:
在安装前,用户必须获得一套对应于用户的操作系统的JDK(在 www.sun.com 免费下载),安装好后把WebLogic5.10的压缩包解开,建议放在根目录上,这样会省去很多修改设置的麻烦,在linux下可以解在其他目录,然后在根目录再做一个硬连接也可以。
我的安装的文件目录是
/usr/local/jdk1.2/
/usr/local/weblogic/
ln -s / /usr/local/weblogic/
配置weblogic:
启动weblogic需要执行两个脚本文件:
linux:setEnv.sh和startWebLogic.sh
WINNT对应的是:setEnv.cmd和startWebLogic.cmd
1、weblogic.properties
打开/usr/local/weblogic/的 weblogic.properties 文件,找到这一行
weblogic.password.system=
这是设置管理员密码用的,这个设置项不能为空,所以必须设置一个可靠的管理员密码。
例如:weblogic.password.system=sdfjkdshfds
设置运行JSP:
# WEBLOGIC JSP PROPERTIES
# ------------------------------------------------
# Sets up automatic page compilation for JSP. Adjust init args for
# directory locations and uncomment to use.
#weblogic.httpd.register.*.jsp=\
#weblogic.servlet.JSPServlet
#weblogic.httpd.initArgs.*.jsp=\
#pageCheckSeconds=1,\
#compileCommand=c:/jdk1.2.1/bin/javac.exe, \
#workingDir=/weblogic/myserver/classfiles, \
#verbose=true
把那些注释删去,即改为
# WEBLOGIC JSP PROPERTIES
# ------------------------------------------------
# Sets up automatic page compilation for JSP. Adjust init args for
# directory locations and uncomment to use.
weblogic.httpd.register.*.jsp=\
weblogic.servlet.JSPServlet
weblogic.httpd.initArgs.*.jsp=\
pageCheckSeconds=1,\
compileCommand=c:/jdk1.2.1/bin/javac.exe, \
workingDir=/weblogic/myserver/classfiles, \
verbose=true
要注意的是还要配置好这一行:
compileCommand=/usr/local/jdk1.2/bin/javac, \
这是JDK的JAVA编译器的路径。
2、setEnv.sh
打开/weblogic/setEnv.sh,找到这一行
JAVA_HOME=/usr/java
改为
JAVA_HOME=/usr/local/jdk1.2/
3、 startWebLogic.sh
找到一个全是大写的 "LINUX" 字符串,改为 "Linux",很奇怪是为何要这样是吗?这是因为启动时,脚本文件调用了uname 指令来得到系统的名字,再与"LINUX" 字符串比较确认是否是linux系统,但是uname 指令来返回的系统的名字是Linux,所以我们要改一下,这应该是这个启动脚本的一个小小的BUG,WINT就不用那么麻烦了。
运行weblogic:
经过简单的配置就试运行了。
在目录/weblogic/下执行
. ./setEnv.sh (大家请留意,我这里有两个 ".",因为我们需要在当前的shell下执行这个脚本 )
./startWebLogic.sh
当看到成功启动 WebLogic 的信息时,说明启动成功了。
服务器简单的测试:
WebLogic 默认的WEB端口为7001,我们可以在打开一个浏览器输入地址
http://localhost:7001/
测试是否能连接得上。
Servlet的测试
如果能连得上得话,那么就可以进行下一步的Servlet的测试,在浏览器输入地址
http://localhost:7001/helloWorld
这个就是WebLogic Servlet的演示(至于怎样安装 Servlet请看下文)
JSP的测试
在目录 /weblogic/myserver/public_html/下建立一个名为test.jsp的文件
test.jsp
在浏览器输入地址
http://localhost:7001/test.jsp
测试能否看到正确的输出"test JSP"的信息。
--结构篇
要学习好一套系统首先要了解它的结构,本文详细的介绍 WebLogic 的一些结构和特点:
WebLogic的大部分配置是在 weblogic.properties 里完成的,只要仔细的研究这个文件就可以清楚得知关于 WebLogic 的一些结构和特点,下面就对 weblogic.properties 文件里的一些配置项作一些说明:
weblogic.httpd.document.oot=public_html/
这就是WebLogic 的WEB 服务器的根目录,即是目录/weblogic/myserver/public_html/
weblogic.password.system=sdfjkdshfds
这个前文已经提到,是设置管理员密码。
weblogic.system.listenPort=7001
这是设置 WebLogic 的 WEB 服务器端口。
weblogic.httpd.servlet.classpath=/weblogic/myserver/servletclasses
设置Servlet存放的路径
关于Servlet
安全的目的,在 WebLogic 下运行的 WebLogic 必须在 weblogic.properties 里登记后才能运行,例如上文的提到Servlet http://localhost:7001/helloWorld,
它在weblogic.properties 里的登记项是
weblogic.httpd.register.helloWorld=examples.servlets.HelloWorldServlet
实际上,这个 Servlet 的实际路径是
/weblogic/myserver/servletclasses/examples/servlets/HelloWorldServlet.class
对照一下weblogic.properties里的登记项和HelloWorldServlet.class文件的路径,应该不难找出其登记Servlet的规律吧。
在weblogic.properties里有一下几个Servlet的登记项:
weblogic.httpd.register.AdminEvents=admin.AdminEvents
weblogic.httpd.register.AdminClients=admin.AdminClients weblogic.httpd.register.AdminConnections=admin.AdminConnections weblogic.httpd.register.AdminJDBC=admin.AdminJDBC
weblogic.httpd.register.AdminLicense=admin.AdminLicense
weblogic.httpd.register.AdminMain=admin.AdminMain
weblogic.httpd.register.AdminProps=admin.AdminProps
weblogic.httpd.register.AdminRealm=admin.AdminRealm
weblogic.httpd.register.AdminThreads=admin.AdminThreads weblogic.httpd.register.AdminVersion=admin.AdminVersion
这就是管理员管理 WebLogic 用的Servlet,通过URL访问http://localhost:7001/AdminMain,在弹出的身份验证对话框了输入 system 和在 weblogic.password.system= 设置的密码,就可以进入 WebLogic 的Web管理界面进行管理。
2000年7月26日星期三
MVC构架渐行渐进
MVC:模型-视图-控制器结构,这种构架在VC中我们可以体会得更深一些。在JAVA中实现这种构架的目的是实现网页制作人员和开发人员的分工。然而这一知识点并不容易掌握,所需要读者了解的知识点尤其是对servlet的理解一定要深刻,所以这里我采用渐行渐进的步骤,从点到面逐步引导大家掌握这一技术。
首先,大家要看懂以下几个类的作用。每个类都有相应的说明,和导读内容。
Action接口
示例:/WEB-INF/classes/actions/Action.java
package actions;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Action
{
public ActionRouter perform(HttpServlet servlet,
HttpServletRequest req,HttpServletResponse res)
throws java.io.IOException,javax.servlet.ServletException;
}
Action接口定义了一个perform的方法,它向操作servlet、HTTP请求响应传递引用。
ActionFactory类
示例:/WEB-INF/classes/actions/ActionFactory.java
package actions;
import java.util.Hashtable;
public class ActionFactory
{
private Hashtable actions=new Hashtable();
public Action getAction(String classname,ClassLoader loader)
throws ClassNotFoundException,IllegalAccessException,InstantiationException
{
Action action=(Action)actions.get(classname);
if (action==null) //如果该操作未存储在哈希表中,操作库将首先创建它
{
Class klass=loader.loadClass(classname);//获取对象类型
action=(Action)klass.newInstance();//对象初始化
actions.put(classname,action);//存储到哈希表中
}
return action;
}
}
在操作库中保持操作的哈希表,该库的getAction方法由操作的Servlet调用,并返回对应于类名的操作。如果该操作未存储在哈希表中,操作库将首先创建它,然后再存储到哈希表中。以后遇到相同的操作请求,操作库将仅仅从哈希表中返回一个引用。
导读:GetClass():方法返回为一个Class类型的对象。例:
…
private LoginDB loginDB;
Class pt=loginDB.getClass();
System.out.println(“The Beans name is:”+pt.getName());
…
输出内容:The Beans name is: bean.LoginDB
大多数情况下定义一个Class的对象的目的是要引用getClass方法。
newInstance():这个方法将调用类的默认的构造器,由当前的Class对象表示,并把生成的对象作为Object类型返回。如果你想把该类型保存在某一类型的变量里,则需要将它转换
为相应的类型,否则将直接把结果存储在一个Object类型的变量里。这个方法会抛出两个异常:IllegalAccessException,InstantiationException
ActionRouter类
示例:/WEB-INF/classes/actions/ActionFactory.java
package actions;
import javax.servlet.GenericServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ActionRouter
{
private final String url;
private final boolean isForward;
public ActionRouter(String url)
{
this(url,true);
}
public ActionRouter(String url,boolean isForward)
{
this.url=url;
this.isForward=isForward;
}
public void route(GenericServlet servlet,HttpServletRequest req,HttpServletResponse res)
{
try
{
if (isForward)
{
//req.getRequestDispatcher(res.encodeURL(url)).forward(req,res);
//req.getRequestDispatcher(res.encodeURL("/index.jsp")).forward(req,res);
servlet.getServletContext().getRequestDispatcher(res.encodeURL(url)).forward(req,res);
}
else
{
res.sendRedirect(res.encodeRedirectURL(url));
}
}
catch(Exception e)
{
System.out.print(e);
}
}
}
操作路径选择器功能是转发或重定向请求。(所有类均可在/Tomcat 4.1/common/lib/ servlet.jar包中找到。)
导读:
前提知识:Servlet的软件包:打开Tomcat 4.1下的Servlet.jar我们发现Servlet包主要由javax.Servlet和javax.Servlet.http和javax.Servlet.jsp三个包组成,javax.Servlet.jsp这里就不计论。前两种包的所包含的方法和接口如下表所示:
包 提供的接口 提供的方法
javax.Servlet RequestDispatcher接口Servlet接口ServletConfig接口ServletContext接口ServletRequest接口ServletResponse接口SingleThreadModel接口 GenericServlet类ServletInputStream类ServletOutputStream类ServletException类UnavailableException类
javax.Servlet.http HttpServletRequest接口HttpServletResponse接口HttpSession接口HttpSessionContext接口HttpSessionBindingListener接口 Cookie类HttpServlet类HttpSessionBindingEvent类HttpUtils类
继承关系示例:
java.lang.Object
|
+--javax.servlet.GenericServlet
|
+--javax.servlet.http.HttpServlet
|
+--org.apache.struts.action.ActionServlet
1、GenericServlet类
Public abstract class GenericServlet implants Servlet
此类提供了servlet接口的基本实现部分,其service()方法被申明为abstract,因此需要被派生。init(ServletConfig conf)方法把servletConfig对象存储在一个private transient(私有临时)实例变量里,getServletConfig()方法返回指向本对象的指针,如果你重载此方法,将不能使用getServletConfig来获得ServletConfig对象,如果确实想重载,记住要包含对super.config的调用。2.1版的API提供一个重载的没有参数的init()方法。现在在init(ServletConfig)方法结束时有一个对init()的调用,尽管目前它是空的。2.1版API里面,此类实现了ServletConfig接口,这使得开发者不用获得ServletConfig对象情况下直接调用ServletConfig的方法,这些方法是:
getInitParameter(),getInitParameterNames(),getServletContext()。此类还包含两个写日志的方法,它们实际上调用的是ServletContext上的对应方法。log(String msg)方法将servlet的名称和msg参数写到容器的日志中,log(String msg,Throwable cause)除了包含servlet外还包含一个异常。
2、HttpServlet类:该类扩展了GenericServlet类并对servlet接口提供了为处理 HTML 表单提供了专门的方法。例:
service():
protected void service(HttpServletRequest req,HttpServletResponse res)
throws ServletException,IOException
public void service(HttpServletRequest req,HttpServletResponse res)
throws ServletException,IOException
该方法作为HTTP请求的分发器,这个方法在任何时候都不能被重载。当请求到来时,service()方法决定请求的类型(GET,POST,HEAD,OPTIONS,DELETE,PUT,TRACE),并把请求分发给相应的处理方法(doGet(),doPost(),doHead(),doOptions(),doDelete(),doPut(),doTrace())每个do方法具有和第一个service()相同的形式。为了响应特定类型的HTTP请求,我们必须重载相应的do方法。如果servlet收到一个HTTP请求而你没有重载相应的do方法,它就返回一个说明此方法对本资源不可用的标准HTTP错误。
HttpServlet类的init()方法、service()方法和destroy()方法,这三种方法表示了了一个servlet 的生命周期。即初始化时期、执行时期、结束时期。
3、HttpServletRequest接口:所有实现此接口的对象(例如从servlet容器传递的HTTP请求对象)都能让servlet通过自己的方法访问所有请求的数据。下面是一些用来获取表单数据的基本方法。其中定义的主要方法有:
a、 getParameter() =>public String getParameter(String key)
b、 getParameterValues()=> public String[] getParameterValues(String key) 如果一个参数可以返回多个值,比如复选框集合,则可以用此方法获得对应参数的所有值。如果请求信息中没有指定参数,则返回null。
c、 GetParameterNames()=> Public Enumeration getParameterNames()此方法返回一个Enumeration对象,包含对应请求的所有参数名字列表。
d、 获得传入路径的方法:例:
http://localhost:7001/myservlet/somepath/test?someparam=somevaluerequest.getPathInfo():返回/somepath/test
request.getRequestURL():http://localhost:7001/myservlet/somepath/test
request.getRequestURI():返回/myservlet/somepath/test
request.getServletPath():返回/myservlet
request.getQueryString():返回someparam=somevalue
4、HttpServletResponse接口:servlet容器提供一个实现该接口的对象并通过service()方法将它传递给servlet。通过此对象及其方法,servlet可以修改响应头并返回结果。其中定义的主要方法有:
a、 setContentType()=> public void setContentType(String type) 在给调用者发回响应前,必须用此方法来设置HTTP响应的MIME类型。可以是任何有效的MIME类型,当给浏览器返回HTML是就是”text/html”类型。
b、 getWriter()=>public PrintWriter getWriter()throws IOException此方法将返回PrintWriter对象,把servlet的结果作为文本返回给调用者。PrintWriter对象自动把Java内部的UniCode编码字符转换成正确的编码以使客户端能够阅读。
c、 getOutputStream()=>public ServletOutputStream getOutputStream() throws IOException此方法返回ServletOutputStream对象,它是java.io.OutputStream的一个子类。此对象向客户发送二进制数据。
d、 setHeader()=>public void setHeader(String name,String value) 此方法用来设置送回给客户的HTTP响应头。有一些快捷的方法用来改变某些常用的响应头,但有时也需要直接调用此方法。
5、javax.servlet.ServletContext接口: 安装在一个服务器中的一个特定URL名字空间(比如,/myapplication)下的所有Servlet,JSP,JavaBean等Web部件的集合构成了一个Web的应用,每一个Web应用(同一JVM),容器都会有一个背景对象,而javax.servlet.ServletContext接口就提供了访问这个背景对象的途径。
Servlet实例的getServletContext方法: 得到该Servlet运行其中的这个背景对象。从这个背景对象中你可以访问如下信息或资源:(注意该方法不是ServletContext的方法而是获取背景对象的方法由于HttpServlet继承Servlet的关系GenericServlet类和HttpServlet类同时具有该方法)
· 初始化参数 ServletContext.getInitParameter(String name)。
· 存储在背境中的对象 context.getAttribute(String name)
· 与本背景关联的资源 ServletContext.getResource(String path)
· 日志 ServletContext.log(String msg)
以上所示方法均为ServletContext所提供,值得一提的是对于存储在背境中的对象访问方法常用的还有:
context.setAttribute(String name, Object object);将特定名字绑定的任意类型的对象上。将把object对象绑定到名字name,存放在Servlet背景中,可供同一背景中的其他Servlet共享。其他Servlet可以通过context.getAttribute(String name),得到一个背景中的对象,或通过context.removeAttribute(String name)在背景中移除一个对象。
getRequestDispatcher方法:它的作用将一个包含路径的String传递给其它资源。该路径是相对于ServletContext的根路径的。.方法示例:
RequestDispatcher rd = request.getRequestDispatcher("SecondServlet");
rd.forward(request, response);.
该方法主要用于服务器重定向技术上,servlet中重定向的方法主要有两种分别由ServletContext接口和ServletRequest接口提供但二者提供的同名方法getRequestDispatcher却有很大的不同。在ActionRouter类示例中//req.getRequestDispatcher(res.encodeURL(url)).forward(req,res);表示的用ServletRequest接口提供方法进行重定向的。
操作Servlet[/b:9e5a30927b]
action
ActionServlet
action
*.do
部署信息把以.do结尾的URL映射到操作的servlet. JSP页面在引用的方法为:
action="">
用Servlet把URL映射到操作类
示例:ActionServlet类 /WEB-INF/classes/ ActionServlet
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import actions.Action;
import actions.ActionRouter;
import actions.ActionFactory;
public class ActionServlet extends HttpServlet
{
private ActionFactory factory=new ActionFactory();
public void init(ServletConfig config) throws ServletException
{
/*根据ActionRouter类导读内容中对HttpServlet类的描述来看,HttpServlet规定了必须执行的方法,该时期调用了init()方法,当Servlet被Servlet引擎载入后,接下来就会执行init()这个方法,因此我们可以重载这个方法以做一些我们自己的初始化的工作。在Servlet的生命期中,init()方法仅在服务器装入Servlet时被执行一次,此后无论有多少客户机访问这个Servlet,init()都不会被重复执行。*/
}
public void service(HttpServletRequest req, HttpServletResponse res)
throws java.io.IOException, ServletException
{
/*在Servlet被载入后,主要通过service()方法对外响应,该方法可以被同时、多次地呼叫。*/
try
{
Action action=factory.getAction(getClassname(req),getClass().getClassLoader());
ActionRouter router=action.perform(this,req,res);
router.route(this,req,res);
}
catch(Exception e)
{
throw new ServletException(e);
}
}
public String getClassname(HttpServletRequest req)
{
String Path=req.getServletPath();
int beginPos=Path.lastIndexOf("/");
int endPos=Path.lastIndexOf(".");
if (beginPos>-1 && endPos>beginPos)
{
Path=Path.substring(beginPos+1,endPos);
}
return Path;
}
}
该类的service方法实现了:从操作库中获取操作,然后调用接口的perform方法。由perform的实现返回一个操作路径,最后由ActionRouter类的route方法进行重定向操作。达到页面跳转的作用。
导读:getClassname(req)将获取操作类名,过程如下:
由req.getServletPath()获取servlet的路径为/ actions.LoginAction.do通过截取得到类名:actions.LoginAction。
getClass().getClassLoader():getClass()方法是类的一个方法,主要用于返回一个类型为Class的对象。该例中返回为:class ActionServlet
GetClassLoader()是Class类的一个方法,返回为ClassLoader对象。本例返回为:sun.misc.Launcher$AppClassLoader@92e78c。这说明加载ActionServlet类的类的加载者(classLoader的翻译)为AppClassLoader.那么AppClassLoader又是什么呢?下面我们来解答该问题:
首先要明确java虚拟机上所有的类,必须要加载才能运行。JVM在运行时会产生三个ClassLoader, 它们分别是Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader:
ClassLoader 作 用
Bootstrap ClassLoader 加载核心类库static const char classpathFormat[] ="%/lib/rt.jar:""%/lib/i18n.jar:""%/lib/sunrsasign.jar:""%/lib/jsse.jar:""%/lib/jce.jar:""%/lib/charsets.jar:""%/classes";这里我们可看到为什么在classpath里为什么不加载这些类
Extension ClassLoader 加载扩展类,即/lib/ext中的类。
AppClassLoader 加载Classpath中指定的类。
从上面可以看出,所有web应用程序的类都是AppClassLoader来加载的,三者的关系为:AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent为Bootstrap ClassLoader。加载一个类时,首先BootStrap先进行寻找,找不到再由ExtClassLoader寻找,最后才是AppClassLoader。ClassLoader这种加载类的模型被称为是委托模型。
下面我们要了解的问题是动态加载类原基本原理?或者说为什么我们要这么做?
测试页面testmvc.jsp[/b:d01b749036]示例:
/testmvc.jsp
Pleasa Login
用户名:
密 码:
测试页面:welcome.jsp
示例: /welcome.jsp
you are welcome
说明:这里我偷了个懒,该页面只写这句话。
BEAN:USER类
示例: /WEB-INF/Classes/beans/User.java
package bean;
public class User implements java.io.Serializable
{
private final String userName,password,hint;
public User(String userName,String password,String hint)
{
this.userName=userName;
this.password=password;
this.hint=hint;
}
public String getUserName()
{
return userName;
}
public String getPassWord()
{
return password;
}
public String getHint()
{
return hint;
}
public boolean equals(String uname,String pwd)
{
return getUserName().equals(uname)&& getPassWord().equals(pwd);
}
}
该类表示了一个用户,并提供了一个equals的方法,当用户名和口令匹配的时候,返回true值。
BEAN:LoginDB类
示例: /WEB-INF/Classes/beans/User.java
package bean;
import java.util.Iterator;
import java.util.Vector;
import java.io.*;
public class LoginDB implements Serializable
{
private Vector users=new Vector();
public void addUser(String uname,String pwd,String hint)//添加用户的方法
{
users.add(new User(uname,pwd,hint));
}
public User getUser(String uname,String pwd)//检索用户的方法
{
Iterator it=users.iterator();
User bean=null;
synchronized (users){
while(it.hasNext())
{
bean=(User)it.next();
if (bean.equals(uname,pwd))
return bean;
}
}
return null;
}
public String getHint(String uname)//对指定的用户提供返回口令提示的方法
{
Iterator it=users.iterator();
User bean=null;
synchronized (users)
{
while(it.hasNext())
{
if (bean.getUserName().equals(uname))
return bean.getHint();
}
}
return null;
}
}
LoginServlet类:
示例: /WEB-INF/Classes/LoginServlet.java
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.*;
import bean.*;
public class LoginServlet extends HttpServlet
{
private LoginDB loginDB;
public void init(ServletConfig config) throws ServletException
{
loginDB=new LoginDB();
}
public void service(HttpServletRequest req, HttpServletResponse res)
throws java.io.IOException, ServletException
{
loginDB.addUser("long","long","long");
User user=loginDB.getUser(req.getParameter("userName"),req.getParameter("PassWord"));
System.out.println("The name of loginDB is"+loginDB.getClass().getName());
//String user=req.getParameter("userName");
//System.out.println("get user name:"+user);
/*getServletContext().getRequestDispatcher(res.encodeURL("/index.jsp")).forward(req,res);*/
/*要注意getServletContext()和req两个对象的区别,经过实验应用getServletContext()进行重定向*/
/*总是不行,而应用req则可以*/
if (user!=null)
{
req.getRequestDispatcher(res.encodeURL("/welcome.jsp")).forward(req,res);
}
else
{
req.getRequestDispatcher(res.encodeURL("/adduser.jsp")).forward(req,res);
}
}
}
当testmvc.jsp的表单提交时,请求被发送到登录的Servlet,这段代码我没什么好说的,在使用mvc构架之前,我们喜欢用隐藏帧来处理表单提交的内容,实际LoginServlet.java就是替代了隐藏帧而已。
到此为止,请读者将所有的示例,按示例所示的路径存储好所有的类和jsp页面。下一步我们将讨论这个东东的玩法和原理。
对了,忘了告诉大家,我的测试环境是:win2000server,tomcat 4.1,jdk1.4,没有数据库
首先,大家要看懂以下几个类的作用。每个类都有相应的说明,和导读内容。
Action接口
示例:/WEB-INF/classes/actions/Action.java
package actions;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Action
{
public ActionRouter perform(HttpServlet servlet,
HttpServletRequest req,HttpServletResponse res)
throws java.io.IOException,javax.servlet.ServletException;
}
Action接口定义了一个perform的方法,它向操作servlet、HTTP请求响应传递引用。
ActionFactory类
示例:/WEB-INF/classes/actions/ActionFactory.java
package actions;
import java.util.Hashtable;
public class ActionFactory
{
private Hashtable actions=new Hashtable();
public Action getAction(String classname,ClassLoader loader)
throws ClassNotFoundException,IllegalAccessException,InstantiationException
{
Action action=(Action)actions.get(classname);
if (action==null) //如果该操作未存储在哈希表中,操作库将首先创建它
{
Class klass=loader.loadClass(classname);//获取对象类型
action=(Action)klass.newInstance();//对象初始化
actions.put(classname,action);//存储到哈希表中
}
return action;
}
}
在操作库中保持操作的哈希表,该库的getAction方法由操作的Servlet调用,并返回对应于类名的操作。如果该操作未存储在哈希表中,操作库将首先创建它,然后再存储到哈希表中。以后遇到相同的操作请求,操作库将仅仅从哈希表中返回一个引用。
导读:GetClass():方法返回为一个Class类型的对象。例:
…
private LoginDB loginDB;
Class pt=loginDB.getClass();
System.out.println(“The Beans name is:”+pt.getName());
…
输出内容:The Beans name is: bean.LoginDB
大多数情况下定义一个Class的对象的目的是要引用getClass方法。
newInstance():这个方法将调用类的默认的构造器,由当前的Class对象表示,并把生成的对象作为Object类型返回。如果你想把该类型保存在某一类型的变量里,则需要将它转换
为相应的类型,否则将直接把结果存储在一个Object类型的变量里。这个方法会抛出两个异常:IllegalAccessException,InstantiationException
ActionRouter类
示例:/WEB-INF/classes/actions/ActionFactory.java
package actions;
import javax.servlet.GenericServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ActionRouter
{
private final String url;
private final boolean isForward;
public ActionRouter(String url)
{
this(url,true);
}
public ActionRouter(String url,boolean isForward)
{
this.url=url;
this.isForward=isForward;
}
public void route(GenericServlet servlet,HttpServletRequest req,HttpServletResponse res)
{
try
{
if (isForward)
{
//req.getRequestDispatcher(res.encodeURL(url)).forward(req,res);
//req.getRequestDispatcher(res.encodeURL("/index.jsp")).forward(req,res);
servlet.getServletContext().getRequestDispatcher(res.encodeURL(url)).forward(req,res);
}
else
{
res.sendRedirect(res.encodeRedirectURL(url));
}
}
catch(Exception e)
{
System.out.print(e);
}
}
}
操作路径选择器功能是转发或重定向请求。(所有类均可在/Tomcat 4.1/common/lib/ servlet.jar包中找到。)
导读:
前提知识:Servlet的软件包:打开Tomcat 4.1下的Servlet.jar我们发现Servlet包主要由javax.Servlet和javax.Servlet.http和javax.Servlet.jsp三个包组成,javax.Servlet.jsp这里就不计论。前两种包的所包含的方法和接口如下表所示:
包 提供的接口 提供的方法
javax.Servlet RequestDispatcher接口Servlet接口ServletConfig接口ServletContext接口ServletRequest接口ServletResponse接口SingleThreadModel接口 GenericServlet类ServletInputStream类ServletOutputStream类ServletException类UnavailableException类
javax.Servlet.http HttpServletRequest接口HttpServletResponse接口HttpSession接口HttpSessionContext接口HttpSessionBindingListener接口 Cookie类HttpServlet类HttpSessionBindingEvent类HttpUtils类
继承关系示例:
java.lang.Object
|
+--javax.servlet.GenericServlet
|
+--javax.servlet.http.HttpServlet
|
+--org.apache.struts.action.ActionServlet
1、GenericServlet类
Public abstract class GenericServlet implants Servlet
此类提供了servlet接口的基本实现部分,其service()方法被申明为abstract,因此需要被派生。init(ServletConfig conf)方法把servletConfig对象存储在一个private transient(私有临时)实例变量里,getServletConfig()方法返回指向本对象的指针,如果你重载此方法,将不能使用getServletConfig来获得ServletConfig对象,如果确实想重载,记住要包含对super.config的调用。2.1版的API提供一个重载的没有参数的init()方法。现在在init(ServletConfig)方法结束时有一个对init()的调用,尽管目前它是空的。2.1版API里面,此类实现了ServletConfig接口,这使得开发者不用获得ServletConfig对象情况下直接调用ServletConfig的方法,这些方法是:
getInitParameter(),getInitParameterNames(),getServletContext()。此类还包含两个写日志的方法,它们实际上调用的是ServletContext上的对应方法。log(String msg)方法将servlet的名称和msg参数写到容器的日志中,log(String msg,Throwable cause)除了包含servlet外还包含一个异常。
2、HttpServlet类:该类扩展了GenericServlet类并对servlet接口提供了为处理 HTML 表单提供了专门的方法。例:
service():
protected void service(HttpServletRequest req,HttpServletResponse res)
throws ServletException,IOException
public void service(HttpServletRequest req,HttpServletResponse res)
throws ServletException,IOException
该方法作为HTTP请求的分发器,这个方法在任何时候都不能被重载。当请求到来时,service()方法决定请求的类型(GET,POST,HEAD,OPTIONS,DELETE,PUT,TRACE),并把请求分发给相应的处理方法(doGet(),doPost(),doHead(),doOptions(),doDelete(),doPut(),doTrace())每个do方法具有和第一个service()相同的形式。为了响应特定类型的HTTP请求,我们必须重载相应的do方法。如果servlet收到一个HTTP请求而你没有重载相应的do方法,它就返回一个说明此方法对本资源不可用的标准HTTP错误。
HttpServlet类的init()方法、service()方法和destroy()方法,这三种方法表示了了一个servlet 的生命周期。即初始化时期、执行时期、结束时期。
3、HttpServletRequest接口:所有实现此接口的对象(例如从servlet容器传递的HTTP请求对象)都能让servlet通过自己的方法访问所有请求的数据。下面是一些用来获取表单数据的基本方法。其中定义的主要方法有:
a、 getParameter() =>public String getParameter(String key)
b、 getParameterValues()=> public String[] getParameterValues(String key) 如果一个参数可以返回多个值,比如复选框集合,则可以用此方法获得对应参数的所有值。如果请求信息中没有指定参数,则返回null。
c、 GetParameterNames()=> Public Enumeration getParameterNames()此方法返回一个Enumeration对象,包含对应请求的所有参数名字列表。
d、 获得传入路径的方法:例:
http://localhost:7001/myservlet/somepath/test?someparam=somevaluerequest.getPathInfo():返回/somepath/test
request.getRequestURL():http://localhost:7001/myservlet/somepath/test
request.getRequestURI():返回/myservlet/somepath/test
request.getServletPath():返回/myservlet
request.getQueryString():返回someparam=somevalue
4、HttpServletResponse接口:servlet容器提供一个实现该接口的对象并通过service()方法将它传递给servlet。通过此对象及其方法,servlet可以修改响应头并返回结果。其中定义的主要方法有:
a、 setContentType()=> public void setContentType(String type) 在给调用者发回响应前,必须用此方法来设置HTTP响应的MIME类型。可以是任何有效的MIME类型,当给浏览器返回HTML是就是”text/html”类型。
b、 getWriter()=>public PrintWriter getWriter()throws IOException此方法将返回PrintWriter对象,把servlet的结果作为文本返回给调用者。PrintWriter对象自动把Java内部的UniCode编码字符转换成正确的编码以使客户端能够阅读。
c、 getOutputStream()=>public ServletOutputStream getOutputStream() throws IOException此方法返回ServletOutputStream对象,它是java.io.OutputStream的一个子类。此对象向客户发送二进制数据。
d、 setHeader()=>public void setHeader(String name,String value) 此方法用来设置送回给客户的HTTP响应头。有一些快捷的方法用来改变某些常用的响应头,但有时也需要直接调用此方法。
5、javax.servlet.ServletContext接口: 安装在一个服务器中的一个特定URL名字空间(比如,/myapplication)下的所有Servlet,JSP,JavaBean等Web部件的集合构成了一个Web的应用,每一个Web应用(同一JVM),容器都会有一个背景对象,而javax.servlet.ServletContext接口就提供了访问这个背景对象的途径。
Servlet实例的getServletContext方法: 得到该Servlet运行其中的这个背景对象。从这个背景对象中你可以访问如下信息或资源:(注意该方法不是ServletContext的方法而是获取背景对象的方法由于HttpServlet继承Servlet的关系GenericServlet类和HttpServlet类同时具有该方法)
· 初始化参数 ServletContext.getInitParameter(String name)。
· 存储在背境中的对象 context.getAttribute(String name)
· 与本背景关联的资源 ServletContext.getResource(String path)
· 日志 ServletContext.log(String msg)
以上所示方法均为ServletContext所提供,值得一提的是对于存储在背境中的对象访问方法常用的还有:
context.setAttribute(String name, Object object);将特定名字绑定的任意类型的对象上。将把object对象绑定到名字name,存放在Servlet背景中,可供同一背景中的其他Servlet共享。其他Servlet可以通过context.getAttribute(String name),得到一个背景中的对象,或通过context.removeAttribute(String name)在背景中移除一个对象。
getRequestDispatcher方法:它的作用将一个包含路径的String传递给其它资源。该路径是相对于ServletContext的根路径的。.方法示例:
RequestDispatcher rd = request.getRequestDispatcher("SecondServlet");
rd.forward(request, response);.
该方法主要用于服务器重定向技术上,servlet中重定向的方法主要有两种分别由ServletContext接口和ServletRequest接口提供但二者提供的同名方法getRequestDispatcher却有很大的不同。在ActionRouter类示例中//req.getRequestDispatcher(res.encodeURL(url)).forward(req,res);表示的用ServletRequest接口提供方法进行重定向的。
操作Servlet[/b:9e5a30927b]
action
ActionServlet
action
*.do
部署信息把以.do结尾的URL映射到操作的servlet. JSP页面在引用的方法为:
action="">
用Servlet把URL映射到操作类
示例:ActionServlet类 /WEB-INF/classes/ ActionServlet
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import actions.Action;
import actions.ActionRouter;
import actions.ActionFactory;
public class ActionServlet extends HttpServlet
{
private ActionFactory factory=new ActionFactory();
public void init(ServletConfig config) throws ServletException
{
/*根据ActionRouter类导读内容中对HttpServlet类的描述来看,HttpServlet规定了必须执行的方法,该时期调用了init()方法,当Servlet被Servlet引擎载入后,接下来就会执行init()这个方法,因此我们可以重载这个方法以做一些我们自己的初始化的工作。在Servlet的生命期中,init()方法仅在服务器装入Servlet时被执行一次,此后无论有多少客户机访问这个Servlet,init()都不会被重复执行。*/
}
public void service(HttpServletRequest req, HttpServletResponse res)
throws java.io.IOException, ServletException
{
/*在Servlet被载入后,主要通过service()方法对外响应,该方法可以被同时、多次地呼叫。*/
try
{
Action action=factory.getAction(getClassname(req),getClass().getClassLoader());
ActionRouter router=action.perform(this,req,res);
router.route(this,req,res);
}
catch(Exception e)
{
throw new ServletException(e);
}
}
public String getClassname(HttpServletRequest req)
{
String Path=req.getServletPath();
int beginPos=Path.lastIndexOf("/");
int endPos=Path.lastIndexOf(".");
if (beginPos>-1 && endPos>beginPos)
{
Path=Path.substring(beginPos+1,endPos);
}
return Path;
}
}
该类的service方法实现了:从操作库中获取操作,然后调用接口的perform方法。由perform的实现返回一个操作路径,最后由ActionRouter类的route方法进行重定向操作。达到页面跳转的作用。
导读:getClassname(req)将获取操作类名,过程如下:
由req.getServletPath()获取servlet的路径为/ actions.LoginAction.do通过截取得到类名:actions.LoginAction。
getClass().getClassLoader():getClass()方法是类的一个方法,主要用于返回一个类型为Class的对象。该例中返回为:class ActionServlet
GetClassLoader()是Class类的一个方法,返回为ClassLoader对象。本例返回为:sun.misc.Launcher$AppClassLoader@92e78c。这说明加载ActionServlet类的类的加载者(classLoader的翻译)为AppClassLoader.那么AppClassLoader又是什么呢?下面我们来解答该问题:
首先要明确java虚拟机上所有的类,必须要加载才能运行。JVM在运行时会产生三个ClassLoader, 它们分别是Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader:
ClassLoader 作 用
Bootstrap ClassLoader 加载核心类库static const char classpathFormat[] ="%/lib/rt.jar:""%/lib/i18n.jar:""%/lib/sunrsasign.jar:""%/lib/jsse.jar:""%/lib/jce.jar:""%/lib/charsets.jar:""%/classes";这里我们可看到为什么在classpath里为什么不加载这些类
Extension ClassLoader 加载扩展类,即/lib/ext中的类。
AppClassLoader 加载Classpath中指定的类。
从上面可以看出,所有web应用程序的类都是AppClassLoader来加载的,三者的关系为:AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent为Bootstrap ClassLoader。加载一个类时,首先BootStrap先进行寻找,找不到再由ExtClassLoader寻找,最后才是AppClassLoader。ClassLoader这种加载类的模型被称为是委托模型。
下面我们要了解的问题是动态加载类原基本原理?或者说为什么我们要这么做?
测试页面testmvc.jsp[/b:d01b749036]示例:
/testmvc.jsp
Pleasa Login
用户名:
密 码:
测试页面:welcome.jsp
示例: /welcome.jsp
you are welcome
说明:这里我偷了个懒,该页面只写这句话。
BEAN:USER类
示例: /WEB-INF/Classes/beans/User.java
package bean;
public class User implements java.io.Serializable
{
private final String userName,password,hint;
public User(String userName,String password,String hint)
{
this.userName=userName;
this.password=password;
this.hint=hint;
}
public String getUserName()
{
return userName;
}
public String getPassWord()
{
return password;
}
public String getHint()
{
return hint;
}
public boolean equals(String uname,String pwd)
{
return getUserName().equals(uname)&& getPassWord().equals(pwd);
}
}
该类表示了一个用户,并提供了一个equals的方法,当用户名和口令匹配的时候,返回true值。
BEAN:LoginDB类
示例: /WEB-INF/Classes/beans/User.java
package bean;
import java.util.Iterator;
import java.util.Vector;
import java.io.*;
public class LoginDB implements Serializable
{
private Vector users=new Vector();
public void addUser(String uname,String pwd,String hint)//添加用户的方法
{
users.add(new User(uname,pwd,hint));
}
public User getUser(String uname,String pwd)//检索用户的方法
{
Iterator it=users.iterator();
User bean=null;
synchronized (users){
while(it.hasNext())
{
bean=(User)it.next();
if (bean.equals(uname,pwd))
return bean;
}
}
return null;
}
public String getHint(String uname)//对指定的用户提供返回口令提示的方法
{
Iterator it=users.iterator();
User bean=null;
synchronized (users)
{
while(it.hasNext())
{
if (bean.getUserName().equals(uname))
return bean.getHint();
}
}
return null;
}
}
LoginServlet类:
示例: /WEB-INF/Classes/LoginServlet.java
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.*;
import bean.*;
public class LoginServlet extends HttpServlet
{
private LoginDB loginDB;
public void init(ServletConfig config) throws ServletException
{
loginDB=new LoginDB();
}
public void service(HttpServletRequest req, HttpServletResponse res)
throws java.io.IOException, ServletException
{
loginDB.addUser("long","long","long");
User user=loginDB.getUser(req.getParameter("userName"),req.getParameter("PassWord"));
System.out.println("The name of loginDB is"+loginDB.getClass().getName());
//String user=req.getParameter("userName");
//System.out.println("get user name:"+user);
/*getServletContext().getRequestDispatcher(res.encodeURL("/index.jsp")).forward(req,res);*/
/*要注意getServletContext()和req两个对象的区别,经过实验应用getServletContext()进行重定向*/
/*总是不行,而应用req则可以*/
if (user!=null)
{
req.getRequestDispatcher(res.encodeURL("/welcome.jsp")).forward(req,res);
}
else
{
req.getRequestDispatcher(res.encodeURL("/adduser.jsp")).forward(req,res);
}
}
}
当testmvc.jsp的表单提交时,请求被发送到登录的Servlet,这段代码我没什么好说的,在使用mvc构架之前,我们喜欢用隐藏帧来处理表单提交的内容,实际LoginServlet.java就是替代了隐藏帧而已。
到此为止,请读者将所有的示例,按示例所示的路径存储好所有的类和jsp页面。下一步我们将讨论这个东东的玩法和原理。
对了,忘了告诉大家,我的测试环境是:win2000server,tomcat 4.1,jdk1.4,没有数据库
Tomcat配置技巧
现在开发Java Web应用,建立和部署Web内容是一件很简单的工作。使用Jakarta Tomcat作为Servlet和JSP容器的人已经遍及全世界。Tomcat具有免费、跨平台等诸多特性,并且更新得很快,现在非常的流行。
你所需要做的就是:按照你的需求配置Tomcat,只要你正确配置,Tomcat一般都能适合你的要求。下面是一系列关于Tomcat的配置技巧,这些技巧源自于我的书:《Tomcat权威指南》,希望对你有所帮助。?? Jason Brittain
1. 配置系统管理(Admin Web Application)
大多数商业化的J2EE服务器都提供一个功能强大的管理界面,且大都采用易于理解的Web应用界面。Tomcat按照自己的方式,同样提供一个成熟的管理工具,并且丝毫不逊于那些商业化的竞争对手。Tomcat的Admin Web Application最初在4.1版本时出现,当时的功能包括管理context、data source、user和group等。当然也可以管理像初始化参数,user、group、role的多种数据库管理等。在后续的版本中,这些功能将得到很大的扩展,但现有的功能已经非常实用了。
Admin Web Application被定义在自动部署文件:CATALINA_BASE/webapps/admin.xml 。
译者注:CATALINA_BASE即tomcat安装目录下的server目录
你必须编辑这个文件,以确定Context中的docBase参数是绝对路径。也就是说,CATALINA_BASE/webapps/admin.xml 的路径是绝对路径。作为另外一种选择,你也可以删除这个自动部署文件,而在server.xml文件中建立一个Admin Web Application的context,效果是一样的。你不能管理Admin Web Application这个应用,换而言之,除了删除CATALINA_BASE/webapps/admin.xml ,你可能什么都做不了。
如果你使用UserDatabaseRealm(默认),你将需要添加一个user以及一个role到CATALINA_BASE/conf/tomcat-users.xml 文件中。你编辑这个文件,添加一个名叫“admin”的role 到该文件中,如下:
<role name="admin"/>
你同样需要有一个用户,并且这个用户的角色是“admin”。象存在的用户那样,添加一个用户(改变密码使其更加安全):
<user name="admin" password="deep_dark_secret" roles="admin"/>
当你完成这些步骤后,请重新启动Tomcat,访问http://localhost:8080/admin,你将看到一个登录界面。Admin Web Application采用基于容器管理的安全机制,并采用了Jakarta Struts框架。一旦你作为“admin”角色的用户登录管理界面,你将能够使用这个管理界面配置Tomcat。
2.配置应用管理(Manager Web Application)
Manager Web Application让你通过一个比Admin Web Application更为简单的用户界面,执行一些简单的Web应用任务。
Manager Web Application被被定义在一个自动部署文件中:
CATALINA_BASE/webapps/manager.xml 。
你必须编辑这个文件,以确保context的docBase参数是绝对路径,也就是说CATALINA_HOME/server/webapps/manager的绝对路径。
(译者注:CATALINA_HOME即tomcat安装目录)
如果你使用的是UserDatabaseRealm,那么你需要添加一个角色和一个用户到CATALINA_BASE/conf/tomcat-users.xml文件中。接下来,编辑这个文件,添加一个名为“manager”的角色到该文件中:
<role name=”manager”>
你同样需要有一个角色为“manager”的用户。像已经存在的用户那样,添加一个新用户(改变密码使其更加安全):
<user name="manager" password="deep_dark_secret" roles="manager"/>
然后重新启动Tomcat,访问http://localhost/manager/list,将看到一个很朴素的文本型管理界面,或者访问http://localhost/manager/html/list,将看到一个HMTL的管理界面。不管是哪种方式都说明你的Manager Web Application现在已经启动了。
Manager application让你可以在没有系统管理特权的基础上,安装新的Web应用,以用于测试。如果我们有一个新的web应用位于/home/user/hello下在,并且想把它安装到 /hello下,为了测试这个应用,我们可以这么做,在第一个文件框中输入“/hello”(作为访问时的path),在第二个文本框中输入“file:/home/user/hello”(作为Config URL)。
Manager application还允许你停止、重新启动、移除以及重新部署一个web应用。停止一个应用使其无法被访问,当有用户尝试访问这个被停止的应用时,将看到一个503的错误??“503 - This application is not currently available”。
移除一个web应用,只是指从Tomcat的运行拷贝中删除了该应用,如果你重新启动Tomcat,被删除的应用将再次出现(也就是说,移除并不是指从硬盘上删除)。
3.部署一个web应用
有两个办法可以在系统中部署web服务。
1> 拷贝你的WAR文件或者你的web应用文件夹(包括该web的所有内容)到$CATALINA_BASE/webapps目录下。
2> 为你的web服务建立一个只包括context内容的XML片断文件,并把该文件放到$CATALINA_BASE/webapps目录下。这个web应用本身可以存储在硬盘上的任何地方。
如果你有一个WAR文件,你若想部署它,则只需要把该文件简单的拷贝到CATALINA_BASE/webapps目录下即可,文件必须以“.war”作为扩展名。一旦Tomcat监听到这个文件,它将(缺省的)解开该文件包作为一个子目录,并以WAR文件的文件名作为子目录的名字。接下来,Tomcat将在内存中建立一个context,就好象你在server.xml文件里建立一样。当然,其他必需的内容,将从server.xml中的DefaultContext获得。
部署web应用的另一种方式是写一个Context XML片断文件,然后把该文件拷贝到CATALINA_BASE/webapps目录下。一个Context片断并非一个完整的XML文件,而只是一个context元素,以及对该应用的相应描述。这种片断文件就像是从server.xml中切取出来的context元素一样,所以这种片断被命名为“context片断”。
举个例子,如果我们想部署一个名叫MyWebApp.war的应用,该应用使用realm作为访问控制方式,我们可以使用下面这个片断:
<!--
Context fragment for deploying MyWebApp.war
-->
<Context path="/demo" docBase="webapps/MyWebApp.war"
debug="0" privileged="true">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Context>
把该片断命名为“MyWebApp.xml”,然后拷贝到CATALINA_BASE/webapps目录下。
这种context片断提供了一种便利的方法来部署web应用,你不需要编辑server.xml,除非你想改变缺省的部署特性,安装一个新的web应用时不需要重启动Tomcat。
4.配置虚拟主机(Virtual Hosts)
关于server.xml中“Host”这个元素,只有在你设置虚拟主机的才需要修改。虚拟主机是一种在一个web服务器上服务多个域名的机制,对每个域名而言,都好象独享了整个主机。实际上,大多数的小型商务网站都是采用虚拟主机实现的,这主要是因为虚拟主机能直接连接到Internet并提供相应的带宽,以保障合理的访问响应速度,另外虚拟主机还能提供一个稳定的固定IP。
基于名字的虚拟主机可以被建立在任何web服务器上,建立的方法就是通过在域名服务器(DNS)上建立IP地址的别名,并且告诉web服务器把去往不同域名的请求分发到相应的网页目录。因为这篇文章主要是讲Tomcat,我们不准备介绍在各种操作系统上设置DNS的方法,如果你在这方面需要帮助,请参考《DNS and Bind》一书,作者是Paul Albitz and Cricket Liu (OReilly)。为了示范方便,我将使用一个静态的主机文件,因为这是测试别名最简单的方法。
在Tomcat中使用虚拟主机,你需要设置DNS或主机数据。为了测试,为本地IP设置一个IP别名就足够了,接下来,你需要在server.xml中添加几行内容,如下:
<Server port="8005" shutdown="SHUTDOWN" debug="0">
<Service name="Tomcat-Standalone">
<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8080" minProcessors="5" maxProcessors="75"
enableLookups="true" redirectPort="8443"/>
<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8443" minProcessors="5" maxProcessors="75"
acceptCount="10" debug="0" scheme="https" secure="true"/>
<Factory className="org.apache.coyote.tomcat4.CoyoteServerSocketFactory"
clientAuth="false" protocol="TLS" />
</Connector>
<Engine name="Standalone" defaultHost="localhost" debug="0">
<!-- This Host is the default Host -->
<Host name="localhost" debug="0" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="ROOT" debug="0"/>
<Context path="/orders" docBase="/home/ian/orders" debug="0"
reloadable="true" crossContext="true">
</Context>
</Host>
<!-- This Host is the first "Virtual Host": www.example.com -->
<Host name="www.example.com" appBase="/home/example/webapp">
<Context path="" docBase="."/>
</Host>
</Engine>
</Service>
</Server>
Tomcat的server.xml文件,在初始状态下,只包括一个虚拟主机,但是它容易被扩充到支持多个虚拟主机。在前面的例子中展示的是一个简单的server.xml版本,其中粗体部分就是用于添加一个虚拟主机。每一个Host元素必须包括一个或多个context元素,所包含的context元素中必须有一个是默认的context,这个默认的context的显示路径应该为空(例如,path=””)。
5.配置基础验证(Basic Authentication)
容器管理验证方法控制着当用户访问受保护的web应用资源时,如何进行用户的身份鉴别。当一个web应用使用了Basic Authentication(BASIC参数在web.xml文件中auto-method元素中设置),而有用户访问受保护的web应用时,Tomcat将通过HTTP Basic Authentication方式,弹出一个对话框,要求用户输入用户名和密码。在这种验证方法中,所有密码将被以64位的编码方式在网络上传输。
注意:使用Basic Authentication通过被认为是不安全的,因为它没有强健的加密方法,除非在客户端和服务器端都使用HTTPS或者其他密码加密码方式(比如,在一个虚拟私人网络中)。若没有额外的加密方法,网络管理员将能够截获(或滥用)用户的密码。但是,如果你是刚开始使用Tomcat,或者你想在你的web应用中测试一下基于容器的安全管理,Basic Authentication还是非常易于设置和使用的。只需要添加<security-constraint>和<login-config>两个元素到你的web应用的web.xml文件中,并且在CATALINA_BASE/conf/tomcat-users.xml 文件中添加适当的<role>和<user>即可,然后重新启动Tomcat。
下面例子中的web.xml摘自一个俱乐部会员网站系统,该系统中只有member目录被保护起来,并使用Basic Authentication进行身份验证。请注意,这种方式将有效的代替Apache web服务器中的.htaccess文件。
<!--
Define the Members-only area, by defining
a "Security Constraint" on this Application, and
mapping it to the subdirectory (URL) that we want
to restrict.
-->
<security-constraint>
<web-resource-collection>
<web-resource-name>
Entire Application
</web-resource-name>
<url-pattern>/members/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>member</role-name>
</auth-constraint>
</security-constraint>
<!-- Define the Login Configuration for this Application -->
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>My Club Members-only Area</realm-name>
</login-config>
6.配置单点登录(Single Sign-On)
一旦你设置了realm和验证的方法,你就需要进行实际的用户登录处理。一般说来,对用户而言登录系统是一件很麻烦的事情,你必须尽量减少用户登录验证的次数。作为缺省的情况,当用户第一次请求受保护的资源时,每一个web应用都会要求用户登录。如果你运行了多个web应用,并且每个应用都需要进行单独的用户验证,那这看起来就有点像你在与你的用户搏斗。用户们不知道怎样才能把多个分离的应用整合成一个单独的系统,所有他们也就不知道他们需要访问多少个不同的应用,只是很迷惑,为什么总要不停的登录。
Tomcat 4的“single sign-on”特性允许用户在访问同一虚拟主机下所有web应用时,只需登录一次。为了使用这个功能,你只需要在Host上添加一个SingleSignOn Valve元素即可,如下所示:
<Valve className="org.apache.catalina.authenticator.SingleSignOn"
debug="0"/>
在Tomcat初始安装后,server.xml的注释里面包括SingleSignOn Valve配置的例子,你只需要去掉注释,即可使用。那么,任何用户只要登录过一个应用,则对于同一虚拟主机下的所有应用同样有效。
使用single sign-on valve有一些重要的限制:
1> value必须被配置和嵌套在相同的Host元素里,并且所有需要进行单点验证的web应用(必须通过context元素定义)都位于该Host下。
2> 包括共享用户信息的realm必须被设置在同一级Host中或者嵌套之外。
3> 不能被context中的realm覆盖。
4> 使用单点登录的web应用最好使用一个Tomcat的内置的验证方式(被定义在web.xml中的<auth-method>中),这比自定义的验证方式强,Tomcat内置的的验证方式包括basic、digest、form和client-cert。
5> 如果你使用单点登录,还希望集成一个第三方的web应用到你的网站中来,并且这个新的web应用使用它自己的验证方式,而不使用容器管理安全,那你基本上就没招了。你的用户每次登录原来所有应用时需要登录一次,并且在请求新的第三方应用时还得再登录一次。当然,如果你拥有这个第三方web应用的源码,而你又是一个程序员,你可以修改它,但那恐怕也不容易做。
6> 单点登录需要使用cookies。
7.配置用户定制目录(Customized User Directores)
一些站点允许个别用户在服务器上发布网页。例如,一所大学的学院可能想给每一位学生一个公共区域,或者是一个ISP希望给一些web空间给他的客户,但这又不是虚拟主机。在这种情况下,一个典型的方法就是在用户名前面加一个特殊字符(~),作为每位用户的网站,比如:
http://www.cs.myuniversity.edu/~username
http://members.mybigisp.com/~username
Tomcat提供两种方法在主机上映射这些个人网站,主要使用一对特殊的Listener元素。Listener的className属性应该是org.apache.catalina.startup.UserConfig,userClass属性应该是几个映射类之一。如果你的系统是Unix,它将有一个标准的/etc/passwd文件,该文件中的帐号能够被运行中的Tomcat很容易的读取,该文件指定了用户的主目录,使用PasswdUserDatabase 映射类。
<Listener className="org.apache.catalina.startup.UserConfig"
directoryName="public_html"
userClass="org.apache.catalina.startup.PasswdUserDatabase"/>
web文件需要放置在像/home/users/ian/public_html 或者 /users/jbrittain/public_html一样的目录下面。当然你也可以改变public_html 到其他任何子目录下。
实际上,这个用户目录根本不一定需要位于用户主目录下里面。如果你没有一个密码文件,但你又想把一个用户名映射到公共的像/home一样目录的子目录里面,则可以使用HomesUserDatabase类。
<Listener className="org.apache.catalina.startup.UserConfig"
directoryName="public_html" homeBase="/home"
userClass="org.apache.catalina.startup.HomesUserDatabase"/>
这样一来,web文件就可以位于像/home/ian/public_html 或者 /home/jasonb/public_html一样的目录下。这种形式对Windows而言更加有利,你可以使用一个像c:home这样的目录。
这些Listener元素,如果出现,则必须在Host元素里面,而不能在context元素里面,因为它们都用应用于Host本身。
8.在Tomcat中使用CGI脚本
Tomcat主要是作为Servlet/JSP容器,但它也有许多传统web服务器的性能。支持通用网关接口(Common Gateway Interface,即CGI)就是其中之一,CGI提供一组方法在响应浏览器请求时运行一些扩展程序。CGI之所以被称为通用,是因为它能在大多数程序或脚本中被调用,包括:Perl,Python,awk,Unix shell scripting等,甚至包括Java。当然,你大概不会把一个Java应用程序当作CGI来运行,毕竟这样太过原始。一般而言,开发Servlet总要比CGI具有更好的效率,因为当用户点击一个链接或一个按钮时,你不需要从操作系统层开始进行处理。
Tomcat包括一个可选的CGI Servlet,允许你运行遗留下来的CGI脚本。
为了使Tomcat能够运行CGI,你必须做如下几件事:
1. 把servlets-cgi.renametojar (在CATALINA_HOME/server/lib/目录下)改名为servlets-cgi.jar。处理CGI的servlet应该位于Tomcat的CLASSPATH下。
2. 在Tomcat的CATALINA_BASE/conf/web.xml 文件中,把关于<servlet-name> CGI的那段的注释去掉(默认情况下,该段位于第241行)。
3. 同样,在Tomcat的CATALINA_BASE/conf/web.xml文件中,把关于对CGI进行映射的那段的注释去掉(默认情况下,该段位于第299行)。注意,这段内容指定了HTML链接到CGI脚本的访问方式。
4. 你可以把CGI脚本放置在WEB-INF/cgi 目录下(注意,WEB-INF是一个安全的地方,你可以把一些不想被用户看见或基于安全考虑不想暴露的文件放在此处),或者你也可以把CGI脚本放置在context下的其他目录下,并为CGI Servlet调整cgiPathPrefix初始化参数。这就指定的CGI Servlet的实际位置,且不能与上一步指定的URL重名。
5. 重新启动Tomcat,你的CGI就可以运行了。
在Tomcat中,CGI程序缺省放置在WEB-INF/cgi目录下,正如前面所提示的那样,WEB-INF目录受保护的,通过客户端的浏览器无法窥探到其中内容,所以对于放置含有密码或其他敏感信息的CGI脚本而言,这是一个非常好的地方。为了兼容其他服务器,尽管你也可以把CGI脚本保存在传统的/cgi-bin目录,但要知道,在这些目录中的文件有可能被网上好奇的冲浪者看到。另外,在Unix中,请确定运行Tomcat的用户有执行CGI脚本的权限。
9.改变Tomcat中的JSP编译器(JSP Compiler)
在Tomcat 4.1(或更高版本,大概),JSP的编译由包含在Tomcat里面的Ant程序控制器直接执行。这听起来有一点点奇怪,但这正是Ant有意为之的一部分,有一个API文档指导开发者在没有启动一个新的JVM的情况下,使用Ant。这是使用Ant进行Java开发的一大优势。另外,这也意味着你现在能够在Ant中使用任何javac支持的编译方式,这里有一个关于Apache Ant使用手册的javac page列表。使用起来是容易的,因为你只需要在<init-param> 元素中定义一个名字叫“compiler”,并且在value中有一个支持编译的编译器名字,示例如下:
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>
org.apache.jasper.servlet.JspServlet
</servlet-class>
<init-param>
<param-name>logVerbosityLevel</param-name>
<param-value>WARNING</param-value>
</init-param>
<init-param>
<param-name>compiler</param-name>
<param-value>jikes</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
当然,给出的编译器必须已经安装在你的系统中,并且CLASSPATH可能需要设置,那处决于你选择的是何种编译器。
10.限制特定主机访问(Restricting Access to Specific Hosts)
有时,你可能想限制对Tomcat web应用的访问,比如,你希望只有你指定的主机或IP地址可以访问你的应用。这样一来,就只有那些指定的的客户端可以访问服务的内容了。为了实现这种效果,Tomcat提供了两个参数供你配置:RemoteHostValve 和RemoteAddrValve。
通过配置这两个参数,可以让你过滤来自请求的主机或IP地址,并允许或拒绝哪些主机/IP。与之类似的,在Apache的httpd文件里有对每个目录的允许/拒绝指定。
例如你可以把Admin Web application设置成只允许本地访问,设置如下:
<Context path="/path/to/secret_files" ...>
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127.0.0.1" deny=""/>
</Context>
如果没有给出允许主机的指定,那么与拒绝主机匹配的主机就会被拒绝,除此之外的都是允许的。与之类似,如果没有给出拒绝主机的指定,那么与允许主机匹配的主机就会被允许,除此之外的都是拒绝的。
你所需要做的就是:按照你的需求配置Tomcat,只要你正确配置,Tomcat一般都能适合你的要求。下面是一系列关于Tomcat的配置技巧,这些技巧源自于我的书:《Tomcat权威指南》,希望对你有所帮助。?? Jason Brittain
1. 配置系统管理(Admin Web Application)
大多数商业化的J2EE服务器都提供一个功能强大的管理界面,且大都采用易于理解的Web应用界面。Tomcat按照自己的方式,同样提供一个成熟的管理工具,并且丝毫不逊于那些商业化的竞争对手。Tomcat的Admin Web Application最初在4.1版本时出现,当时的功能包括管理context、data source、user和group等。当然也可以管理像初始化参数,user、group、role的多种数据库管理等。在后续的版本中,这些功能将得到很大的扩展,但现有的功能已经非常实用了。
Admin Web Application被定义在自动部署文件:CATALINA_BASE/webapps/admin.xml 。
译者注:CATALINA_BASE即tomcat安装目录下的server目录
你必须编辑这个文件,以确定Context中的docBase参数是绝对路径。也就是说,CATALINA_BASE/webapps/admin.xml 的路径是绝对路径。作为另外一种选择,你也可以删除这个自动部署文件,而在server.xml文件中建立一个Admin Web Application的context,效果是一样的。你不能管理Admin Web Application这个应用,换而言之,除了删除CATALINA_BASE/webapps/admin.xml ,你可能什么都做不了。
如果你使用UserDatabaseRealm(默认),你将需要添加一个user以及一个role到CATALINA_BASE/conf/tomcat-users.xml 文件中。你编辑这个文件,添加一个名叫“admin”的role 到该文件中,如下:
<role name="admin"/>
你同样需要有一个用户,并且这个用户的角色是“admin”。象存在的用户那样,添加一个用户(改变密码使其更加安全):
<user name="admin" password="deep_dark_secret" roles="admin"/>
当你完成这些步骤后,请重新启动Tomcat,访问http://localhost:8080/admin,你将看到一个登录界面。Admin Web Application采用基于容器管理的安全机制,并采用了Jakarta Struts框架。一旦你作为“admin”角色的用户登录管理界面,你将能够使用这个管理界面配置Tomcat。
2.配置应用管理(Manager Web Application)
Manager Web Application让你通过一个比Admin Web Application更为简单的用户界面,执行一些简单的Web应用任务。
Manager Web Application被被定义在一个自动部署文件中:
CATALINA_BASE/webapps/manager.xml 。
你必须编辑这个文件,以确保context的docBase参数是绝对路径,也就是说CATALINA_HOME/server/webapps/manager的绝对路径。
(译者注:CATALINA_HOME即tomcat安装目录)
如果你使用的是UserDatabaseRealm,那么你需要添加一个角色和一个用户到CATALINA_BASE/conf/tomcat-users.xml文件中。接下来,编辑这个文件,添加一个名为“manager”的角色到该文件中:
<role name=”manager”>
你同样需要有一个角色为“manager”的用户。像已经存在的用户那样,添加一个新用户(改变密码使其更加安全):
<user name="manager" password="deep_dark_secret" roles="manager"/>
然后重新启动Tomcat,访问http://localhost/manager/list,将看到一个很朴素的文本型管理界面,或者访问http://localhost/manager/html/list,将看到一个HMTL的管理界面。不管是哪种方式都说明你的Manager Web Application现在已经启动了。
Manager application让你可以在没有系统管理特权的基础上,安装新的Web应用,以用于测试。如果我们有一个新的web应用位于/home/user/hello下在,并且想把它安装到 /hello下,为了测试这个应用,我们可以这么做,在第一个文件框中输入“/hello”(作为访问时的path),在第二个文本框中输入“file:/home/user/hello”(作为Config URL)。
Manager application还允许你停止、重新启动、移除以及重新部署一个web应用。停止一个应用使其无法被访问,当有用户尝试访问这个被停止的应用时,将看到一个503的错误??“503 - This application is not currently available”。
移除一个web应用,只是指从Tomcat的运行拷贝中删除了该应用,如果你重新启动Tomcat,被删除的应用将再次出现(也就是说,移除并不是指从硬盘上删除)。
3.部署一个web应用
有两个办法可以在系统中部署web服务。
1> 拷贝你的WAR文件或者你的web应用文件夹(包括该web的所有内容)到$CATALINA_BASE/webapps目录下。
2> 为你的web服务建立一个只包括context内容的XML片断文件,并把该文件放到$CATALINA_BASE/webapps目录下。这个web应用本身可以存储在硬盘上的任何地方。
如果你有一个WAR文件,你若想部署它,则只需要把该文件简单的拷贝到CATALINA_BASE/webapps目录下即可,文件必须以“.war”作为扩展名。一旦Tomcat监听到这个文件,它将(缺省的)解开该文件包作为一个子目录,并以WAR文件的文件名作为子目录的名字。接下来,Tomcat将在内存中建立一个context,就好象你在server.xml文件里建立一样。当然,其他必需的内容,将从server.xml中的DefaultContext获得。
部署web应用的另一种方式是写一个Context XML片断文件,然后把该文件拷贝到CATALINA_BASE/webapps目录下。一个Context片断并非一个完整的XML文件,而只是一个context元素,以及对该应用的相应描述。这种片断文件就像是从server.xml中切取出来的context元素一样,所以这种片断被命名为“context片断”。
举个例子,如果我们想部署一个名叫MyWebApp.war的应用,该应用使用realm作为访问控制方式,我们可以使用下面这个片断:
<!--
Context fragment for deploying MyWebApp.war
-->
<Context path="/demo" docBase="webapps/MyWebApp.war"
debug="0" privileged="true">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Context>
把该片断命名为“MyWebApp.xml”,然后拷贝到CATALINA_BASE/webapps目录下。
这种context片断提供了一种便利的方法来部署web应用,你不需要编辑server.xml,除非你想改变缺省的部署特性,安装一个新的web应用时不需要重启动Tomcat。
4.配置虚拟主机(Virtual Hosts)
关于server.xml中“Host”这个元素,只有在你设置虚拟主机的才需要修改。虚拟主机是一种在一个web服务器上服务多个域名的机制,对每个域名而言,都好象独享了整个主机。实际上,大多数的小型商务网站都是采用虚拟主机实现的,这主要是因为虚拟主机能直接连接到Internet并提供相应的带宽,以保障合理的访问响应速度,另外虚拟主机还能提供一个稳定的固定IP。
基于名字的虚拟主机可以被建立在任何web服务器上,建立的方法就是通过在域名服务器(DNS)上建立IP地址的别名,并且告诉web服务器把去往不同域名的请求分发到相应的网页目录。因为这篇文章主要是讲Tomcat,我们不准备介绍在各种操作系统上设置DNS的方法,如果你在这方面需要帮助,请参考《DNS and Bind》一书,作者是Paul Albitz and Cricket Liu (OReilly)。为了示范方便,我将使用一个静态的主机文件,因为这是测试别名最简单的方法。
在Tomcat中使用虚拟主机,你需要设置DNS或主机数据。为了测试,为本地IP设置一个IP别名就足够了,接下来,你需要在server.xml中添加几行内容,如下:
<Server port="8005" shutdown="SHUTDOWN" debug="0">
<Service name="Tomcat-Standalone">
<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8080" minProcessors="5" maxProcessors="75"
enableLookups="true" redirectPort="8443"/>
<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8443" minProcessors="5" maxProcessors="75"
acceptCount="10" debug="0" scheme="https" secure="true"/>
<Factory className="org.apache.coyote.tomcat4.CoyoteServerSocketFactory"
clientAuth="false" protocol="TLS" />
</Connector>
<Engine name="Standalone" defaultHost="localhost" debug="0">
<!-- This Host is the default Host -->
<Host name="localhost" debug="0" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="ROOT" debug="0"/>
<Context path="/orders" docBase="/home/ian/orders" debug="0"
reloadable="true" crossContext="true">
</Context>
</Host>
<!-- This Host is the first "Virtual Host": www.example.com -->
<Host name="www.example.com" appBase="/home/example/webapp">
<Context path="" docBase="."/>
</Host>
</Engine>
</Service>
</Server>
Tomcat的server.xml文件,在初始状态下,只包括一个虚拟主机,但是它容易被扩充到支持多个虚拟主机。在前面的例子中展示的是一个简单的server.xml版本,其中粗体部分就是用于添加一个虚拟主机。每一个Host元素必须包括一个或多个context元素,所包含的context元素中必须有一个是默认的context,这个默认的context的显示路径应该为空(例如,path=””)。
5.配置基础验证(Basic Authentication)
容器管理验证方法控制着当用户访问受保护的web应用资源时,如何进行用户的身份鉴别。当一个web应用使用了Basic Authentication(BASIC参数在web.xml文件中auto-method元素中设置),而有用户访问受保护的web应用时,Tomcat将通过HTTP Basic Authentication方式,弹出一个对话框,要求用户输入用户名和密码。在这种验证方法中,所有密码将被以64位的编码方式在网络上传输。
注意:使用Basic Authentication通过被认为是不安全的,因为它没有强健的加密方法,除非在客户端和服务器端都使用HTTPS或者其他密码加密码方式(比如,在一个虚拟私人网络中)。若没有额外的加密方法,网络管理员将能够截获(或滥用)用户的密码。但是,如果你是刚开始使用Tomcat,或者你想在你的web应用中测试一下基于容器的安全管理,Basic Authentication还是非常易于设置和使用的。只需要添加<security-constraint>和<login-config>两个元素到你的web应用的web.xml文件中,并且在CATALINA_BASE/conf/tomcat-users.xml 文件中添加适当的<role>和<user>即可,然后重新启动Tomcat。
下面例子中的web.xml摘自一个俱乐部会员网站系统,该系统中只有member目录被保护起来,并使用Basic Authentication进行身份验证。请注意,这种方式将有效的代替Apache web服务器中的.htaccess文件。
<!--
Define the Members-only area, by defining
a "Security Constraint" on this Application, and
mapping it to the subdirectory (URL) that we want
to restrict.
-->
<security-constraint>
<web-resource-collection>
<web-resource-name>
Entire Application
</web-resource-name>
<url-pattern>/members/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>member</role-name>
</auth-constraint>
</security-constraint>
<!-- Define the Login Configuration for this Application -->
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>My Club Members-only Area</realm-name>
</login-config>
6.配置单点登录(Single Sign-On)
一旦你设置了realm和验证的方法,你就需要进行实际的用户登录处理。一般说来,对用户而言登录系统是一件很麻烦的事情,你必须尽量减少用户登录验证的次数。作为缺省的情况,当用户第一次请求受保护的资源时,每一个web应用都会要求用户登录。如果你运行了多个web应用,并且每个应用都需要进行单独的用户验证,那这看起来就有点像你在与你的用户搏斗。用户们不知道怎样才能把多个分离的应用整合成一个单独的系统,所有他们也就不知道他们需要访问多少个不同的应用,只是很迷惑,为什么总要不停的登录。
Tomcat 4的“single sign-on”特性允许用户在访问同一虚拟主机下所有web应用时,只需登录一次。为了使用这个功能,你只需要在Host上添加一个SingleSignOn Valve元素即可,如下所示:
<Valve className="org.apache.catalina.authenticator.SingleSignOn"
debug="0"/>
在Tomcat初始安装后,server.xml的注释里面包括SingleSignOn Valve配置的例子,你只需要去掉注释,即可使用。那么,任何用户只要登录过一个应用,则对于同一虚拟主机下的所有应用同样有效。
使用single sign-on valve有一些重要的限制:
1> value必须被配置和嵌套在相同的Host元素里,并且所有需要进行单点验证的web应用(必须通过context元素定义)都位于该Host下。
2> 包括共享用户信息的realm必须被设置在同一级Host中或者嵌套之外。
3> 不能被context中的realm覆盖。
4> 使用单点登录的web应用最好使用一个Tomcat的内置的验证方式(被定义在web.xml中的<auth-method>中),这比自定义的验证方式强,Tomcat内置的的验证方式包括basic、digest、form和client-cert。
5> 如果你使用单点登录,还希望集成一个第三方的web应用到你的网站中来,并且这个新的web应用使用它自己的验证方式,而不使用容器管理安全,那你基本上就没招了。你的用户每次登录原来所有应用时需要登录一次,并且在请求新的第三方应用时还得再登录一次。当然,如果你拥有这个第三方web应用的源码,而你又是一个程序员,你可以修改它,但那恐怕也不容易做。
6> 单点登录需要使用cookies。
7.配置用户定制目录(Customized User Directores)
一些站点允许个别用户在服务器上发布网页。例如,一所大学的学院可能想给每一位学生一个公共区域,或者是一个ISP希望给一些web空间给他的客户,但这又不是虚拟主机。在这种情况下,一个典型的方法就是在用户名前面加一个特殊字符(~),作为每位用户的网站,比如:
http://www.cs.myuniversity.edu/~username
http://members.mybigisp.com/~username
Tomcat提供两种方法在主机上映射这些个人网站,主要使用一对特殊的Listener元素。Listener的className属性应该是org.apache.catalina.startup.UserConfig,userClass属性应该是几个映射类之一。如果你的系统是Unix,它将有一个标准的/etc/passwd文件,该文件中的帐号能够被运行中的Tomcat很容易的读取,该文件指定了用户的主目录,使用PasswdUserDatabase 映射类。
<Listener className="org.apache.catalina.startup.UserConfig"
directoryName="public_html"
userClass="org.apache.catalina.startup.PasswdUserDatabase"/>
web文件需要放置在像/home/users/ian/public_html 或者 /users/jbrittain/public_html一样的目录下面。当然你也可以改变public_html 到其他任何子目录下。
实际上,这个用户目录根本不一定需要位于用户主目录下里面。如果你没有一个密码文件,但你又想把一个用户名映射到公共的像/home一样目录的子目录里面,则可以使用HomesUserDatabase类。
<Listener className="org.apache.catalina.startup.UserConfig"
directoryName="public_html" homeBase="/home"
userClass="org.apache.catalina.startup.HomesUserDatabase"/>
这样一来,web文件就可以位于像/home/ian/public_html 或者 /home/jasonb/public_html一样的目录下。这种形式对Windows而言更加有利,你可以使用一个像c:home这样的目录。
这些Listener元素,如果出现,则必须在Host元素里面,而不能在context元素里面,因为它们都用应用于Host本身。
8.在Tomcat中使用CGI脚本
Tomcat主要是作为Servlet/JSP容器,但它也有许多传统web服务器的性能。支持通用网关接口(Common Gateway Interface,即CGI)就是其中之一,CGI提供一组方法在响应浏览器请求时运行一些扩展程序。CGI之所以被称为通用,是因为它能在大多数程序或脚本中被调用,包括:Perl,Python,awk,Unix shell scripting等,甚至包括Java。当然,你大概不会把一个Java应用程序当作CGI来运行,毕竟这样太过原始。一般而言,开发Servlet总要比CGI具有更好的效率,因为当用户点击一个链接或一个按钮时,你不需要从操作系统层开始进行处理。
Tomcat包括一个可选的CGI Servlet,允许你运行遗留下来的CGI脚本。
为了使Tomcat能够运行CGI,你必须做如下几件事:
1. 把servlets-cgi.renametojar (在CATALINA_HOME/server/lib/目录下)改名为servlets-cgi.jar。处理CGI的servlet应该位于Tomcat的CLASSPATH下。
2. 在Tomcat的CATALINA_BASE/conf/web.xml 文件中,把关于<servlet-name> CGI的那段的注释去掉(默认情况下,该段位于第241行)。
3. 同样,在Tomcat的CATALINA_BASE/conf/web.xml文件中,把关于对CGI进行映射的那段的注释去掉(默认情况下,该段位于第299行)。注意,这段内容指定了HTML链接到CGI脚本的访问方式。
4. 你可以把CGI脚本放置在WEB-INF/cgi 目录下(注意,WEB-INF是一个安全的地方,你可以把一些不想被用户看见或基于安全考虑不想暴露的文件放在此处),或者你也可以把CGI脚本放置在context下的其他目录下,并为CGI Servlet调整cgiPathPrefix初始化参数。这就指定的CGI Servlet的实际位置,且不能与上一步指定的URL重名。
5. 重新启动Tomcat,你的CGI就可以运行了。
在Tomcat中,CGI程序缺省放置在WEB-INF/cgi目录下,正如前面所提示的那样,WEB-INF目录受保护的,通过客户端的浏览器无法窥探到其中内容,所以对于放置含有密码或其他敏感信息的CGI脚本而言,这是一个非常好的地方。为了兼容其他服务器,尽管你也可以把CGI脚本保存在传统的/cgi-bin目录,但要知道,在这些目录中的文件有可能被网上好奇的冲浪者看到。另外,在Unix中,请确定运行Tomcat的用户有执行CGI脚本的权限。
9.改变Tomcat中的JSP编译器(JSP Compiler)
在Tomcat 4.1(或更高版本,大概),JSP的编译由包含在Tomcat里面的Ant程序控制器直接执行。这听起来有一点点奇怪,但这正是Ant有意为之的一部分,有一个API文档指导开发者在没有启动一个新的JVM的情况下,使用Ant。这是使用Ant进行Java开发的一大优势。另外,这也意味着你现在能够在Ant中使用任何javac支持的编译方式,这里有一个关于Apache Ant使用手册的javac page列表。使用起来是容易的,因为你只需要在<init-param> 元素中定义一个名字叫“compiler”,并且在value中有一个支持编译的编译器名字,示例如下:
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>
org.apache.jasper.servlet.JspServlet
</servlet-class>
<init-param>
<param-name>logVerbosityLevel</param-name>
<param-value>WARNING</param-value>
</init-param>
<init-param>
<param-name>compiler</param-name>
<param-value>jikes</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
当然,给出的编译器必须已经安装在你的系统中,并且CLASSPATH可能需要设置,那处决于你选择的是何种编译器。
10.限制特定主机访问(Restricting Access to Specific Hosts)
有时,你可能想限制对Tomcat web应用的访问,比如,你希望只有你指定的主机或IP地址可以访问你的应用。这样一来,就只有那些指定的的客户端可以访问服务的内容了。为了实现这种效果,Tomcat提供了两个参数供你配置:RemoteHostValve 和RemoteAddrValve。
通过配置这两个参数,可以让你过滤来自请求的主机或IP地址,并允许或拒绝哪些主机/IP。与之类似的,在Apache的httpd文件里有对每个目录的允许/拒绝指定。
例如你可以把Admin Web application设置成只允许本地访问,设置如下:
<Context path="/path/to/secret_files" ...>
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127.0.0.1" deny=""/>
</Context>
如果没有给出允许主机的指定,那么与拒绝主机匹配的主机就会被拒绝,除此之外的都是允许的。与之类似,如果没有给出拒绝主机的指定,那么与允许主机匹配的主机就会被允许,除此之外的都是拒绝的。
Hibernate学习笔记(例)
对hibernate的学习
1。
hibernate的学习,前一阵子做过一个hibernate的小例子,后来由于公司项目需要,去做其他事去了,没有把hibernate再进行研究,现在有些空继续把这知识研究一下。目的是想看一下如果把数据库对象化。
找了几个小的例子,一步一不操作。开始我就下载了一个hibernate的包(在www.hibernate.org网站上有)解开包后仔细查找了一下,发现国外人写的东西就是规范,里面有一个英文的hibernate_reference.pdf,里面是很详细的hibernate如何使用。
其中要做几件基本操作:
1。hibernate.cfg.xml
"-//Hibernate/Hibernate Configuration DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
root
jdbc:mysql://localhost:3306/test
net.sf.hibernate.dialect.MySQLDialect
com.mysql.jdbc.Driver
2。在数据库中建立好一个表(就是用来和类对应的表)
比如建立:
CREATE TABLE `person` (
`id` bigint(5) NOT NULL auto_increment,
`name` varchar(20) default NULL,
`address` varchar(20) default NULL,
`school_id` bigint(5) NOT NULL default '0',
PRIMARY KEY (`id`)
) TYPE=InnoDB AUTO_INCREMENT=13 ;
3。建立类和***.hbm.xml文件
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/test
root
org.hibernate.dialect.MySQLDialect
4。书写测试代码检验(项目中利用)
public class Test {
public static void main(String[] args) throws HibernateException {
String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
Configuration cfg = new Configuration().configure(CONFIG_FILE_LOCATION);
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Person person = new Person();
person.setId(Long.valueOf("1"));
person.setName("lichengdongdong");
person.setAddress("zhongguo");
person.setSchoolId(Long.valueOf("1"));
session.save(person);
tx.commit();
session.close();
System.out.println("d");
}
5.其实还可以做一个通用的util对session进行生成和关闭:
package util;
import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
public static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration().configure()
.buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
// Open a new Session, if this thread has none yet
if (s == null) {
s = sessionFactory.openSession();
// Store it in the ThreadLocal variable
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
if (s != null)
s.close();
session.set(null);
}
}
和传统数据库查询的转化问题
对于数据库的操作,在传统的关系数据库的设计和使用上分析,主要分为,
a.“查询--列表”,select 或者 多表联系,和where语句的搭配
b.“插入”,insert
c.“修改”,update 和 where 语句的搭配
d.“删除”,delete 和 where 语句的搭配
e.“表与表之间的关系,1对多,1对1,多对多”
f.“事务处理”,如何让多个数据一起执行
a.“查询--列表”,select 或者 多表联系,和where语句的搭配
传统的查询一条记录,“select * from *** 后面是where 语句”
单表查询:sql语句: select table where field='';
hibernate:开始已经建立了表的持久化连接。
方法有1:hibernate提供的接口:
1。根据ID查询
要用到Session接口的load方法。
load(Class theClass, Serializable id)
load(Class theClass, Serializable id, LockMode lockMode)
load(Object object, Serializable id)
2。HQL语句进行查询
2。1 利用Query接口,Query由Session里的createQuery()来产生一个查询
1)不带参数的查询(这类比较简单)
Query query=session.createQuery("select user from User as user");
select user.name from User as user
中 form后面的User是那个类名,大小写区分 ,后面的as user 是要把他定义到那个变量中去,前面的user.name恰好是后面的user变量
还有,如果select的是 user.name ,那查询出来的对象的的格式就和user.name这个name的本来属性一样,如select user 那么查询出来结果就的User,select user.name 就是String
如果 select pp.address,pp.name 那么查询出来的就是一个Object[] ,用Object[0]这样进行读取
2)带参数的查询 ()
Query query=session.createQuery("select user from User as user where user.name=?");
query.setString(0,name)//假设name为传过来的参数
Query query=session.createQuery("select user from User as user where user.name=:name");
query.setString("name",name)//假设name为传过来的参数
(多个参数以此类推)
在hql中,参数的设置可以有 =?或 =:name
利用Session接口的find查询
find(String query)
find(String query, Object[] values, Type[] types)
find(String query, Object value, Type type) 均返回list
如:
List list=session.find("select user from Users as user where user.name=?",name,Hibernate.STRING)
List list=session.find("select user from Users as user where user.name=? and user.pw=?",new Object[]{name,pw},new Type[]{Hibernate.STRING,Hibernate.STRING})
{推荐使用Query的方法进行查询}
我们先来比较一下传统的查询处理:
相关工具
1。可以用myeclipse来处理数据库连接部分。
在配置数据库属性的时候, mysql部分写法:
URL: [jdbc:mysql://localhost:3306]
driverClass:com.mysql.jdbc.Driver
对hibernate的本人的怀疑。
1。hibernate机制利MAPPING等技术把数据库对象化了,在性能上时候降低了,对于大型项目比如大型电子商务网站是否可以利用。
在网上查看了一下,hibernate的评论都不错的,主要还是在数据库的设计上下工夫,毕竟hibernate只是一个mapping工具,有网友说经过数据库和hibernate的配置优化:“我也用 Struts+Hibernate 做大型项目,在并发很高时,每天4500人次访问量的情况下,性能也相当不错. ” 说明hibernate是不错的。相关文章见:《Hibernate的性能之大家谈》 http://www.javaresearch.org/article/showarticle.jsp?column=108&thread=20127
问题
1。在hibernate的操作的时候老是出现log4j的错误。
其实不是错误,是警告而已,对程序的执行是没有影响的。主要是打印出程序执行过程的信息,只要在你程序类的 外放上log4j.properties 这个文件就可以了(需要事先引入 log4j.jar文件到项目)
1。
hibernate的学习,前一阵子做过一个hibernate的小例子,后来由于公司项目需要,去做其他事去了,没有把hibernate再进行研究,现在有些空继续把这知识研究一下。目的是想看一下如果把数据库对象化。
找了几个小的例子,一步一不操作。开始我就下载了一个hibernate的包(在www.hibernate.org网站上有)解开包后仔细查找了一下,发现国外人写的东西就是规范,里面有一个英文的hibernate_reference.pdf,里面是很详细的hibernate如何使用。
其中要做几件基本操作:
1。hibernate.cfg.xml
"-//Hibernate/Hibernate Configuration DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
root
jdbc:mysql://localhost:3306/test
net.sf.hibernate.dialect.MySQLDialect
com.mysql.jdbc.Driver
2。在数据库中建立好一个表(就是用来和类对应的表)
比如建立:
CREATE TABLE `person` (
`id` bigint(5) NOT NULL auto_increment,
`name` varchar(20) default NULL,
`address` varchar(20) default NULL,
`school_id` bigint(5) NOT NULL default '0',
PRIMARY KEY (`id`)
) TYPE=InnoDB AUTO_INCREMENT=13 ;
3。建立类和***.hbm.xml文件
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/test
root
org.hibernate.dialect.MySQLDialect
4。书写测试代码检验(项目中利用)
public class Test {
public static void main(String[] args) throws HibernateException {
String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
Configuration cfg = new Configuration().configure(CONFIG_FILE_LOCATION);
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Person person = new Person();
person.setId(Long.valueOf("1"));
person.setName("lichengdongdong");
person.setAddress("zhongguo");
person.setSchoolId(Long.valueOf("1"));
session.save(person);
tx.commit();
session.close();
System.out.println("d");
}
5.其实还可以做一个通用的util对session进行生成和关闭:
package util;
import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
public static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration().configure()
.buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
// Open a new Session, if this thread has none yet
if (s == null) {
s = sessionFactory.openSession();
// Store it in the ThreadLocal variable
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
if (s != null)
s.close();
session.set(null);
}
}
和传统数据库查询的转化问题
对于数据库的操作,在传统的关系数据库的设计和使用上分析,主要分为,
a.“查询--列表”,select 或者 多表联系,和where语句的搭配
b.“插入”,insert
c.“修改”,update 和 where 语句的搭配
d.“删除”,delete 和 where 语句的搭配
e.“表与表之间的关系,1对多,1对1,多对多”
f.“事务处理”,如何让多个数据一起执行
a.“查询--列表”,select 或者 多表联系,和where语句的搭配
传统的查询一条记录,“select * from *** 后面是where 语句”
单表查询:sql语句: select table where field='';
hibernate:开始已经建立了表的持久化连接。
方法有1:hibernate提供的接口:
1。根据ID查询
要用到Session接口的load方法。
load(Class theClass, Serializable id)
load(Class theClass, Serializable id, LockMode lockMode)
load(Object object, Serializable id)
2。HQL语句进行查询
2。1 利用Query接口,Query由Session里的createQuery()来产生一个查询
1)不带参数的查询(这类比较简单)
Query query=session.createQuery("select user from User as user");
select user.name from User as user
中 form后面的User是那个类名,大小写区分 ,后面的as user 是要把他定义到那个变量中去,前面的user.name恰好是后面的user变量
还有,如果select的是 user.name ,那查询出来的对象的的格式就和user.name这个name的本来属性一样,如select user 那么查询出来结果就的User,select user.name 就是String
如果 select pp.address,pp.name 那么查询出来的就是一个Object[] ,用Object[0]这样进行读取
2)带参数的查询 ()
Query query=session.createQuery("select user from User as user where user.name=?");
query.setString(0,name)//假设name为传过来的参数
Query query=session.createQuery("select user from User as user where user.name=:name");
query.setString("name",name)//假设name为传过来的参数
(多个参数以此类推)
在hql中,参数的设置可以有 =?或 =:name
利用Session接口的find查询
find(String query)
find(String query, Object[] values, Type[] types)
find(String query, Object value, Type type) 均返回list
如:
List list=session.find("select user from Users as user where user.name=?",name,Hibernate.STRING)
List list=session.find("select user from Users as user where user.name=? and user.pw=?",new Object[]{name,pw},new Type[]{Hibernate.STRING,Hibernate.STRING})
{推荐使用Query的方法进行查询}
我们先来比较一下传统的查询处理:
相关工具
1。可以用myeclipse来处理数据库连接部分。
在配置数据库属性的时候, mysql部分写法:
URL: [jdbc:mysql://localhost:3306]
driverClass:com.mysql.jdbc.Driver
对hibernate的本人的怀疑。
1。hibernate机制利MAPPING等技术把数据库对象化了,在性能上时候降低了,对于大型项目比如大型电子商务网站是否可以利用。
在网上查看了一下,hibernate的评论都不错的,主要还是在数据库的设计上下工夫,毕竟hibernate只是一个mapping工具,有网友说经过数据库和hibernate的配置优化:“我也用 Struts+Hibernate 做大型项目,在并发很高时,每天4500人次访问量的情况下,性能也相当不错. ” 说明hibernate是不错的。相关文章见:《Hibernate的性能之大家谈》 http://www.javaresearch.org/article/showarticle.jsp?column=108&thread=20127
问题
1。在hibernate的操作的时候老是出现log4j的错误。
其实不是错误,是警告而已,对程序的执行是没有影响的。主要是打印出程序执行过程的信息,只要在你程序类的 外放上log4j.properties 这个文件就可以了(需要事先引入 log4j.jar文件到项目)
Hibernate实战全解
对象关系映射(Object Relative Mapping)简称ORM,是面向对象开发的一个热点,用来解决JDBC开发中手动进行OR映射的繁杂与不便。EJB中的实体Bean在这个领域是很著名的——既因为它的先进而著名,也因为它的低效而著名。有过实体Bean开发经验的人可能都会为实现远程接口造成的效率低下而头痛,在很多不大不小的项目中,使用实体Bean是否得不偿失,争论很大。一个轻量级的持久化方案也许能够解决一些问题,Hibernate应此而生。
Hibernate是一个中间层,它的目的是把数据库中的关系通过一定的规则映射成为对象,让Java开发人员不用太多的考虑底层数据库的问题,只需要像通常情况下管理对象一样的管理数据。在关系数据库仍将持续占据市场的情况下,它很可观。在数据持久化领域,即便是轻量级的方案也会是复杂饶舌的,也许如同周杰伦的音乐一样不知所云。在学习它之前,最好先回想一下以前进行数据库开发中遇到的问题和不便,想想为什么需要一个持久化层,才能知道很多操作的目的是什么,以及为什么要这么干,在这个问题上我不想做更多的叙述,因为“长久以来……”这样的句式通常long(不好意思,打不出来)长,会对我的键盘和热情造成很大的磨损。如果让我写一本书,那么我会乐意去叙述什么是数据持久化,它有什么好处等等。废话少说,来了。
首先需要配置环境,下载Hibernate 2.1(www.hibernate.org),把lib下的*.jar添加到classpath,你的数据库JDBC驱动程序也应该在classpath中。打开hibernate.properties,针对你使用的数据库,配置相应的信息,比如我使用的是MS SQL Server,配置如下:
## MS SQL Server
hibernate.dialect net.sf.hibernate.dialect.SQLServerDialect
hibernate.connection.driver_class com.microsoft.jdbc.sqlserver.SQLServerDriver
hibernate.connection.url jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=zizz
hibernate.connection.username sa
hibernate.connection.password
其中很大部分是已经写好的,只需要取掉注释即可,我自己只是修改了数据库名称、帐号、密码。建立一个名为zizz的数据库备用。
然后把这个文件拷贝到你的应用的根目录下。
我们谈论了很多次映射,应该首先来看看这个映射是如何完成的。假设一个最简单的应用,写一个功能最单一的留言板,设计的数据有留言的编号、留言者名称、留言内容,还有留言时间。足够简单吧,换做是你打算怎么干?我猜你要首先建立一个数据库表格,名字也许叫做guestbook。No,这不是面向对象的方式,不妨首先从对象的角度来考虑。我们当然希望每一条留言都以对象的方式呈现,每个对象应该具有的属性有:id、author、content、time。偷个懒,没有画UML。下面这个类应该是很容易理解的:
//GuestBook.java
package org.bromon.zizz;
import java.util.*;
public class GuestBook
{
private int id;
private String author;
private String content;
private Calendar time;
private void setId(int id)
{
this.id=id;
}
public int getId()
{
return(id);
}
public void setAuthor(String author)
{
this.author=author;
}
public String getAuthro()
{
return(author);
}
public void setContent(String content)
{
this.content=content;
}
public String getContent()
{
return(content);
}
public void setTime(Calendar time)
{
this.time=time;
}
public Calendar getTime()
{
return(time);
}
}
基本上是最简单的Bean了,如果觉得困难的话,请你先回火星等我。
需要注意的是setId方法被指定为private,这是因为我希望用这个字段做主键,它最好由系统自动生成,所以不应该由用户来指定,这个方法专为Hibernate准备,所以是私有的。
如何把这个类与数据库映射起来?看看Hibernate的魔法,使用一个XML文件来描述,它应该被命名为GuestBook.hbm.xml:
< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
< hibernate-mapping package="org.bromon.zizz" >
< class table=”guestbook" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator / >
< /id >
< property column="author" not-null="true" unique="false"/ >
< property column="content" not-null="true"/ >
< property column="time" not-null="true"/ >
< /class >
< /hibernate-mapping >
虽然有点陌生,但是很易读,仔细琢磨一下。
下面来编写我们的应用,它的功能是插入数据:
//Operate.java
package org.bromon.zizz;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.*;
import java.util.*;
public class Operate
{
public static void main(String args[])
{
try
{
Configuration cfg=new Configuration().addClass(GuestBook.class);
SessionFactory sessions=cfg.buildSessionFactory();
new SchemaExport(cfg).create(true,true);
Session session=sessions.openSession();
GuestBook gb=new GuestBook();
gb.setAuthor(“Bromon”);
gb.setContent(“留言的内容”);
gb.setTime(Calendar.getInstance());
Transaction ts=session.beginTransaction();
session.save(gb);
ts.commit();
session.close();
}catch(Exception e)
{
System.out.println(e);
}
}
}
编译:javac –d . *.java
执行一下:java org.bromon.zizz.Operate
到数据库里面看看,表格已经建立好了,并且数据也已经保存。如果把
new SchemaExport().create(true,true);
注释掉,那么系统不会创建表格,而只是在已有的表格中添加新的记录,当然,如果表格不存在的话,会产生异常。
你已经看到了Hibernate神奇魔法的5%,它足够的复杂强大,可以让你应付复杂的应用。
one-to-one关系
在绝大多数系统当中不可能只存在一个数据表格,否则就违背了关系数据库的初衷。表与表的关系比较复杂,可以分为几种情况:
● 一对一关联(one to one)
● 一对多关联(one to many)
● 多对一关联(many to one)
● 多对多关联(many to many)
按顺序来讲。假设一个一对一关联的例子是:
表格:person
id 编号(主键)
name 姓名
email email地址
表格:spouse
id 编号(外键)
name 姓名
person这个表保存用户信息,而spouse保存用户配偶的信息。在一般情况下一个人只有一个配偶,这很适合我们一对一的情况。如果你对婚外恋感兴趣的话,我们可以在一对多和多对一的关联中讨论这个问题。
OK,下面设计POJO:
Person这个类非常简单:
/*
* Created on 2004-4-19
*/
package org.bromon.zizz;
/**
* @author Bromon
*/
public class Person
{
private int id;
private String name;
private String email;
public void setId(int id)
{
this.id=id;
}
public int getId()
{
return(id);
}
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return(name);
}
public void setEmail(String email)
{
this.email=email;
}
public String getEmail()
{
return(email);
}
}
然后编写它的映射规则,这个应该能够理解了:
< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
< hibernate-mapping package="org.bromon.zizz" >
< class table="person" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator / >
< /id >
< property column="name" not-null="true" unique="false"/ >
< property column="email" not-null="false"/ >
< /class >
< /hibernate-mapping >
so easy是不是?一切都按部就班。下面是Souse类:
/*
* Created on 2004-4-20
*/
package org.bromon.zizz;
/**
* @author Bromon
*/
public class Spouse
{
private int id;
private String name;
private Person person;
public void setId(int id)
{
this.id=id;
}
public int getId()
{
return(id);
}
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return(name);
}
public void setPerson(Person person)
{
this.person=person;
}
public Person getPerson()
{
return(person);
}
}
注意里面的域person。它的映射文件:
< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
< hibernate-mapping package="org.bromon.zizz" >
< class table="spouse" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator >
< param >person< /param >
< /generator >
< /id >
< property column="name" not-null="true" unique="false"/ >
< one-to-one cascade="all" constrained="true" / >
< /class >
< /hibernate-mapping >
这里指明了id的generator是一个外键,和person相关联。然后指定一个one-to-one关系,不难理解是不是?Hibernate的确很符合我们的思维习惯。需要提醒的是,这种关联关系是单向的,Person并不需要去指定Spouse。
下面来操作这两个类:
/*
* Created on 2004-4-20
*/
package org.bromon.zizz;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.*;
/**
* @author Bromon
*/
public class OperateSpouse
{
public static void main(String args[])
{
try
{
Configuration cfg=new Configuration().addClass(Spouse.class);
cfg.addClass(Person.class);
SessionFactory factory=cfg.buildSessionFactory();
new SchemaExport(cfg).create(true,true);
Session session=factory.openSession();
Person person=new Person();
person.setName("bromon");
person.setEmail("bromon@163.com");
Spouse spouse=new Spouse();
spouse.setName("who");
spouse.setPerson(person);
Transaction ts=session.beginTransaction();
session.save(person);
session.save(spouse);
ts.commit();
session.close();
}catch(Exception e)
{
System.out.println(e);
}
}
}
这个例子和第一篇中的例子非常相似。OK,执行一下,然后看看zizz数据库,搞掂。
Many-to-One关系
很明显一对多或者多对一关系是关系数据库中非常常见的现象,下面通过父亲-儿子的例子来演示一对多关系,多对一关系是类似的,不过在我们的这个例子中不宜采用,否则会带来伦理学上的问题。
首先定义Child类:
/*
* Created on 2004-5-8
*/
package org.bromon.zizz;
/**
* @author Bromon
*/
public class Child
{
private int id;
private String name;
private int fatherId;
private Person father;
public Child(){}
/**
* @return
*/
public Person getFather()
{
return father;
}
/**
* @return
*/
public int getFatherId()
{
return fatherId;
}
/**
* @return
*/
public int getId()
{
return id;
}
/**
* @return
*/
public String getName()
{
return name;
}
/**
* @param person
*/
public void setFather(Person p)
{
father = p;
}
/**
* @param i
*/
public void setFatherId(int i)
{
fatherId = i;
}
/**
* @param i
*/
public void setId(int i)
{
}
/**
* @param string
*/
public void setName(String string)
{
}
}
这里的fatherId是外键,关联person表的id字段。
下面是映射文件Child.hbm.xml:
< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
< hibernate-mapping package="org.bromon.zizz" >
< class table="child" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator / >
< /id >
< property column="name" not-null="true" unique="false"/ >
< many-to-one column="fatherid" / >
< /class >
< /hibernate-mapping >
需要注意的是fatherId并没有做为一个property被映射,而是在many-to-one声明中使用。
需要对Person..java做修改,添加以下代码:
import java.util.*;
private Set children=new HashSet();
/**
* @return
*/
public Set getChildren()
{
return children;
}
/**
* @param set
*/
public void setChildren(Set set)
{
children = set;
}
然后修改Person.hbm.xml,对添加的代码做映射:
< set lazy="true" inverse="true" cascade="all" >
< key column="fatherid"/ >
< one-to-many / >
< /set >
这里的key column是child表的外键,inverse需要指定为true。
下面做操作一下,功能是查询person表中id=1的记录,作为小孩的父亲,然后给child表添加一条新记录。
/*
* Created on 2004-5-8
*/
package org.bromon.zizz;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.*;
/**
* @author Bromon
*/
public class OperateChild
{
/**
* @param args
*/
public static void main(String args[])
{
try
{
Configuration cfg = new Configuration().addClass(Person.class);
cfg.addClass(Child.class);
SessionFactory sessions = cfg.buildSessionFactory();
new SchemaExport(cfg).create(true, true);
Session session = sessions.openSession();
Child c=new Child();
/*Query q=session.createQuery("from org.bromon.zizz.Person as p where p.id=1");
Person p=(Person)q.list().get(0);*/
Person p=(Person)session.find("from org.bromon.zizz.Person as p where p.id=?",new Integer(1),Hibernate.INTEGER).get(0);
System.out.println(p.getName());
c.setName("andy");
c.setFather(p);
Transaction ts = session.beginTransaction();
session.save(c);
ts.commit();
session.close();
} catch (Exception e)
{
System.out.println(e);
}
}
}
被注释掉的部分是HQL的另外一种查询方法。在这个例子中可以看出对象的查询非常容易,不需要自己再去封装数据,修改和删除对象也很容易:
//得到一个对象
Query q=session.createQuery("from org.bromon.zizz.Person as p where p.id=1");
Person p=(Person)q.list().get(0);
//修改数据
p.setName(“Mr Smith”);
//保存数据
session.save(p);
session.flush();
//删除数据
session.delete(p);
session.flush();
Hibernate是一个中间层,它的目的是把数据库中的关系通过一定的规则映射成为对象,让Java开发人员不用太多的考虑底层数据库的问题,只需要像通常情况下管理对象一样的管理数据。在关系数据库仍将持续占据市场的情况下,它很可观。在数据持久化领域,即便是轻量级的方案也会是复杂饶舌的,也许如同周杰伦的音乐一样不知所云。在学习它之前,最好先回想一下以前进行数据库开发中遇到的问题和不便,想想为什么需要一个持久化层,才能知道很多操作的目的是什么,以及为什么要这么干,在这个问题上我不想做更多的叙述,因为“长久以来……”这样的句式通常long(不好意思,打不出来)长,会对我的键盘和热情造成很大的磨损。如果让我写一本书,那么我会乐意去叙述什么是数据持久化,它有什么好处等等。废话少说,来了。
首先需要配置环境,下载Hibernate 2.1(www.hibernate.org),把lib下的*.jar添加到classpath,你的数据库JDBC驱动程序也应该在classpath中。打开hibernate.properties,针对你使用的数据库,配置相应的信息,比如我使用的是MS SQL Server,配置如下:
## MS SQL Server
hibernate.dialect net.sf.hibernate.dialect.SQLServerDialect
hibernate.connection.driver_class com.microsoft.jdbc.sqlserver.SQLServerDriver
hibernate.connection.url jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=zizz
hibernate.connection.username sa
hibernate.connection.password
其中很大部分是已经写好的,只需要取掉注释即可,我自己只是修改了数据库名称、帐号、密码。建立一个名为zizz的数据库备用。
然后把这个文件拷贝到你的应用的根目录下。
我们谈论了很多次映射,应该首先来看看这个映射是如何完成的。假设一个最简单的应用,写一个功能最单一的留言板,设计的数据有留言的编号、留言者名称、留言内容,还有留言时间。足够简单吧,换做是你打算怎么干?我猜你要首先建立一个数据库表格,名字也许叫做guestbook。No,这不是面向对象的方式,不妨首先从对象的角度来考虑。我们当然希望每一条留言都以对象的方式呈现,每个对象应该具有的属性有:id、author、content、time。偷个懒,没有画UML。下面这个类应该是很容易理解的:
//GuestBook.java
package org.bromon.zizz;
import java.util.*;
public class GuestBook
{
private int id;
private String author;
private String content;
private Calendar time;
private void setId(int id)
{
this.id=id;
}
public int getId()
{
return(id);
}
public void setAuthor(String author)
{
this.author=author;
}
public String getAuthro()
{
return(author);
}
public void setContent(String content)
{
this.content=content;
}
public String getContent()
{
return(content);
}
public void setTime(Calendar time)
{
this.time=time;
}
public Calendar getTime()
{
return(time);
}
}
基本上是最简单的Bean了,如果觉得困难的话,请你先回火星等我。
需要注意的是setId方法被指定为private,这是因为我希望用这个字段做主键,它最好由系统自动生成,所以不应该由用户来指定,这个方法专为Hibernate准备,所以是私有的。
如何把这个类与数据库映射起来?看看Hibernate的魔法,使用一个XML文件来描述,它应该被命名为GuestBook.hbm.xml:
< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
< hibernate-mapping package="org.bromon.zizz" >
< class table=”guestbook" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator / >
< /id >
< property column="author" not-null="true" unique="false"/ >
< property column="content" not-null="true"/ >
< property column="time" not-null="true"/ >
< /class >
< /hibernate-mapping >
虽然有点陌生,但是很易读,仔细琢磨一下。
下面来编写我们的应用,它的功能是插入数据:
//Operate.java
package org.bromon.zizz;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.*;
import java.util.*;
public class Operate
{
public static void main(String args[])
{
try
{
Configuration cfg=new Configuration().addClass(GuestBook.class);
SessionFactory sessions=cfg.buildSessionFactory();
new SchemaExport(cfg).create(true,true);
Session session=sessions.openSession();
GuestBook gb=new GuestBook();
gb.setAuthor(“Bromon”);
gb.setContent(“留言的内容”);
gb.setTime(Calendar.getInstance());
Transaction ts=session.beginTransaction();
session.save(gb);
ts.commit();
session.close();
}catch(Exception e)
{
System.out.println(e);
}
}
}
编译:javac –d . *.java
执行一下:java org.bromon.zizz.Operate
到数据库里面看看,表格已经建立好了,并且数据也已经保存。如果把
new SchemaExport().create(true,true);
注释掉,那么系统不会创建表格,而只是在已有的表格中添加新的记录,当然,如果表格不存在的话,会产生异常。
你已经看到了Hibernate神奇魔法的5%,它足够的复杂强大,可以让你应付复杂的应用。
one-to-one关系
在绝大多数系统当中不可能只存在一个数据表格,否则就违背了关系数据库的初衷。表与表的关系比较复杂,可以分为几种情况:
● 一对一关联(one to one)
● 一对多关联(one to many)
● 多对一关联(many to one)
● 多对多关联(many to many)
按顺序来讲。假设一个一对一关联的例子是:
表格:person
id 编号(主键)
name 姓名
email email地址
表格:spouse
id 编号(外键)
name 姓名
person这个表保存用户信息,而spouse保存用户配偶的信息。在一般情况下一个人只有一个配偶,这很适合我们一对一的情况。如果你对婚外恋感兴趣的话,我们可以在一对多和多对一的关联中讨论这个问题。
OK,下面设计POJO:
Person这个类非常简单:
/*
* Created on 2004-4-19
*/
package org.bromon.zizz;
/**
* @author Bromon
*/
public class Person
{
private int id;
private String name;
private String email;
public void setId(int id)
{
this.id=id;
}
public int getId()
{
return(id);
}
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return(name);
}
public void setEmail(String email)
{
this.email=email;
}
public String getEmail()
{
return(email);
}
}
然后编写它的映射规则,这个应该能够理解了:
< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
< hibernate-mapping package="org.bromon.zizz" >
< class table="person" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator / >
< /id >
< property column="name" not-null="true" unique="false"/ >
< property column="email" not-null="false"/ >
< /class >
< /hibernate-mapping >
so easy是不是?一切都按部就班。下面是Souse类:
/*
* Created on 2004-4-20
*/
package org.bromon.zizz;
/**
* @author Bromon
*/
public class Spouse
{
private int id;
private String name;
private Person person;
public void setId(int id)
{
this.id=id;
}
public int getId()
{
return(id);
}
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return(name);
}
public void setPerson(Person person)
{
this.person=person;
}
public Person getPerson()
{
return(person);
}
}
注意里面的域person。它的映射文件:
< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
< hibernate-mapping package="org.bromon.zizz" >
< class table="spouse" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator >
< param >person< /param >
< /generator >
< /id >
< property column="name" not-null="true" unique="false"/ >
< one-to-one cascade="all" constrained="true" / >
< /class >
< /hibernate-mapping >
这里指明了id的generator是一个外键,和person相关联。然后指定一个one-to-one关系,不难理解是不是?Hibernate的确很符合我们的思维习惯。需要提醒的是,这种关联关系是单向的,Person并不需要去指定Spouse。
下面来操作这两个类:
/*
* Created on 2004-4-20
*/
package org.bromon.zizz;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.*;
/**
* @author Bromon
*/
public class OperateSpouse
{
public static void main(String args[])
{
try
{
Configuration cfg=new Configuration().addClass(Spouse.class);
cfg.addClass(Person.class);
SessionFactory factory=cfg.buildSessionFactory();
new SchemaExport(cfg).create(true,true);
Session session=factory.openSession();
Person person=new Person();
person.setName("bromon");
person.setEmail("bromon@163.com");
Spouse spouse=new Spouse();
spouse.setName("who");
spouse.setPerson(person);
Transaction ts=session.beginTransaction();
session.save(person);
session.save(spouse);
ts.commit();
session.close();
}catch(Exception e)
{
System.out.println(e);
}
}
}
这个例子和第一篇中的例子非常相似。OK,执行一下,然后看看zizz数据库,搞掂。
Many-to-One关系
很明显一对多或者多对一关系是关系数据库中非常常见的现象,下面通过父亲-儿子的例子来演示一对多关系,多对一关系是类似的,不过在我们的这个例子中不宜采用,否则会带来伦理学上的问题。
首先定义Child类:
/*
* Created on 2004-5-8
*/
package org.bromon.zizz;
/**
* @author Bromon
*/
public class Child
{
private int id;
private String name;
private int fatherId;
private Person father;
public Child(){}
/**
* @return
*/
public Person getFather()
{
return father;
}
/**
* @return
*/
public int getFatherId()
{
return fatherId;
}
/**
* @return
*/
public int getId()
{
return id;
}
/**
* @return
*/
public String getName()
{
return name;
}
/**
* @param person
*/
public void setFather(Person p)
{
father = p;
}
/**
* @param i
*/
public void setFatherId(int i)
{
fatherId = i;
}
/**
* @param i
*/
public void setId(int i)
{
}
/**
* @param string
*/
public void setName(String string)
{
}
}
这里的fatherId是外键,关联person表的id字段。
下面是映射文件Child.hbm.xml:
< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
< hibernate-mapping package="org.bromon.zizz" >
< class table="child" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator / >
< /id >
< property column="name" not-null="true" unique="false"/ >
< many-to-one column="fatherid" / >
< /class >
< /hibernate-mapping >
需要注意的是fatherId并没有做为一个property被映射,而是在many-to-one声明中使用。
需要对Person..java做修改,添加以下代码:
import java.util.*;
private Set children=new HashSet();
/**
* @return
*/
public Set getChildren()
{
return children;
}
/**
* @param set
*/
public void setChildren(Set set)
{
children = set;
}
然后修改Person.hbm.xml,对添加的代码做映射:
< set lazy="true" inverse="true" cascade="all" >
< key column="fatherid"/ >
< one-to-many / >
< /set >
这里的key column是child表的外键,inverse需要指定为true。
下面做操作一下,功能是查询person表中id=1的记录,作为小孩的父亲,然后给child表添加一条新记录。
/*
* Created on 2004-5-8
*/
package org.bromon.zizz;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.*;
/**
* @author Bromon
*/
public class OperateChild
{
/**
* @param args
*/
public static void main(String args[])
{
try
{
Configuration cfg = new Configuration().addClass(Person.class);
cfg.addClass(Child.class);
SessionFactory sessions = cfg.buildSessionFactory();
new SchemaExport(cfg).create(true, true);
Session session = sessions.openSession();
Child c=new Child();
/*Query q=session.createQuery("from org.bromon.zizz.Person as p where p.id=1");
Person p=(Person)q.list().get(0);*/
Person p=(Person)session.find("from org.bromon.zizz.Person as p where p.id=?",new Integer(1),Hibernate.INTEGER).get(0);
System.out.println(p.getName());
c.setName("andy");
c.setFather(p);
Transaction ts = session.beginTransaction();
session.save(c);
ts.commit();
session.close();
} catch (Exception e)
{
System.out.println(e);
}
}
}
被注释掉的部分是HQL的另外一种查询方法。在这个例子中可以看出对象的查询非常容易,不需要自己再去封装数据,修改和删除对象也很容易:
//得到一个对象
Query q=session.createQuery("from org.bromon.zizz.Person as p where p.id=1");
Person p=(Person)q.list().get(0);
//修改数据
p.setName(“Mr Smith”);
//保存数据
session.save(p);
session.flush();
//删除数据
session.delete(p);
session.flush();
快速上手Spring
“好的开始是成功的一半”,但凡一种新的技术,开始阶段一般是很苦恼的。为了不让更多的人走同样的弯路,我这里就把学习Spring的经验与大家分享, 一起快乐一下。
Spring是什么?反正这里我不会谈论春天的。我要说的是Spring Framework ,它又是用来做什么的呢?一会半会我也说不清楚。还好现在是互联网的时代,google一下,会发现不少有用的东西。
先来到Spring的大本营:http://www.springframework.org/ 可以看看 About Spring,最重要的是下载一个
Spring版本。
Spring的下载包有两种,一种是带有“with-dependencies”名称的,这种包含了Spring常用到的第三方包,比如Hibernate、aopalliance、jakarta-commons等。这里就使用了这种包,可以省却查找第三方包的麻烦。
当前版本1.2.1 :spring-framework-1.2.1-with-dependencies.zip
解压这个包,可以在 docs\reference目录下找到Spring 的参考手册,有html和pdf两种格式。如果你的英文不太好,可以看看Spring中文论坛翻译的1.1版本的中文版(pdf格式)。这个参考手册就是入门的不二法宝。
● 新手刚学Spring时,可能对一些概念似懂非懂,比如IoC(这里可不是国际奥委会)、DI等。这里向你推荐几篇好文章,有助于理解Spring。
· Rod Johnson:Introduction to Spring Framework (Spring 1.2版本)
Java视线论坛上翻译:Introducing to Spring Framework(中文修订版 旧版本)
· Robert C Martin, Bob: The Dependency Inversion Principle
透明对此文的翻译: IoC 容器和Dependency Injection 模式
· 冰云: 介绍 IOC
· 夏昕:Spring开发指南
· 良葛格:Spring入门(非常适合新手入门)
· javaboutique网站上有篇教程(入门): The Spring Framework
● 如果要系统的学习Spring,还是需要Book的。
· Rod Johnson:Expert One-on-One J2EE Design and Development(10/2002)
看看作者是谁,就知道这本书的质量如何了。毫无疑问,这本书吹响了向Spring进军的号角。前半部分主要阐述了J2EE开发的方方面面的经验和设计,后半部分通过对J2EE的改善设计而引申出了SpringFramework。这本书的中译本也出来了,不过翻译的一般。
· Rod Johnson, Juergen Hoeller: Expert One-on-One J2EE Development without EJB(7/2004)
看看书名就会吓一跳,不过Rod Johnson完全有资格说出来。前半本介绍EJB的不足,后半本讲Spring的原理。这本书的中译本也出来了。
· Bruce A. Tate, Justin Gehtland:Better, Faster, Lighter Java(5/2004)
这本书主要阐述了如何利用Java进行轻量级开发,有关Spring的内容比较少,只有一章,不过此书获得了Jolt 大奖,质量肯定没的说了。
· Craig Walls, Ryan Breidenbach: Spring in Action(1/2005)
Manning出版的“in action”系列图书,质量一直非常好。此书系统介绍了Spring的方方面面,解析相当清晰。
· Matt Raible: Spring Live
这本是source beat的电子书(只出电子版)。如果要深入这本书,需要有相当的基础。这本书非常适合有实践项目经验的人。采用TDD的方式使用Spring,而且提供Spring整合其他framework的各种作法 (相当多)。同时作者也维护着一个开源的项目appfuse。
· Rob Harrop,Jan Machacek:Pro Spring(1/2005)
我目前只看过了样章,还很不错,很系统,也很细致,尤其书中提供的源代码很适合新手练习。
· Rod Johnson等5人: Professional Java Development with the Spring Framework
预计2005年7月出版,作者阵容挺庞大的,几乎都是Spring开发团队的,值得期待。
· 罗时飞 精通Spring
这好像是目前为止系统介绍Spring的唯一简体中文书籍。
以上书籍都在网上提供了书中的源代码下载和部分样章,如果拿不到书,看看源代码也是很不错。
● 说完书籍,再介绍一些好的论坛。
最后给大家推荐一个不错的资源收集站点 http://www.java201.com/
在其右上方的搜索栏中填入“spring”,可以获得不少整理好的有关spring的资料。
好了,先写到这里了,以后会陆续添加一些好的资料和站点。
快速上手Spring-HelloWorld
这篇文章主要谈谈Spring的入门开发,例子很简单,就是输出一条语句。有关各种软件的配置这里就不在多说了,如有不明白的,可以参考我以前的文章。
一、下载和配置
1. 所需软件
· JDK 5.0
· eclipse-SDK-3.1RC3-win32.zip
· xmlbuddy_2.0.62.zip
· spring-framework-1.2.1-with-dependencies.zip
如果安装后在Eclipse中看不到XmlBuddy,那么在eclipse命令后加上“-clean”参数。
2. 配置
● 配置Eclipse:
· Window->Perferences->Java->Compiler:将"Compiler compliance level"设为“5.0”
· Window->Perferences->Java->Build Path:勾选“Folders”
● 建立库文件夹
具体做法参考《Eclipse快速上手Hibernate--1. 入门实例》一文中的“建立库文件夹”的相关部分。
这里先将spring-framework-1.2.1-with-dependencies.zip解压,将其中的spring.jar(dist目录中)、commons-logging.jar(lib\jakarta-commons目录)、log4j-1.2.9.jar(lib\log4j目录)这三个文件复制到的”D:\java\Spring\lib" 目录中,然后在Eclipse中建立一个“Spring”库,将那三个文件添加进“Spring”库中。
二、项目实践
1. 创建项目
· 新建一个Java Project:SpringHello,注意要导入用户库Spring。
· 这是完成后整个项目的结构(预览一下):
2. 简单的HelloWorld
·下面开始创建一个新类:HelloWorld ;包名:javamxj.spring.beginning1,代码如下:
HelloWorld.java
package javamxj.spring.beginning1;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
OK!非常简单,我就是要它打印出一条语句“Hello World!”。
现在我不想输出“Hello World!”,我想随心所欲的输出任何语句,很简单啊!将“Hello World!”替换成所希望输出的语句即可。不过这样有一个缺点,每次修改好了,都需要重新编译一下程序。像这样的小程序固然无所谓,可是如果是一个大项目,这样做就有些讨厌了。
还好,只要稍微修改一下程序,通过参数输入即可。
· 继续在这个包下建立一个新类:HelloWorldWithCommandLine,同样非常简单:
HelloWorldWithCommandLine.java
package javamxj.spring.beginning1;
public class HelloWorldWithCommandLine {
public static void main(String[] args) {
if (args.length > 0) {
System.out.println(args[0]);
} else {
System.out.println("Hello World!");
}
}
}
· 选中HelloWorldWithCommandLine,右击->Run As->Run...,在弹出窗口切换到“Arguments”栏,在“program arguments”中填入“Hello,javamxj!”,如下图:
这样,就输出了我希望的语句。
一、下载和配置
1. 所需软件
· JDK 5.0
· eclipse-SDK-3.1RC3-win32.zip
· xmlbuddy_2.0.62.zip
· spring-framework-1.2.1-with-dependencies.zip
如果安装后在Eclipse中看不到XmlBuddy,那么在eclipse命令后加上“-clean”参数。
2. 配置
● 配置Eclipse:
· Window->Perferences->Java->Compiler:将"Compiler compliance level"设为“5.0”
· Window->Perferences->Java->Build Path:勾选“Folders”
● 建立库文件夹
具体做法参考《Eclipse快速上手Hibernate--1. 入门实例》一文中的“建立库文件夹”的相关部分。
这里先将spring-framework-1.2.1-with-dependencies.zip解压,将其中的spring.jar(dist目录中)、commons-logging.jar(lib\jakarta-commons目录)、log4j-1.2.9.jar(lib\log4j目录)这三个文件复制到的”D:\java\Spring\lib" 目录中,然后在Eclipse中建立一个“Spring”库,将那三个文件添加进“Spring”库中。
二、项目实践
1. 创建项目
· 新建一个Java Project:SpringHello,注意要导入用户库Spring。
· 这是完成后整个项目的结构(预览一下):
2. 简单的HelloWorld
·下面开始创建一个新类:HelloWorld ;包名:javamxj.spring.beginning1,代码如下:
HelloWorld.java
package javamxj.spring.beginning1;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
OK!非常简单,我就是要它打印出一条语句“Hello World!”。
现在我不想输出“Hello World!”,我想随心所欲的输出任何语句,很简单啊!将“Hello World!”替换成所希望输出的语句即可。不过这样有一个缺点,每次修改好了,都需要重新编译一下程序。像这样的小程序固然无所谓,可是如果是一个大项目,这样做就有些讨厌了。
还好,只要稍微修改一下程序,通过参数输入即可。
· 继续在这个包下建立一个新类:HelloWorldWithCommandLine,同样非常简单:
HelloWorldWithCommandLine.java
package javamxj.spring.beginning1;
public class HelloWorldWithCommandLine {
public static void main(String[] args) {
if (args.length > 0) {
System.out.println(args[0]);
} else {
System.out.println("Hello World!");
}
}
}
· 选中HelloWorldWithCommandLine,右击->Run As->Run...,在弹出窗口切换到“Arguments”栏,在“program arguments”中填入“Hello,javamxj!”,如下图:
这样,就输出了我希望的语句。
Spring之IOC
Spring的功能是很强大的,在其“绝不发明自己认为好的轮子,而只发明自己认为不好的轮子”的指导思想下,通过充分实践了“一切实事求是、‘循证架构’的工作方式”的理论,基本上把轻量级的J2EE应用框架(如ORM、MVC等)进行了整合,并构架了一些常用的功能(如DAO),形成了一个功能强大的J2EE轻量级企业应用框架。
然而,或许是大家对Spring掌握得还不透彻的缘故吧,看到很多软件企业中用到的Spring功能,基本上大多数都只是用其IOC功能,有时候附带用了其中的AOP事务管理功能。
IOC及AOP虽然不是Spring首创,然而其在这两块都是做得很不错的,应该说整个Spring框架就是围绕着其IOC实现及AOP实现架设起来的。我想,深入挖掘IOC、AOP以及Spring中的实现,使用等,对于初学者帮助会非常大,因此,从本期开始,大峡的《玩玩Spring系列》将伴随大家一起走进IOC及AOP的世界。
由于本人水平有限,文中难免有很多不足甚至错误之处,还请各位朋友不吝批评指教。
一、IOC简介
IOC-全称Inversion of Control,中文解释:控制反转。另外,IOC又称DI(全称)Dependency Injection,中文解释:依赖注入。
呵呵,这些名词搞得有点像学古文的味道哈。很大师还都说IOC中有一个著名的好莱坞理论:你呆着别动,到时我会找你。由于本人未到过好莱坞参加过社会实践,因此,这句话理解有点困难。
IOC是一种新的设计模式,即IOC模式,系统中通过引入实现了IOC模式的IOC容器,即可由IOC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配件文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的Java代码。
当前比较知名的IOC容器有:Pico Container、Avalon 、Spring、JBoss、HiveMind、EJB等,国内由板桥里人负责的国产开源项目Jdon框架,也是具有IOC容器功能(由于没来得及认真研读其源码,似乎jdon中IOC部份是调用Pico的IOC容器功能来实现的)。
在上面的几个IOC容器中,轻量级的有Pico Container、Avalon、Spring、HiveMind等,超重量级的有EJB,而半轻半重的有容器有JBoss,Jdon等。
IOC究竟是什么?IOC是如何产生的?用在什么场合?为什么我们以前不用IOC,而现在要用IOC?“物有本末,事有终始”,为了更加透彻的理解这一问题,大峡打算从自己所理解的面向对象(OO)设计及编程发展历程来进行分析,也许这样能让IOC的初学者更加了解IOC的发展的前因后果,争取做到“知其然,知其所以然,使其然!”。
若大家等不急了,就直接百度一下有关IOC的其它文章,这方面国内很多先驱们已经作了很多介绍。如冰云的《IOC详解》、板桥里人的设计模式及IOC理论等。
二、 最老的OO编程
记得曾经看《Think in Java》最早版本的时候,里面有这么一句让人振奋话:一切都是对象。这时我们OO编程的核心是围绕着面向对象编程的三个特性即“继承”、“封装”、“多态”来展开的。
2.1 封装
那时我们学会了对现实就的事物及软件模型进行了抽象。比如要描述一只猫,那么这支猫应该有“颜色”、“重量”、“公母”、“脾气”、“出生日期”等属性,另外还有“跑”、“吃”、“叫”、“猫捉老鼠”等方法。如Java代码来表示,大致就是如下:
public Class Cat
{
private String color;//颜色
private String weight;//重量
private String sex;//公母
private String temper;//脾气
private String birthday;//出生日期
private void run ();//跑
private void eat (Food food); //吃(食物)
private void shout(int type);//叫(类别)
private boolean chase(Mice mice);//猫捉老鼠
}
2.2 继承
最早的OO编程时期,我们还会引入继承,还经常鼓励大家多用继续,认为继承就是OO编程思想的核心。继承的核心就是围绕着如何把类与类之间具有共同特性的部份抽象到基类中。认为这样不但能使用了OO的特性,还减少了很多子类的代码。
我们通过日常生活的常识知道,猫是一种动物,因此动物有的特性他基本上都有。于是,如果我们的系统中不但有猫,还会有很多其它的动物出现。我们就会设计一个动物类,把所有动物的共性抽象到一个基类中。这里,猫及动物基类的代码大致如下:
public abstract Class Animal{
private String color;//颜色
private String weight;//重量
private String sex;//公母
private String temper;//脾气
private String birthday;//出生日期
private void run ();//跑
private void eat (Food food); //吃(食物)
private void shout(int type);//叫(类别)
}
public Class Cat extends Animal
private int power;//能力
private int agility;//敏捷度
// 猫捉老鼠是特有的方法
private boolean chase(Mice mice) {
return true;
};
}
2.3 多态
这时我们还会不时使用到OO的另外一个特性多态。多态是很重要的一门技术,然而很多时候却没有很好的理解并使用,回头看以前的代码,我们看到有很多地方属于故弄玄虚的嫌疑。
接上面的例子,假如我们要写一个喂养宠物(有猫、狗、猪、豹、老鼠等)的程序。利用Java的多态特性,我们的大致代码如下:
public class PetManage {
private Animal pet;
public PetManage(Animal pet)
{
this.pet=pet;
}
//喂食我的宠物
public void feeding(Animal a)
{
}
/**
* @param args
*/
public static void main(String[] args) {
Animal myPet=new Cat();
PetManage mp=new PetManage(myPet);
}
}
通过使用多态特性,哪一天若我们的不喜欢猫,而是喜欢养猪的时候,只要把new Cat()变成new Pig(),即可。
2.4 对象生命周期
这一阶段的OO程序中,我们知道要用一个对象的时候,就要使用Java中的关键字new来生成一个来用即可。OO对于我们来说,一切都是那么简单,很多时候甚至感觉OO跟OP的编程方法也没太大区别。代码如下:
Cat myCat=new Cat();//创建一支具体的猫
myCat.shout();//叫一声
此时,我们对Java虚拟是非常信任的,我们的思想也很单纯,我们知道Java对象的生命开始于new关键词。我们不太关心对象生命的结束,我们知道Java有一个比C语言历害、智能化的垃圾收集器,他会帮我们自己的清理内存中不用的对象。
当然,也有的人由于对垃圾收集器忠诚度的怀疑,不放心垃圾收集器的能力,于是在程序中经常要加一句类似“myPet=null”的代码来结束对象的生命。
当然,我们也知道有一些外部资源如数据库连接等,需要手动熟悉资源。于是知道在使用类似资源的时候必须都加上一句:conn.close(),有时候还要在close()后面再加一句:conn=null。呵呵,非常有意思。
2.5小结
现在看来,其实那时确实犯了很多幼稚的错误,也走了不少的弯路,做了很多画蛇添足的工作,写了很多难与维护的代码。
对比今天的IOC模式,若要从早的OO方法中硬要找一个类似Spring的容器的话,那就是:“程序员+JVM本身”。是程序员以及JVM一起我们管理对象的生命周期、对象之间的关系等。那时候若有任何变动都需要改代码,(虽然好的设计代码修改会非常少,但也得改!),然后编译,然后拿到测试环境及用户环境中执行。如此反复,年日复一日、年复一年。
那时我们的代码复用用得最多的就是OO的继承功能,另外还有很多OP方法中带过来的函数。
本文中涉及到的几个简单源码,请到EasyJF开源团队官网下载,地址:
http://www.easyjf.com/html/bbs/20060602/12718636-1843943.htm?ejid=1287314863003738
然而,或许是大家对Spring掌握得还不透彻的缘故吧,看到很多软件企业中用到的Spring功能,基本上大多数都只是用其IOC功能,有时候附带用了其中的AOP事务管理功能。
IOC及AOP虽然不是Spring首创,然而其在这两块都是做得很不错的,应该说整个Spring框架就是围绕着其IOC实现及AOP实现架设起来的。我想,深入挖掘IOC、AOP以及Spring中的实现,使用等,对于初学者帮助会非常大,因此,从本期开始,大峡的《玩玩Spring系列》将伴随大家一起走进IOC及AOP的世界。
由于本人水平有限,文中难免有很多不足甚至错误之处,还请各位朋友不吝批评指教。
一、IOC简介
IOC-全称Inversion of Control,中文解释:控制反转。另外,IOC又称DI(全称)Dependency Injection,中文解释:依赖注入。
呵呵,这些名词搞得有点像学古文的味道哈。很大师还都说IOC中有一个著名的好莱坞理论:你呆着别动,到时我会找你。由于本人未到过好莱坞参加过社会实践,因此,这句话理解有点困难。
IOC是一种新的设计模式,即IOC模式,系统中通过引入实现了IOC模式的IOC容器,即可由IOC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配件文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的Java代码。
当前比较知名的IOC容器有:Pico Container、Avalon 、Spring、JBoss、HiveMind、EJB等,国内由板桥里人负责的国产开源项目Jdon框架,也是具有IOC容器功能(由于没来得及认真研读其源码,似乎jdon中IOC部份是调用Pico的IOC容器功能来实现的)。
在上面的几个IOC容器中,轻量级的有Pico Container、Avalon、Spring、HiveMind等,超重量级的有EJB,而半轻半重的有容器有JBoss,Jdon等。
IOC究竟是什么?IOC是如何产生的?用在什么场合?为什么我们以前不用IOC,而现在要用IOC?“物有本末,事有终始”,为了更加透彻的理解这一问题,大峡打算从自己所理解的面向对象(OO)设计及编程发展历程来进行分析,也许这样能让IOC的初学者更加了解IOC的发展的前因后果,争取做到“知其然,知其所以然,使其然!”。
若大家等不急了,就直接百度一下有关IOC的其它文章,这方面国内很多先驱们已经作了很多介绍。如冰云的《IOC详解》、板桥里人的设计模式及IOC理论等。
二、 最老的OO编程
记得曾经看《Think in Java》最早版本的时候,里面有这么一句让人振奋话:一切都是对象。这时我们OO编程的核心是围绕着面向对象编程的三个特性即“继承”、“封装”、“多态”来展开的。
2.1 封装
那时我们学会了对现实就的事物及软件模型进行了抽象。比如要描述一只猫,那么这支猫应该有“颜色”、“重量”、“公母”、“脾气”、“出生日期”等属性,另外还有“跑”、“吃”、“叫”、“猫捉老鼠”等方法。如Java代码来表示,大致就是如下:
public Class Cat
{
private String color;//颜色
private String weight;//重量
private String sex;//公母
private String temper;//脾气
private String birthday;//出生日期
private void run ();//跑
private void eat (Food food); //吃(食物)
private void shout(int type);//叫(类别)
private boolean chase(Mice mice);//猫捉老鼠
}
2.2 继承
最早的OO编程时期,我们还会引入继承,还经常鼓励大家多用继续,认为继承就是OO编程思想的核心。继承的核心就是围绕着如何把类与类之间具有共同特性的部份抽象到基类中。认为这样不但能使用了OO的特性,还减少了很多子类的代码。
我们通过日常生活的常识知道,猫是一种动物,因此动物有的特性他基本上都有。于是,如果我们的系统中不但有猫,还会有很多其它的动物出现。我们就会设计一个动物类,把所有动物的共性抽象到一个基类中。这里,猫及动物基类的代码大致如下:
public abstract Class Animal{
private String color;//颜色
private String weight;//重量
private String sex;//公母
private String temper;//脾气
private String birthday;//出生日期
private void run ();//跑
private void eat (Food food); //吃(食物)
private void shout(int type);//叫(类别)
}
public Class Cat extends Animal
private int power;//能力
private int agility;//敏捷度
// 猫捉老鼠是特有的方法
private boolean chase(Mice mice) {
return true;
};
}
2.3 多态
这时我们还会不时使用到OO的另外一个特性多态。多态是很重要的一门技术,然而很多时候却没有很好的理解并使用,回头看以前的代码,我们看到有很多地方属于故弄玄虚的嫌疑。
接上面的例子,假如我们要写一个喂养宠物(有猫、狗、猪、豹、老鼠等)的程序。利用Java的多态特性,我们的大致代码如下:
public class PetManage {
private Animal pet;
public PetManage(Animal pet)
{
this.pet=pet;
}
//喂食我的宠物
public void feeding(Animal a)
{
}
/**
* @param args
*/
public static void main(String[] args) {
Animal myPet=new Cat();
PetManage mp=new PetManage(myPet);
}
}
通过使用多态特性,哪一天若我们的不喜欢猫,而是喜欢养猪的时候,只要把new Cat()变成new Pig(),即可。
2.4 对象生命周期
这一阶段的OO程序中,我们知道要用一个对象的时候,就要使用Java中的关键字new来生成一个来用即可。OO对于我们来说,一切都是那么简单,很多时候甚至感觉OO跟OP的编程方法也没太大区别。代码如下:
Cat myCat=new Cat();//创建一支具体的猫
myCat.shout();//叫一声
此时,我们对Java虚拟是非常信任的,我们的思想也很单纯,我们知道Java对象的生命开始于new关键词。我们不太关心对象生命的结束,我们知道Java有一个比C语言历害、智能化的垃圾收集器,他会帮我们自己的清理内存中不用的对象。
当然,也有的人由于对垃圾收集器忠诚度的怀疑,不放心垃圾收集器的能力,于是在程序中经常要加一句类似“myPet=null”的代码来结束对象的生命。
当然,我们也知道有一些外部资源如数据库连接等,需要手动熟悉资源。于是知道在使用类似资源的时候必须都加上一句:conn.close(),有时候还要在close()后面再加一句:conn=null。呵呵,非常有意思。
2.5小结
现在看来,其实那时确实犯了很多幼稚的错误,也走了不少的弯路,做了很多画蛇添足的工作,写了很多难与维护的代码。
对比今天的IOC模式,若要从早的OO方法中硬要找一个类似Spring的容器的话,那就是:“程序员+JVM本身”。是程序员以及JVM一起我们管理对象的生命周期、对象之间的关系等。那时候若有任何变动都需要改代码,(虽然好的设计代码修改会非常少,但也得改!),然后编译,然后拿到测试环境及用户环境中执行。如此反复,年日复一日、年复一年。
那时我们的代码复用用得最多的就是OO的继承功能,另外还有很多OP方法中带过来的函数。
本文中涉及到的几个简单源码,请到EasyJF开源团队官网下载,地址:
http://www.easyjf.com/html/bbs/20060602/12718636-1843943.htm?ejid=1287314863003738
ASP的六大对象说明
Application对象
Application对象是个应用程序级的对象,用来在所有用户间共享信息,并可以在Web应用程序运行期间持久地保持数据。
Application的属性:
方法如下:
Application对象没有内置的属性,但是我们可以自行创建其属性。
<% Application("属性名")=值 %>
其实大部分Application变量都 存放在Contents集合中,当你创建一个新的Application变量时,其实就是在Contents集合中添加了一项。下面两个脚本是等效的:
<% Application("greeting")="hello!" %> 或 <% Application.contents("greeting")="hello!"由于Application变量存在集合里,所以如果想要全部显示,其方法我们已经多次使用,例如For Each循环。
<%
For Each item IN Application.Contents
Response.write("<br>"&item&Application.Contents(item))
next
%>
Application的方法:
Application的方法只有两个方法:一个是Lock,另一个是Unlock。其中Lock方法用于保证同一时刻只能一个用户对Application操作。Unlock则用于取消Lock方法的限制。如:
<%
Application.Lock
Application("visitor_num")=Application("visitor_num")+1
Application.Unlock
%>
Application的事件:
1、Application_OnStart()
当事件应用程序启动时触发。
2、Application_OnEnd()
此事件应用程序结束时触发。
这两个事件过程都是必须定义在Global.asp文件中,一般把连接数据的函数定义在这两个事件,然后放在Global.asp中。例如:
Sub Application_OnStart
Application("tt18_ConnectionString") = "driver={SQL
Server};server=jeff;uid=sa;pwd=;database=test"
End Sub
一个数组能够被定义成Application对象,不过这个数组只能作为一个对象保存,而不能用Application(0)取出其值。可以定义一个临时数组实现这种功能。如:
<%
dim Array()
Array=Application("array")
for i = 0 to ubound(array)
Response.write Array(i)
next i
%>
同样要修改这个Application对象也可以定义一个临时数组,把Application对象赋给数组,然后修改这个数组的元素,最后把数组赋回Application对象。如:
<%
dim Array()
Array=Application("array")
Array(0)="jeff"
Array(1)="zhu"
Application.lock
Application("array")=Array
Application.unlock
%>
ObjectContext对象
该对象用于控制Active Server Pages的事务处理。事务处理由Microsoft Transaction Server (MTS)管理。
事件
ObjectContext.OnTransactionAbort
由放弃的事务处理事件激发,在脚本完成处理后发生。
ObjectContext.OnTransactionCommit
由成功的事务处理事件激发,在脚本完成处理后发生。
方法
ObjectContext.SetAbort
显式的放弃一次事务处理。
ObjectContext.SetComplete
覆盖前面任何调用ObjectContext.SetAbort方法的调用。
Request对象
Request对象用于接受所有从浏览器发往你的服务器的请求内的所有信息。
集合
Request.ClientCertificate(key[SubField])
所有客户证书的信息的集合。对于Key,该集合具有如下的关键字:
Subject
证书的主题。包含所有关于证书收据的信息。能和所有的子域后缀一起使用。
Issuer
证书的发行人。包含所有关于证书验证的信息。除了CN外,能和所有的子域后缀一起使用。
VadidFrom
证书发行的日期。使用VBScript格式。
ValidUntil
该证书不在有效的时间。
SerialNumber
包含该证书的序列号。
Certificate
包含整个证书内容的二进制流,使用ASN.1格式。
对于SubField,Subject和Issuer关键字可以具有如下的子域后缀:(比如:SubjectOU或IssuerL)
C
起源国家。
O
公司或组织名称。
OU
组织单元。
CN
用户的常规名称。
L
局部。
S
州(或省)。
T
个人或公司的标题。
GN
给定名称。
I
初始。
当文件cervbs.inc(VBScript使用)或cerjavas.inc(Jscript使用)通过使用#INCLUDE导向包含在你的Active Server Page里时,下面两个标志可以使用:
ceCertPresent
指明客户证书是否存在,其值为TRUE或FALSE。
ceUnrecongnizedIssure
指明在该链表中的最后的证书的发行者是否未知,其值为TRUE或FALSE。
Request.Cookies(Cookie[(key).Attribute])
Cookie的集合。允许获得浏览器的Cookie。Cookie指明返回那一个Cookie。Key用于从Cookie字典中返回具有某一关键字的Cookie值。对于Attribute,你能使用属性HasKeys来确定某一Cookie是否具有子关键字。HasKeys的值为TRUE或FALSE。
Request.Form(Parameter)[(Index).Count]
填写在HTML的表单中所有的数据的集合。Parameter是在HTML表单中某一元素的名称。当某一参数具有不止一个值(比如,当在<SELECT>中使用MULTIPLE属性时)时,使用Index。当某一参数具有多值时,Count指明多值个数。
Request.QueryString(Varible)[(Index).Count]
查询字符串的所有值的集合。Varible是在查询字符串某一变量的名称。当某一变量具有多于一个值时,使用Index。当某一参数具有多值时,Count指明值的个数。
Request.ServerVaribles(Server Environment Variable)
环境变量的集合。允许读取HTTP头。你可以通过使用HTTP_前缀来读取任何头信息。比如,HTTP_USER_AGENT接受客户代理HTTP头(浏览器类型)。除此外,你可以使用下表所示的变量获得任何环境信息。
ALL_HTTP
客户端发送的所有HTTP标头,他的结果都有前缀HTTP_。
ALL_RAW
客户端发送的所有HTTP标头,其结果和客户端发送时一样,没有前缀HTTP_
APPL_MD_PATH
应用程序的元数据库路径。
APPL_PHYSICAL_PATH
与应用程序元数据库路径相应的物理路径。
AUTH_PASSWORD
当使用基本验证模式时,客户在密码对话框中输入的密码。
AUTH_TYPE
这是用户访问受保护的脚本时,服务器用于检验用户的验证方法。
AUTH_USER
代验证的用户名。
CERT_COOKIE
唯一的客户证书ID号。
CERT_FLAG
客户证书标志,如有客户端证书,则bit0为0。如果客户端证书验证无效,bit1被设置为1。
CERT_ISSUER
用户证书中的发行者字段。
CERT_KEYSIZE
安全套接字层连接关键字的位数,如128。
CERT_SECRETKEYSIZE
服务器验证私人关键字的位数。如1024。
CERT_SERIALNUMBER
客户证书的序列号字段。
CERT_SERVER_ISSUER
服务器证书的发行者字段
CERT_SERVER_SUBJECT
服务器证书的主题字段。
CERT_SUBJECT
客户端证书的主题字段。
CONTENT_LENGTH
客户端发出内容的长度。
CONTENT_TYPE
客户发送的form内容或HTTP PUT的数据类型。
GATEWAY_INTERFACE
服务器使用的网关界面。
HTTPS
如果请求穿过安全通道(SSL),则返回ON。如果请求来自非安全通道,则返回OFF。
HTTPS_KEYSIZE
安全套接字层连接关键字的位数,如128。
HTTPS_SECRETKEYSIZE
服务器验证私人关键字的位数。如1024。
HTTPS_SERVER_ISSUER
服务器证书的发行者字段。
HTTPS_SERVER_SUBJECT
服务器证书的主题字段。
INSTANCE_ID
IIS实例的ID号。
INSTANCE_META_PATH
响应请求的IIS实例的元数据库路径。
LOCAL_ADDR
返回接受请求的服务器地址。
LOGON_USER
用户登录Windows NT的帐号
PATH_INFO
客户端提供的路径信息。
PATH_TRANSLATED
通过由虚拟至物理的映射后得到的路径。
QUERY_STRING
查询字符串内容。
REMOTE_ADDR
发出请求的远程主机的IP地址。
REMOTE_HOST
发出请求的远程主机名称。
REQUEST_METHOD
提出请求的方法。比如GET、HEAD、POST等等。
SCRIPT_NAME
执行脚本的名称。
SERVER_NAME
服务器的主机名、DNS地址或IP地址。
SERVER_PORT
接受请求的服务器端口号。
SERVER_PORT_SECURE
如果接受请求的服务器端口为安全端口时,则为1,否则为0。
SERVER_PROTOCOL
服务器使用的协议的名称和版本。
SERVER_SOFTWARE
应答请求并运行网关的服务器软件的名称和版本。
URL
提供URL的基本部分。
方法
Request.BinaryRead(Count)
接收一个HTML表单的未经过处理的内容。当调用此方法时,Count指明要接收多少字节。在调用此方法后,Count指明实际上接收到多少个字节。
属性
Request.TotalBytes
查询体的长度,以字节为单位
Response对象
Response对象用于向客户端浏览器发送数据,用户可以使用该对象将服务器的数据以HTML的格式发送到用户端的浏览器,它与Request组成了一对接收、发送数据的对象,这也是实现动态的基础。下面介绍它常用的属性和方法。
1、Buffer属性
该属性用于指定页面输出时是否要用到缓冲区,默认值为False。当它为True时,直到整个Active Server Page执行结束后才会将结果输出到浏览器上。如:
<%Response.Buffer=True%>
<html>
<Head>
<title>Buffer示例</title>
</head>
<body>
<%
for i=1 to 500
response.write(i & "<br>")
next
%>
</body>
</html>
这页执行时,整个主页的所有内容会同时显示在浏览器上,这个主页会存在缓存区中直到脚本执行结束。
2、Expires属性
该属性用于设置浏览器缓存页面的时间长度(单位为分),必须在服务器端刷新。通过如下设置:
<%Response.Expires=0%>
通过在ASP文件中加入这一行代码,要求每次请求是刷新页面,因为Response一收到页面就会过期。
3、Write方法
该方法把数据发送到客户端浏览器,如:
<%Response.write "Hello,world!"%>
4、Redirect方法
该方法使浏览器可以重新定位到另一个URL上,这样,当客户发出Web请求时,客户端的浏览器类型已经确定,客户被重新定位到相应的页面。如:
<html>
<head>
<title>Redirect示例</title>
</head>
<body>
<form aciton="formjump.asp" method="post">
<select >
<option selected value="fun">Fun</option>
<option value="news">News</option>
<option value="sample">Sample</option>
</select>
<input type=submit value="Jump">
</form>
</body>
</html>
以上是提交的表单,下面是处理表单的文件formjump.asp:
<%response.buff=true%>
<html>
<head>
<title>Redirect示例</title>
</head>
<body>
<%
thisurl="http://www.tinyu.com/";
where=Request.form("wheretogo")
Select Case where
case "fun"
response.redirect thisurl & "/fun/default.asp"
case "news"
response.redirect thisurl & "/news/default.asp"
case "sample"
response.redirect thisurl & "/sample/default.asp"
End Select
%>
</body>
<html>
这个例子当用户选择了以后,按"Jump"按钮提交表单,服务器接到申请后调用formjump.asp判断后定位到相应的URL。不过这里有一点要注意,HTTP标题已经写入到客户浏览器,任何HTTP标题的修改必须在写入页内容之前,遇到这种问题时,可以如下做:
在文件的开始<@ Language=..>后写:
Response.Buffer=True
在结尾定:
Response.Flush
这里Flush是Response的一个方法,它必须是Buffer属性设置为True时才能使用,否则会产生一个运行模式错误。另外一个Clear方法也是用于清除被缓存的页面,同样要Buffer属性设置为True时才能使用。
5、End方法
该方法用于告知Active Server当遇到该方法时停止处理ASP文件。如果Response对象的Buffer属性设置为True,这时End方法即把缓存中的内容发送到客户并清除冲区。所以要取消所有向客户的输出民,可以先清除缓冲区,然后利用End方法。如:
<%
Response.buffer=true
On error resume next
Err.clear
if Err.number<>0 then
Response.Clear
Response.End
end if
%>
Server 对象:
Server 对象提供对服务器上的方法和属性的访问。其中大多数方法和属性是作为实用程序的功能服务的。
语法
Server.property|method
属性
ScriptTimeout:
ScriptTimeout 属性指定脚本在结束前最大可运行多长时间。 当处理服务器组件时,超时限制将不再生效。
语法 Server.ScriptTimeout = NumSeconds
参数 NumSeconds
指定脚本在被服务器结束前最大可运行的秒数。默认值为 90 秒。
注释
通过使用元数据库中的AspScriptTimeout属性可以为 Web 服务或 Web 服务器设置缺省的ScriptTimeout值。ScriptTimeout属性不能设置为小于在元数据库中指定的值。例如,如果NumSeconds设置为10,而元数据库设置包含了默认值90秒,则脚本在90秒后超时。但如果NumSeconds设置为100,则脚本在100秒后超时。
关于使用元数据库的详细信息,参阅 关于元数据库。
示例 以下示例中,如果服务器处理脚本超过 100 秒,将使之超时。
<% Server.ScriptTimeout = 100 %>
以下示例获取 ScriptTimeout 属性当前值,并将其存储在变量 TimeOut 中。
<% TimeOut = Server.ScriptTimeout %>
方法
CreateObject
CreateObject 方法创建服务器组件的实例。如果该组件执行了 OnStartPage 和 OnEndPage 方法,则此时就会调用 OnStartPage 方法。有关服务器组件的详细信息,请参阅 可安装的 ASP 组件 。
语法 Server.CreateObject( progID )
参数 progID 指定要创建的对象的类型。progID 的格式为 [Vendor.] component[.Version]。
注释 默认情况下,由 Server.CreateObject 方法创建的对象具有页作用域。这就是说,再当前 ASP 页处理完成之后,服务器将自动破坏这些对象。要创建有会话或应用程序作用域的对象,可以使用 <OBJECT> 标记并设置 SESSION 或 APPLICATION 的 SCOPE 属性,也可以在对话及应用程序变量中存储该对象。
例如,在如下所示的脚本中,当 Session 对象被破坏,即当对话超时时或 Abandon 方法被调用时,存储在会话变量中的对象也将被破坏。
<% Set Session("ad") = Server.CreateObject("MSWC.AdRotator")%>
可以通过将变量设置为 Nothing 或新的值来破坏对象,如下所示。第一个例子释放 ad 对象,第二个例子用字串代替 ad 。
<% Session ("ad") = Nothing %>
<% Session ("ad") = " Other Valum " %>
不能创建与内建对象同名的对象实例。 例如,下列脚本将返回错误。
<% Set Response = Server.CreateObject("Response") %>
示例 <% Set MyAd = Server.CreateObject("MSWC.AdRotator") %>
上面的例子创建一个名为 MyAd 的 MSWC.AdRotator 服务器组件,MSWC.AdRotator 组件可用于在 Web 页上的自动轮换广告。
关于服务器组件的详细信息, 请参阅 Creating Components for ASP.
HTMLEncode HTMLEncode方法对指定的字符串应用 HTML 编码。
语法 Server.HTMLEncode( string )
参数 string 指定要编码的字符串。
示例脚本 <%= Server.HTMLEncode("The paragraph tag: <P>") %>
输出 The paragraph tag: <P>
注意 以上输出将被 Web 浏览器显示为The paragraph tag: <P>如果查看一下源文件或以文本方式打开一个 Web 页,您就可以看到已编码的 HTML。
MapPath
MapPath 方法将指定的相对或虚拟路径映射到服务器上相应的物理目录上。
语法
Server.MapPath( Path )
参数
Path
指定要映射物理目录的相对或虚拟路径。若 Path 以一个正斜杠 (/) 或反斜杠 (\) 开始,则 MapPath 方法返回路径时将 Path 视为完整的虚拟路径。若 Path 不是以斜杠开始,则 MapPath 方法返回同 .asp 文件中已有的路径相对的路径。
注释
MapPath 方法不支持相对路径语法 (.) 或 (..)。例如,下列相对路径 ../MyDir/MyFile.txt 返回一个错误。
MapPath 方法不检查返回的路径是否正确或在服务器上是否存在。
因为 MapPath 方法只映射路径而不管指定的目录是否存在,所以,您可以先用 MapPath 方法映射物理目录结构的路径,然后将其传递给在服务器上创建指定目录或文件的组件。
示例
对于下列示例,文件data.txt和包含下列脚本的test.asp文件都位于目录C:\Inetpub\Wwwroot\Script下。C:\Inetpub\Wwwroot目录被设置为服务器的宿主目录。
下列示例使用服务器变量 PATH_INFO 映射当前文件的物理路径。脚本
<%= server.mappath(Request.ServerVariables("PATH_INFO"))%><BR>
输出
c:\inetpub\wwwroot\script\test.asp<BR>
由于下列示例中的路径参数不是以斜杠字符开始的,所以它们被相对映射到当前目录,此处是 C:\Inetpub\Wwwroot\Script。脚本
<%= server.mappath("data.txt")%><BR>
<%= server.mappath("script/data.txt")%><BR>
输出
c:\inetpub\wwwroot\script\data.txt<BR>
c:\inetpub\wwwroot\script\script\data.txt<BR>
接下来的两个示例使用斜杠字符指定返回的路径应被视为在服务器的完整虚拟路径。脚本
<%= server.mappath("/script/data.txt")%><BR>
<%= server.mappath("\script")%><BR>
输出
c:\inetpub\script\data.txt<BR>
c:\inetpub\script<BR>
下列示例演示如何使用正斜杠 (/) 或反斜杠 (\) 返回宿主目录的物理路径。脚本
<%= server.mappath("/")%><BR>
<%= server.mappath("\")%><BR>
输出
c:\inetpub\wwwroot<BR>
c:\inetpub\wwwroot<BR>
URLEncode
URLEncode 方法将 URL 编码规则,包括转义字符,应用到指定的字符串。
语法
Server.URLEncode( string )
参数
String 指定要编码的字符串。
示例
脚本 <%Response.Write(Server.URLEncode("http://www.tinyu.com";)) %>
输出 http%3A%2F%2Fwww%2Etinyu%2Ecom
Session对象
Session其实指的就是访问者从到达某个特定主页到离开为止的那段时间。每一访问者都会单独获得一个Session。在Web应用程序中,当一个用户访问该应用时,Session类型的变量可以供这个用户在该Web应用的所有页面中共享数据;如果另一个用户也同时访问该Web应用,他也拥有自己的Session变量,但两个用户之间无法通过Session变量共享信息,而Application类型的变更则可以实现站点多个用户之间在所有页面中共享信息。
1、SessionID属性
该属性返回当前会话的唯一标志,为每一个Session分配不同的编号。
我曾在开发过程中就遇到对用户的控制问题。它要实现的功能就是,针对某个网站的一个模块,当一个会员登录后正在看此模块时,另一个人用同样的会员名登录,就不能浏览这个模块。也就是说一个会员名同时只能一个人浏览此模块。我通过用会员名(假设为UserID,唯一)和SessionID来实现了控制。当会员登录时,给这个会员一个Session记录登录状态如:Session("Status")="Logged",同时把这个会员的Session.SessionID写入数据库。当他要浏览此模块时,先判断其是否登录,若已经登录再判断它的SessionID是否与数据库记录的相同,如果不同则不能访问。这样,当另一个用户用相同的会员名登录时,那么数据库中记录的就是新的SessionID,前者访问此模块时就不能通过检查。这就实现了一个会员名同时只能一个人浏览某个模块。这个功能在一些收费网站有很有特别作用,它防止了一个会员名给多个人浏览的问题,为公司保障了利益。
2、TimeOut属性
该属性用来定义用户Session对象的时限。如果用户在规定的时间内没有刷新网页,则Session对象就会终止。一般默认为20分钟。
3、Abandon方法
该方法是Session对象的唯一方法,可以清除Session对象,用来消除用户的Session对象并释放其所占的资源。如: <% Session.Abandon %>
4、Session_OnStart和Session_OnEnd事件
和Application一样,当对象的例程每一次启动时触发Session_OnStart事件,然后运行Session_Onstart事件的处理过程。也就是说,当服务器接收到应用程序中的URL的HTTP请求时,触发此事件,并建立一个Session对象。同理,这个事件也必须定在Global.asa文件中。
当调用Session.Abandon方法时或者在TimeOut的时间内没有刷新,这会触发Session_OnEnd事件,然后执行里面的脚本。Session变量与特定的用户相联系,针对某一个用户赋值的Session变量是和其他用户的Session变量完全独立的,不会存在相互影响。
Session应用一列:
与Application一样,一个被定义为Session类型的数组只能将整个数组作为一个对象,用户不能直接改变Session数组中某个元素的值。为了创建一个Session数组,需先定义一个普通的数组,并对它的每一个元素赋初值,最后把它定义为一个Session数组。如:
<%
dim array()
array=array("李","明","男")
Session("info")=array
Response.write Session("info")(0) &"-"
Response.write Session("info")(1) &"-"
Response.write Session("info")(2) &"<br>"
%>
<hr>
<%
array(0)="天"
array(1)="宇"
array(2)="男"
Session("info")=array
Response.write Session("info")(0) & "-"
Response.write Session("info")(1) & "-"
Response.write Session("info")(2) & "<br>"
%>
Application对象是个应用程序级的对象,用来在所有用户间共享信息,并可以在Web应用程序运行期间持久地保持数据。
Application的属性:
方法如下:
Application对象没有内置的属性,但是我们可以自行创建其属性。
<% Application("属性名")=值 %>
其实大部分Application变量都 存放在Contents集合中,当你创建一个新的Application变量时,其实就是在Contents集合中添加了一项。下面两个脚本是等效的:
<% Application("greeting")="hello!" %> 或 <% Application.contents("greeting")="hello!"由于Application变量存在集合里,所以如果想要全部显示,其方法我们已经多次使用,例如For Each循环。
<%
For Each item IN Application.Contents
Response.write("<br>"&item&Application.Contents(item))
next
%>
Application的方法:
Application的方法只有两个方法:一个是Lock,另一个是Unlock。其中Lock方法用于保证同一时刻只能一个用户对Application操作。Unlock则用于取消Lock方法的限制。如:
<%
Application.Lock
Application("visitor_num")=Application("visitor_num")+1
Application.Unlock
%>
Application的事件:
1、Application_OnStart()
当事件应用程序启动时触发。
2、Application_OnEnd()
此事件应用程序结束时触发。
这两个事件过程都是必须定义在Global.asp文件中,一般把连接数据的函数定义在这两个事件,然后放在Global.asp中。例如:
Sub Application_OnStart
Application("tt18_ConnectionString") = "driver={SQL
Server};server=jeff;uid=sa;pwd=;database=test"
End Sub
一个数组能够被定义成Application对象,不过这个数组只能作为一个对象保存,而不能用Application(0)取出其值。可以定义一个临时数组实现这种功能。如:
<%
dim Array()
Array=Application("array")
for i = 0 to ubound(array)
Response.write Array(i)
next i
%>
同样要修改这个Application对象也可以定义一个临时数组,把Application对象赋给数组,然后修改这个数组的元素,最后把数组赋回Application对象。如:
<%
dim Array()
Array=Application("array")
Array(0)="jeff"
Array(1)="zhu"
Application.lock
Application("array")=Array
Application.unlock
%>
ObjectContext对象
该对象用于控制Active Server Pages的事务处理。事务处理由Microsoft Transaction Server (MTS)管理。
事件
ObjectContext.OnTransactionAbort
由放弃的事务处理事件激发,在脚本完成处理后发生。
ObjectContext.OnTransactionCommit
由成功的事务处理事件激发,在脚本完成处理后发生。
方法
ObjectContext.SetAbort
显式的放弃一次事务处理。
ObjectContext.SetComplete
覆盖前面任何调用ObjectContext.SetAbort方法的调用。
Request对象
Request对象用于接受所有从浏览器发往你的服务器的请求内的所有信息。
集合
Request.ClientCertificate(key[SubField])
所有客户证书的信息的集合。对于Key,该集合具有如下的关键字:
Subject
证书的主题。包含所有关于证书收据的信息。能和所有的子域后缀一起使用。
Issuer
证书的发行人。包含所有关于证书验证的信息。除了CN外,能和所有的子域后缀一起使用。
VadidFrom
证书发行的日期。使用VBScript格式。
ValidUntil
该证书不在有效的时间。
SerialNumber
包含该证书的序列号。
Certificate
包含整个证书内容的二进制流,使用ASN.1格式。
对于SubField,Subject和Issuer关键字可以具有如下的子域后缀:(比如:SubjectOU或IssuerL)
C
起源国家。
O
公司或组织名称。
OU
组织单元。
CN
用户的常规名称。
L
局部。
S
州(或省)。
T
个人或公司的标题。
GN
给定名称。
I
初始。
当文件cervbs.inc(VBScript使用)或cerjavas.inc(Jscript使用)通过使用#INCLUDE导向包含在你的Active Server Page里时,下面两个标志可以使用:
ceCertPresent
指明客户证书是否存在,其值为TRUE或FALSE。
ceUnrecongnizedIssure
指明在该链表中的最后的证书的发行者是否未知,其值为TRUE或FALSE。
Request.Cookies(Cookie[(key).Attribute])
Cookie的集合。允许获得浏览器的Cookie。Cookie指明返回那一个Cookie。Key用于从Cookie字典中返回具有某一关键字的Cookie值。对于Attribute,你能使用属性HasKeys来确定某一Cookie是否具有子关键字。HasKeys的值为TRUE或FALSE。
Request.Form(Parameter)[(Index).Count]
填写在HTML的表单中所有的数据的集合。Parameter是在HTML表单中某一元素的名称。当某一参数具有不止一个值(比如,当在<SELECT>中使用MULTIPLE属性时)时,使用Index。当某一参数具有多值时,Count指明多值个数。
Request.QueryString(Varible)[(Index).Count]
查询字符串的所有值的集合。Varible是在查询字符串某一变量的名称。当某一变量具有多于一个值时,使用Index。当某一参数具有多值时,Count指明值的个数。
Request.ServerVaribles(Server Environment Variable)
环境变量的集合。允许读取HTTP头。你可以通过使用HTTP_前缀来读取任何头信息。比如,HTTP_USER_AGENT接受客户代理HTTP头(浏览器类型)。除此外,你可以使用下表所示的变量获得任何环境信息。
ALL_HTTP
客户端发送的所有HTTP标头,他的结果都有前缀HTTP_。
ALL_RAW
客户端发送的所有HTTP标头,其结果和客户端发送时一样,没有前缀HTTP_
APPL_MD_PATH
应用程序的元数据库路径。
APPL_PHYSICAL_PATH
与应用程序元数据库路径相应的物理路径。
AUTH_PASSWORD
当使用基本验证模式时,客户在密码对话框中输入的密码。
AUTH_TYPE
这是用户访问受保护的脚本时,服务器用于检验用户的验证方法。
AUTH_USER
代验证的用户名。
CERT_COOKIE
唯一的客户证书ID号。
CERT_FLAG
客户证书标志,如有客户端证书,则bit0为0。如果客户端证书验证无效,bit1被设置为1。
CERT_ISSUER
用户证书中的发行者字段。
CERT_KEYSIZE
安全套接字层连接关键字的位数,如128。
CERT_SECRETKEYSIZE
服务器验证私人关键字的位数。如1024。
CERT_SERIALNUMBER
客户证书的序列号字段。
CERT_SERVER_ISSUER
服务器证书的发行者字段
CERT_SERVER_SUBJECT
服务器证书的主题字段。
CERT_SUBJECT
客户端证书的主题字段。
CONTENT_LENGTH
客户端发出内容的长度。
CONTENT_TYPE
客户发送的form内容或HTTP PUT的数据类型。
GATEWAY_INTERFACE
服务器使用的网关界面。
HTTPS
如果请求穿过安全通道(SSL),则返回ON。如果请求来自非安全通道,则返回OFF。
HTTPS_KEYSIZE
安全套接字层连接关键字的位数,如128。
HTTPS_SECRETKEYSIZE
服务器验证私人关键字的位数。如1024。
HTTPS_SERVER_ISSUER
服务器证书的发行者字段。
HTTPS_SERVER_SUBJECT
服务器证书的主题字段。
INSTANCE_ID
IIS实例的ID号。
INSTANCE_META_PATH
响应请求的IIS实例的元数据库路径。
LOCAL_ADDR
返回接受请求的服务器地址。
LOGON_USER
用户登录Windows NT的帐号
PATH_INFO
客户端提供的路径信息。
PATH_TRANSLATED
通过由虚拟至物理的映射后得到的路径。
QUERY_STRING
查询字符串内容。
REMOTE_ADDR
发出请求的远程主机的IP地址。
REMOTE_HOST
发出请求的远程主机名称。
REQUEST_METHOD
提出请求的方法。比如GET、HEAD、POST等等。
SCRIPT_NAME
执行脚本的名称。
SERVER_NAME
服务器的主机名、DNS地址或IP地址。
SERVER_PORT
接受请求的服务器端口号。
SERVER_PORT_SECURE
如果接受请求的服务器端口为安全端口时,则为1,否则为0。
SERVER_PROTOCOL
服务器使用的协议的名称和版本。
SERVER_SOFTWARE
应答请求并运行网关的服务器软件的名称和版本。
URL
提供URL的基本部分。
方法
Request.BinaryRead(Count)
接收一个HTML表单的未经过处理的内容。当调用此方法时,Count指明要接收多少字节。在调用此方法后,Count指明实际上接收到多少个字节。
属性
Request.TotalBytes
查询体的长度,以字节为单位
Response对象
Response对象用于向客户端浏览器发送数据,用户可以使用该对象将服务器的数据以HTML的格式发送到用户端的浏览器,它与Request组成了一对接收、发送数据的对象,这也是实现动态的基础。下面介绍它常用的属性和方法。
1、Buffer属性
该属性用于指定页面输出时是否要用到缓冲区,默认值为False。当它为True时,直到整个Active Server Page执行结束后才会将结果输出到浏览器上。如:
<%Response.Buffer=True%>
<html>
<Head>
<title>Buffer示例</title>
</head>
<body>
<%
for i=1 to 500
response.write(i & "<br>")
next
%>
</body>
</html>
这页执行时,整个主页的所有内容会同时显示在浏览器上,这个主页会存在缓存区中直到脚本执行结束。
2、Expires属性
该属性用于设置浏览器缓存页面的时间长度(单位为分),必须在服务器端刷新。通过如下设置:
<%Response.Expires=0%>
通过在ASP文件中加入这一行代码,要求每次请求是刷新页面,因为Response一收到页面就会过期。
3、Write方法
该方法把数据发送到客户端浏览器,如:
<%Response.write "Hello,world!"%>
4、Redirect方法
该方法使浏览器可以重新定位到另一个URL上,这样,当客户发出Web请求时,客户端的浏览器类型已经确定,客户被重新定位到相应的页面。如:
<html>
<head>
<title>Redirect示例</title>
</head>
<body>
<form aciton="formjump.asp" method="post">
<select >
<option selected value="fun">Fun</option>
<option value="news">News</option>
<option value="sample">Sample</option>
</select>
<input type=submit value="Jump">
</form>
</body>
</html>
以上是提交的表单,下面是处理表单的文件formjump.asp:
<%response.buff=true%>
<html>
<head>
<title>Redirect示例</title>
</head>
<body>
<%
thisurl="http://www.tinyu.com/";
where=Request.form("wheretogo")
Select Case where
case "fun"
response.redirect thisurl & "/fun/default.asp"
case "news"
response.redirect thisurl & "/news/default.asp"
case "sample"
response.redirect thisurl & "/sample/default.asp"
End Select
%>
</body>
<html>
这个例子当用户选择了以后,按"Jump"按钮提交表单,服务器接到申请后调用formjump.asp判断后定位到相应的URL。不过这里有一点要注意,HTTP标题已经写入到客户浏览器,任何HTTP标题的修改必须在写入页内容之前,遇到这种问题时,可以如下做:
在文件的开始<@ Language=..>后写:
Response.Buffer=True
在结尾定:
Response.Flush
这里Flush是Response的一个方法,它必须是Buffer属性设置为True时才能使用,否则会产生一个运行模式错误。另外一个Clear方法也是用于清除被缓存的页面,同样要Buffer属性设置为True时才能使用。
5、End方法
该方法用于告知Active Server当遇到该方法时停止处理ASP文件。如果Response对象的Buffer属性设置为True,这时End方法即把缓存中的内容发送到客户并清除冲区。所以要取消所有向客户的输出民,可以先清除缓冲区,然后利用End方法。如:
<%
Response.buffer=true
On error resume next
Err.clear
if Err.number<>0 then
Response.Clear
Response.End
end if
%>
Server 对象:
Server 对象提供对服务器上的方法和属性的访问。其中大多数方法和属性是作为实用程序的功能服务的。
语法
Server.property|method
属性
ScriptTimeout:
ScriptTimeout 属性指定脚本在结束前最大可运行多长时间。 当处理服务器组件时,超时限制将不再生效。
语法 Server.ScriptTimeout = NumSeconds
参数 NumSeconds
指定脚本在被服务器结束前最大可运行的秒数。默认值为 90 秒。
注释
通过使用元数据库中的AspScriptTimeout属性可以为 Web 服务或 Web 服务器设置缺省的ScriptTimeout值。ScriptTimeout属性不能设置为小于在元数据库中指定的值。例如,如果NumSeconds设置为10,而元数据库设置包含了默认值90秒,则脚本在90秒后超时。但如果NumSeconds设置为100,则脚本在100秒后超时。
关于使用元数据库的详细信息,参阅 关于元数据库。
示例 以下示例中,如果服务器处理脚本超过 100 秒,将使之超时。
<% Server.ScriptTimeout = 100 %>
以下示例获取 ScriptTimeout 属性当前值,并将其存储在变量 TimeOut 中。
<% TimeOut = Server.ScriptTimeout %>
方法
CreateObject
CreateObject 方法创建服务器组件的实例。如果该组件执行了 OnStartPage 和 OnEndPage 方法,则此时就会调用 OnStartPage 方法。有关服务器组件的详细信息,请参阅 可安装的 ASP 组件 。
语法 Server.CreateObject( progID )
参数 progID 指定要创建的对象的类型。progID 的格式为 [Vendor.] component[.Version]。
注释 默认情况下,由 Server.CreateObject 方法创建的对象具有页作用域。这就是说,再当前 ASP 页处理完成之后,服务器将自动破坏这些对象。要创建有会话或应用程序作用域的对象,可以使用 <OBJECT> 标记并设置 SESSION 或 APPLICATION 的 SCOPE 属性,也可以在对话及应用程序变量中存储该对象。
例如,在如下所示的脚本中,当 Session 对象被破坏,即当对话超时时或 Abandon 方法被调用时,存储在会话变量中的对象也将被破坏。
<% Set Session("ad") = Server.CreateObject("MSWC.AdRotator")%>
可以通过将变量设置为 Nothing 或新的值来破坏对象,如下所示。第一个例子释放 ad 对象,第二个例子用字串代替 ad 。
<% Session ("ad") = Nothing %>
<% Session ("ad") = " Other Valum " %>
不能创建与内建对象同名的对象实例。 例如,下列脚本将返回错误。
<% Set Response = Server.CreateObject("Response") %>
示例 <% Set MyAd = Server.CreateObject("MSWC.AdRotator") %>
上面的例子创建一个名为 MyAd 的 MSWC.AdRotator 服务器组件,MSWC.AdRotator 组件可用于在 Web 页上的自动轮换广告。
关于服务器组件的详细信息, 请参阅 Creating Components for ASP.
HTMLEncode HTMLEncode方法对指定的字符串应用 HTML 编码。
语法 Server.HTMLEncode( string )
参数 string 指定要编码的字符串。
示例脚本 <%= Server.HTMLEncode("The paragraph tag: <P>") %>
输出 The paragraph tag: <P>
注意 以上输出将被 Web 浏览器显示为The paragraph tag: <P>如果查看一下源文件或以文本方式打开一个 Web 页,您就可以看到已编码的 HTML。
MapPath
MapPath 方法将指定的相对或虚拟路径映射到服务器上相应的物理目录上。
语法
Server.MapPath( Path )
参数
Path
指定要映射物理目录的相对或虚拟路径。若 Path 以一个正斜杠 (/) 或反斜杠 (\) 开始,则 MapPath 方法返回路径时将 Path 视为完整的虚拟路径。若 Path 不是以斜杠开始,则 MapPath 方法返回同 .asp 文件中已有的路径相对的路径。
注释
MapPath 方法不支持相对路径语法 (.) 或 (..)。例如,下列相对路径 ../MyDir/MyFile.txt 返回一个错误。
MapPath 方法不检查返回的路径是否正确或在服务器上是否存在。
因为 MapPath 方法只映射路径而不管指定的目录是否存在,所以,您可以先用 MapPath 方法映射物理目录结构的路径,然后将其传递给在服务器上创建指定目录或文件的组件。
示例
对于下列示例,文件data.txt和包含下列脚本的test.asp文件都位于目录C:\Inetpub\Wwwroot\Script下。C:\Inetpub\Wwwroot目录被设置为服务器的宿主目录。
下列示例使用服务器变量 PATH_INFO 映射当前文件的物理路径。脚本
<%= server.mappath(Request.ServerVariables("PATH_INFO"))%><BR>
输出
c:\inetpub\wwwroot\script\test.asp<BR>
由于下列示例中的路径参数不是以斜杠字符开始的,所以它们被相对映射到当前目录,此处是 C:\Inetpub\Wwwroot\Script。脚本
<%= server.mappath("data.txt")%><BR>
<%= server.mappath("script/data.txt")%><BR>
输出
c:\inetpub\wwwroot\script\data.txt<BR>
c:\inetpub\wwwroot\script\script\data.txt<BR>
接下来的两个示例使用斜杠字符指定返回的路径应被视为在服务器的完整虚拟路径。脚本
<%= server.mappath("/script/data.txt")%><BR>
<%= server.mappath("\script")%><BR>
输出
c:\inetpub\script\data.txt<BR>
c:\inetpub\script<BR>
下列示例演示如何使用正斜杠 (/) 或反斜杠 (\) 返回宿主目录的物理路径。脚本
<%= server.mappath("/")%><BR>
<%= server.mappath("\")%><BR>
输出
c:\inetpub\wwwroot<BR>
c:\inetpub\wwwroot<BR>
URLEncode
URLEncode 方法将 URL 编码规则,包括转义字符,应用到指定的字符串。
语法
Server.URLEncode( string )
参数
String 指定要编码的字符串。
示例
脚本 <%Response.Write(Server.URLEncode("http://www.tinyu.com";)) %>
输出 http%3A%2F%2Fwww%2Etinyu%2Ecom
Session对象
Session其实指的就是访问者从到达某个特定主页到离开为止的那段时间。每一访问者都会单独获得一个Session。在Web应用程序中,当一个用户访问该应用时,Session类型的变量可以供这个用户在该Web应用的所有页面中共享数据;如果另一个用户也同时访问该Web应用,他也拥有自己的Session变量,但两个用户之间无法通过Session变量共享信息,而Application类型的变更则可以实现站点多个用户之间在所有页面中共享信息。
1、SessionID属性
该属性返回当前会话的唯一标志,为每一个Session分配不同的编号。
我曾在开发过程中就遇到对用户的控制问题。它要实现的功能就是,针对某个网站的一个模块,当一个会员登录后正在看此模块时,另一个人用同样的会员名登录,就不能浏览这个模块。也就是说一个会员名同时只能一个人浏览此模块。我通过用会员名(假设为UserID,唯一)和SessionID来实现了控制。当会员登录时,给这个会员一个Session记录登录状态如:Session("Status")="Logged",同时把这个会员的Session.SessionID写入数据库。当他要浏览此模块时,先判断其是否登录,若已经登录再判断它的SessionID是否与数据库记录的相同,如果不同则不能访问。这样,当另一个用户用相同的会员名登录时,那么数据库中记录的就是新的SessionID,前者访问此模块时就不能通过检查。这就实现了一个会员名同时只能一个人浏览某个模块。这个功能在一些收费网站有很有特别作用,它防止了一个会员名给多个人浏览的问题,为公司保障了利益。
2、TimeOut属性
该属性用来定义用户Session对象的时限。如果用户在规定的时间内没有刷新网页,则Session对象就会终止。一般默认为20分钟。
3、Abandon方法
该方法是Session对象的唯一方法,可以清除Session对象,用来消除用户的Session对象并释放其所占的资源。如: <% Session.Abandon %>
4、Session_OnStart和Session_OnEnd事件
和Application一样,当对象的例程每一次启动时触发Session_OnStart事件,然后运行Session_Onstart事件的处理过程。也就是说,当服务器接收到应用程序中的URL的HTTP请求时,触发此事件,并建立一个Session对象。同理,这个事件也必须定在Global.asa文件中。
当调用Session.Abandon方法时或者在TimeOut的时间内没有刷新,这会触发Session_OnEnd事件,然后执行里面的脚本。Session变量与特定的用户相联系,针对某一个用户赋值的Session变量是和其他用户的Session变量完全独立的,不会存在相互影响。
Session应用一列:
与Application一样,一个被定义为Session类型的数组只能将整个数组作为一个对象,用户不能直接改变Session数组中某个元素的值。为了创建一个Session数组,需先定义一个普通的数组,并对它的每一个元素赋初值,最后把它定义为一个Session数组。如:
<%
dim array()
array=array("李","明","男")
Session("info")=array
Response.write Session("info")(0) &"-"
Response.write Session("info")(1) &"-"
Response.write Session("info")(2) &"<br>"
%>
<hr>
<%
array(0)="天"
array(1)="宇"
array(2)="男"
Session("info")=array
Response.write Session("info")(0) & "-"
Response.write Session("info")(1) & "-"
Response.write Session("info")(2) & "<br>"
%>
2000年7月25日星期二
Tomcat ユーザ認証
■BASIC 認証
実行環境
JDK 5.0
eclipse 3.2
Tomcat 5.5
Windows XP
J2EE Webアプリケーションでは、安全なアプリケーション設計を行うため、任意のページにセキュリティ制約条件をつけて安全性を高めることができます。
セキュリティ制約条件がつけられたページにアクセスがあった場合、パスワードによるログイン認証を行うことができます。
ログイン認証には、BASIC 認証、DIGEST 認証、FORM 認証、CLIENT-CERT 認証があります。
◆BASIC 認証 : 最も一般的な認証方式。ユーザー名とパスワードをクリアテキストでサーバーに送信する。(パスワードは Base64 エンコード)
◆DIGEST 認証 : パスワードを暗号化して送信する。
◆FORM 認証 : ログインページなどをHTML形式でカスタマイズできる。ユーザー名とパスワードをクリアテキストでサーバーに送信する。
◆CLIENT-CERT 認証 : HTTPS(HTTP over SSL)を使用した認証システム。
ここでは、BASIC 認証を用いて、「secure」フォルダ以下のファイルにセキュリティ制約条件をつけた例をレポート。コンテナは Tomcat を使用し、eclipse + tomcatPlugin を使用して作成します。
ディレクトリ構成は下記のようになります。
web.xml に security-constraint タグと login-config タグを下記のように追加記述します。
security-constraint の子要素
Tomcat 5.5\conf にある tomcat-users.xml を編集します。ここでは、新しいロール名「test」、ユーザー名「test」、パスワード「test」を下記のように定義します。パスワードは仮のものですので、適切に変更してください。
http://localhost:8080/SrvSample/secure/menu.html にアクセスしてみます。
ユーザー名とパスワードを入力して「OK」を押すと menu.html にアクセスできます。
■FORM 認証
実行環境
JDK 5.0
eclipse 3.2
Tomcat 5.5
Windows XP
FORM 認証のは BASIC 認証とよく似ています。FORM 認証では、ログインフォームをHTML形式でカスタマイズできます。また、ログインに失敗した場合に転送するエラーページもカスタマイズできます。
ここでは、ログインページを login.html、エラーページを error.html とし、前の BASIC 認証を FORM 認証に書き換えてみます。
ディレクトリ構成は下記のようになります。
BASIC 認証で記述した web.xml の login-config タグの部分を下記のように書き換えます。
login.html を記述します。送信先は「j_security_check」、ユーザー名は「j_username」、パスワードは「j_password」と設定します。これはユーザー名とパスワードが「j_security_check」という予約された場所に送られることを意味しています。変更することはできません。
Tomcat を再起動して、http://localhost:8080/SrvSample/secure/menu.html にアクセス。
■JDBCRealm にる認証
実行環境
JDK 5.0
Tomcat 5.5
MySQL 5.0
Windows XP
BASIC 認証や FORM 認証は、Tomcat のデフォルトの UserDatabaseRealm で行います。UserDatabaseRealm では、ユーザー名、パスワード、ロール名を tomcat-users.xml から読み込んで使用しますが、ユーザーが多数になると管理が困難になるでしょう。
JDBCRealm を使うとデータベースにユーザー名、パスワード、ロール名を登録して管理することができます。ユーザー数の拡大に対応しやすくなるでしょう。
☆server.xml の設定
JDBCRealm 方式を使用するには Tomcat の server.xml Engine タグ内の150行目あたりを下記のように書き換えます。ここではデータベースに MySQL を使用し、データベース名「authority」、データベースユーザー名「authority」、パスワード「authority」と設定します。
☆データベースの設定
MySQL にデータベース名「authority」、ユーザー名「authority」、パスワード「authority」を設定します。
SQL文
grant all privileges on authority.* to authority@localhost identified by 'authority';
flush privileges;
create database authority;
「users」テーブルと「user_roles」テーブルを設定します。
ユーザー名「test」、パスワード「test」、ロール名「test」を登録します。
JDBCRealm 方式に切り替えると、tomcat-users.xml にデフォルトで記述してある「tomcat」、「admin」などのユーザー名とロール名もデータベースに登録し直す必要があります。
設定はこれで完了です。
実行環境
JDK 5.0
eclipse 3.2
Tomcat 5.5
Windows XP
J2EE Webアプリケーションでは、安全なアプリケーション設計を行うため、任意のページにセキュリティ制約条件をつけて安全性を高めることができます。
セキュリティ制約条件がつけられたページにアクセスがあった場合、パスワードによるログイン認証を行うことができます。
ログイン認証には、BASIC 認証、DIGEST 認証、FORM 認証、CLIENT-CERT 認証があります。
◆BASIC 認証 : 最も一般的な認証方式。ユーザー名とパスワードをクリアテキストでサーバーに送信する。(パスワードは Base64 エンコード)
◆DIGEST 認証 : パスワードを暗号化して送信する。
◆FORM 認証 : ログインページなどをHTML形式でカスタマイズできる。ユーザー名とパスワードをクリアテキストでサーバーに送信する。
◆CLIENT-CERT 認証 : HTTPS(HTTP over SSL)を使用した認証システム。
ここでは、BASIC 認証を用いて、「secure」フォルダ以下のファイルにセキュリティ制約条件をつけた例をレポート。コンテナは Tomcat を使用し、eclipse + tomcatPlugin を使用して作成します。
ディレクトリ構成は下記のようになります。
web.xml に security-constraint タグと login-config タグを下記のように追加記述します。
security-constraint の子要素
Tomcat 5.5\conf にある tomcat-users.xml を編集します。ここでは、新しいロール名「test」、ユーザー名「test」、パスワード「test」を下記のように定義します。パスワードは仮のものですので、適切に変更してください。
http://localhost:8080/SrvSample/secure/menu.html にアクセスしてみます。
ユーザー名とパスワードを入力して「OK」を押すと menu.html にアクセスできます。
■FORM 認証
実行環境
JDK 5.0
eclipse 3.2
Tomcat 5.5
Windows XP
FORM 認証のは BASIC 認証とよく似ています。FORM 認証では、ログインフォームをHTML形式でカスタマイズできます。また、ログインに失敗した場合に転送するエラーページもカスタマイズできます。
ここでは、ログインページを login.html、エラーページを error.html とし、前の BASIC 認証を FORM 認証に書き換えてみます。
ディレクトリ構成は下記のようになります。
BASIC 認証で記述した web.xml の login-config タグの部分を下記のように書き換えます。
login.html を記述します。送信先は「j_security_check」、ユーザー名は「j_username」、パスワードは「j_password」と設定します。これはユーザー名とパスワードが「j_security_check」という予約された場所に送られることを意味しています。変更することはできません。
Tomcat を再起動して、http://localhost:8080/SrvSample/secure/menu.html にアクセス。
■JDBCRealm にる認証
実行環境
JDK 5.0
Tomcat 5.5
MySQL 5.0
Windows XP
BASIC 認証や FORM 認証は、Tomcat のデフォルトの UserDatabaseRealm で行います。UserDatabaseRealm では、ユーザー名、パスワード、ロール名を tomcat-users.xml から読み込んで使用しますが、ユーザーが多数になると管理が困難になるでしょう。
JDBCRealm を使うとデータベースにユーザー名、パスワード、ロール名を登録して管理することができます。ユーザー数の拡大に対応しやすくなるでしょう。
☆server.xml の設定
JDBCRealm 方式を使用するには Tomcat の server.xml Engine タグ内の150行目あたりを下記のように書き換えます。ここではデータベースに MySQL を使用し、データベース名「authority」、データベースユーザー名「authority」、パスワード「authority」と設定します。
☆データベースの設定
MySQL にデータベース名「authority」、ユーザー名「authority」、パスワード「authority」を設定します。
SQL文
grant all privileges on authority.* to authority@localhost identified by 'authority';
flush privileges;
create database authority;
「users」テーブルと「user_roles」テーブルを設定します。
ユーザー名「test」、パスワード「test」、ロール名「test」を登録します。
JDBCRealm 方式に切り替えると、tomcat-users.xml にデフォルトで記述してある「tomcat」、「admin」などのユーザー名とロール名もデータベースに登録し直す必要があります。
設定はこれで完了です。
订阅:
博文 (Atom)