??xml version="1.0" encoding="utf-8" standalone="yes"?>
1),DBConnectionPool.java 数据库连接池c?br />
2),DBConnectionManager .java 数据库管理类
3),DSConfigBean .java 单个数据库连接信息Bean
4),ParseDSConfig.java 操作?q个'?包括不同的数据库和同一U数据库有多个数据库)
数据 配置文gxml
5),ds.config.xml 数据库配|文件xml
原代码如?
DBConnectionPool.java
----------------------------------------------------------
/**
* 数据库连接池c?br />
*/
package com.chunkyo.db;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Timer;
* @author chenyanlin
*
*/
public class DBConnectionPool implements TimerListener {
private int inUsed=0; //使用的连接数
private ArrayList freeConnections = new ArrayList();//容器Q空闲连?br />
private int minConn; //最连接数
private int maxConn; //最大连?br />
private String name; //q接池名?br />
private String password; //密码
private String url; //数据库连接地址
private String driver; //驱动
private String user; //用户?br />
public Timer timer; //定时
/**
*
*/
public DBConnectionPool() {
// TODO Auto-generated constructor stub
}
/**
* 创徏q接?br />
* @param driver
* @param name
* @param URL
* @param user
* @param password
* @param maxConn
*/
public DBConnectionPool(String name, String driver,String URL, String user, String password, int maxConn)
{
this.name=name;
this.driver=driver;
this.url=URL;
this.user=user;
this.password=password;
this.maxConn=maxConn;
}
/**
* 用完Q释放连?br />
* @param con
*/
public synchronized void freeConnection(Connection con)
{
this.freeConnections.add(con);//d到空闲连接的末尾
this.inUsed--;
}
/**
* timeout Ҏtimeout得到q接
* @param timeout
* @return
*/
public synchronized Connection getConnection(long timeout)
{
Connection con=null;
if(this.freeConnections.size()>0)
{
con=(Connection)this.freeConnections.get(0);
if(con==null)con=getConnection(timeout); //l箋获得q接
}
else
{
con=newConnection(); //新徏q接
}
if(this.maxConn==0||this.maxConn<this.inUsed)
{
con=null;//辑ֈ最大连接数Q暂时不能获得连接了?br />
}
if(con!=null)
{
this.inUsed++;
}
return con;
}
/**
*
* 从连接池里得到连?br />
* @return
*/
public synchronized Connection getConnection()
{
Connection con=null;
if(this.freeConnections.size()>0)
{
con=(Connection)this.freeConnections.get(0);
this.freeConnections.remove(0);//如果q接分配出去了,׃I闲q接里删?br />
if(con==null)con=getConnection(); //l箋获得q接
}
else
{
con=newConnection(); //新徏q接
}
if(this.maxConn==0||this.maxConn<this.inUsed)
{
con=null;//{待 过最大连接时
}
if(con!=null)
{
this.inUsed++;
System.out.println("得到 "+this.name+" 的连接,现有"+inUsed+"个连接在使用!");
}
return con;
}
/**
*释放全部q接
*
*/
public synchronized void release()
{
Iterator allConns=this.freeConnections.iterator();
while(allConns.hasNext())
{
Connection con=(Connection)allConns.next();
try
{
con.close();
}
catch(SQLException e)
{
e.printStackTrace();
}
}
this.freeConnections.clear();
}
/**
* 创徏新连?br />
* @return
*/
private Connection newConnection()
{
try {
Class.forName(driver);
con=DriverManager.getConnection(url, user, password);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("sorry can't find db driver!");
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
System.out.println("sorry can't create Connection!");
}
return con;
}
/**
* 定时处理函数
*/
public synchronized void TimerEvent()
{
//暂时q没有实C后会加上?br />
}
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
/**
* @return the driver
*/
public String getDriver() {
return driver;
}
/**
* @param driver the driver to set
*/
public void setDriver(String driver) {
this.driver = driver;
}
/**
* @return the maxConn
*/
public int getMaxConn() {
return maxConn;
}
/**
* @param maxConn the maxConn to set
*/
public void setMaxConn(int maxConn) {
this.maxConn = maxConn;
}
/**
* @return the minConn
*/
public int getMinConn() {
return minConn;
}
/**
* @param minConn the minConn to set
*/
public void setMinConn(int minConn) {
this.minConn = minConn;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the password
*/
public String getPassword() {
return password;
}
/**
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @return the url
*/
public String getUrl() {
return url;
}
/**
* @param url the url to set
*/
public void setUrl(String url) {
this.url = url;
}
/**
* @return the user
*/
public String getUser() {
return user;
}
/**
* @param user the user to set
*/
public void setUser(String user) {
this.user = user;
}
DBConnectionManager .java
------------------------------------------
/**
* 数据库连接池理c?br />
*/
package com.chunkyo.db;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;
import com.chunkyo.db.DSConfigBean;
import com.chunkyo.db.DBConnectionPool;
* @author chenyanlin
*
*/
public class DBConnectionManager {
private Hashtable pools=new Hashtable();//q接?br />
/**
* 实例化管理类
*/
public DBConnectionManager() {
// TODO Auto-generated constructor stub
this.init();
}
/**
* 得到唯一实例理c?br />
* @return
*/
static synchronized public DBConnectionManager getInstance()
{
if(instance==null)
{
instance=new DBConnectionManager();
}
return instance;
}
/**
* 释放q接
* @param name
* @param con
*/
public void freeConnection(String name, Connection con)
{
DBConnectionPool pool=(DBConnectionPool)pools.get(name);//Ҏ关键名字得到q接?br />
if(pool!=null)
pool.freeConnection(con);//释放q接
}
/**
* 得到一个连接根据连接池的名字name
* @param name
* @return
*/
public Connection getConnection(String name)
{
DBConnectionPool pool=null;
Connection con=null;
pool=(DBConnectionPool)pools.get(name);//从名字中获取q接?br />
con=pool.getConnection();//从选定的连接池中获得连?br />
if(con!=null)
System.out.println("得到q接。。?);
return con;
}
/**
* 得到一个连接,Ҏq接池的名字和等待时?br />
* @param name
* @param time
* @return
*/
public Connection getConnection(String name, long timeout)
{
DBConnectionPool pool=null;
Connection con=null;
pool=(DBConnectionPool)pools.get(name);//从名字中获取q接?br />
con=pool.getConnection(timeout);//从选定的连接池中获得连?br />
System.out.println("得到q接。。?);
return con;
}
/**
* 释放所有连?br />
*/
public synchronized void release()
{
Enumeration allpools=pools.elements();
while(allpools.hasMoreElements())
{
DBConnectionPool pool=(DBConnectionPool)allpools.nextElement();
if(pool!=null)pool.release();
}
pools.clear();
}
* 创徏q接?br />
* @param props
*/
private void createPools(DSConfigBean dsb)
{
DBConnectionPool dbpool=new DBConnectionPool();
dbpool.setName(dsb.getName());
dbpool.setDriver(dsb.getDriver());
dbpool.setUrl(dsb.getUrl());
dbpool.setUser(dsb.getUsername());
dbpool.setPassword(dsb.getPassword());
dbpool.setMaxConn(dsb.getMaxconn());
System.out.println("ioio:"+dsb.getMaxconn());
pools.put(dsb.getName(), dbpool);
}
/**
* 初始化连接池的参?br />
*/
private void init()
{
//加蝲驱动E序
this.loadDrivers();
//创徏q接?br />
Iterator alldriver=drivers.iterator();
while(alldriver.hasNext())
{
this.createPools((DSConfigBean)alldriver.next());
System.out.println("创徏q接池。。?);
}
System.out.println("创徏q接池完毕。。?);
}
* 加蝲驱动E序
* @param props
*/
private void loadDrivers()
{
ParseDSConfig pd=new ParseDSConfig();
//d数据库配|文?br />
drivers=pd.readConfigInfo("ds.config.xml");
System.out.println("加蝲驱动E序。。?);
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
----------------------------------------
DSConfigBean.java
----------------------------------------
/**
* 配置文gBeanc?br />
*/
package com.chunkyo.db;
* @author chenyanlin
*
*/
public class DSConfigBean {
private String username =""; //用户?br />
private String password =""; //密码
private int maxconn =0; //最大连接数
/**
*
*/
public DSConfigBean() {
// TODO Auto-generated constructor stub
}
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
* @return the driver
*/
public String getDriver() {
return driver;
}
* @param driver the driver to set
*/
public void setDriver(String driver) {
this.driver = driver;
}
* @return the maxconn
*/
public int getMaxconn() {
return maxconn;
}
* @param maxconn the maxconn to set
*/
public void setMaxconn(int maxconn) {
this.maxconn = maxconn;
}
* @return the name
*/
public String getName() {
return name;
}
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
* @return the password
*/
public String getPassword() {
return password;
}
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
* @return the type
*/
public String getType() {
return type;
}
* @param type the type to set
*/
public void setType(String type) {
this.type = type;
}
* @return the url
*/
public String getUrl() {
return url;
}
* @param url the url to set
*/
public void setUrl(String url) {
this.url = url;
}
* @return the username
*/
public String getUsername() {
return username;
}
* @param username the username to set
*/
public void setUsername(String username) {
this.username = username;
}
-----------------------------------------------------
ParseDSConfig.java
-----------------------------------------------------
/**
* 操作配置文gc??nbsp; ?修改 删除{操?
*/
package com.chunkyo.db;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Vector;
import java.util.Iterator;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
* @author chenyanlin
*
*/
public class ParseDSConfig {
* 构造函?br />
*/
public ParseDSConfig() {
// TODO Auto-generated constructor stub
}
/**
* dxml配置文g
* @param path
* @return
*/
public Vector readConfigInfo(String path)
{
String rpath=this.getClass().getResource("").getPath().substring(1)+path;
Vector dsConfig=null;
FileInputStream fi = null;
try
{
fi=new FileInputStream(rpath);//d路径文g
dsConfig=new Vector();
SAXBuilder sb=new SAXBuilder();
Document doc=sb.build(fi);
Element root=doc.getRootElement();
List pools=root.getChildren();
Element pool=null;
Iterator allPool=pools.iterator();
while(allPool.hasNext())
{
pool=(Element)allPool.next();
DSConfigBean dscBean=new DSConfigBean();
dscBean.setType(pool.getChild("type").getText());
dscBean.setName(pool.getChild("name").getText());
System.out.println(dscBean.getName());
dscBean.setDriver(pool.getChild("driver").getText());
dscBean.setUrl(pool.getChild("url").getText());
dscBean.setUsername(pool.getChild("username").getText());
dscBean.setPassword(pool.getChild("password").getText());
dscBean.setMaxconn(Integer.parseInt(pool.getChild("maxconn").getText()));
dsConfig.add(dscBean);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
try {
fi.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return dsConfig;
}
*修改配置文g 没时间写 q段旉再脓上去 其实一L
*/
public void modifyConfigInfo(String path,DSConfigBean dsb) throws Exception
{
String rpath=this.getClass().getResource("").getPath().substring(1)+path;
FileInputStream fi=null; //d
FileOutputStream fo=null; //写入
}
/**
*增加配置文g
*
*/
public void addConfigInfo(String path,DSConfigBean dsb)
{
String rpath=this.getClass().getResource("").getPath().substring(1)+path;
FileInputStream fi=null;
FileOutputStream fo=null;
try
{
fi=new FileInputStream(rpath);//dxml?br />
SAXBuilder sb=new SAXBuilder();
Document doc=sb.build(fi); //得到xml
Element root=doc.getRootElement();
List pools=root.getChildren();//得到xml子树
Element newpool=new Element("pool"); //创徏新连接池
Element pooltype=new Element("type"); //讄q接池类?br />
pooltype.setText(dsb.getType());
newpool.addContent(pooltype);
Element poolname=new Element("name");//讄q接池名?br />
poolname.setText(dsb.getName());
newpool.addContent(poolname);
Element pooldriver=new Element("driver"); //讄q接池驱?br />
pooldriver.addContent(dsb.getDriver());
newpool.addContent(pooldriver);
Element poolurl=new Element("url");//讄q接池url
poolurl.setText(dsb.getUrl());
newpool.addContent(poolurl);
Element poolusername=new Element("username");//讄q接池用户名
poolusername.setText(dsb.getUsername());
newpool.addContent(poolusername);
Element poolpassword=new Element("password");//讄q接池密?br />
poolpassword.setText(dsb.getPassword());
newpool.addContent(poolpassword);
Element poolmaxconn=new Element("maxconn");//讄q接池最大连?br />
poolmaxconn.setText(String.valueOf(dsb.getMaxconn()));
newpool.addContent(poolmaxconn);
pools.add(newpool);//childd到root
Format format = Format.getPrettyFormat();
format.setIndent("");
format.setEncoding("utf-8");
XMLOutputter outp = new XMLOutputter(format);
fo = new FileOutputStream(rpath);
outp.output(doc, fo);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
}
}
/**
*删除配置文g
*/
public void delConfigInfo(String path,String name)
{
String rpath=this.getClass().getResource("").getPath().substring(1)+path;
FileInputStream fi = null;
FileOutputStream fo=null;
try
{
fi=new FileInputStream(rpath);//d路径文g
SAXBuilder sb=new SAXBuilder();
Document doc=sb.build(fi);
Element root=doc.getRootElement();
List pools=root.getChildren();
Element pool=null;
Iterator allPool=pools.iterator();
while(allPool.hasNext())
{
pool=(Element)allPool.next();
if(pool.getChild("name").getText().equals(name))
{
pools.remove(pool);
break;
}
}
Format format = Format.getPrettyFormat();
format.setIndent("");
format.setEncoding("utf-8");
XMLOutputter outp = new XMLOutputter(format);
fo = new FileOutputStream(rpath);
outp.output(doc, fo);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
try {
fi.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String path="ds.config.xml";
pd.readConfigInfo(path);
//pd.delConfigInfo(path, "tj012006");
DSConfigBean dsb=new DSConfigBean();
dsb.setType("oracle");
dsb.setName("yyy004");
dsb.setDriver("org.oracle.jdbc");
dsb.setUrl("jdbc:oracle://localhost");
dsb.setUsername("sa");
dsb.setPassword("");
dsb.setMaxconn(1000);
pd.addConfigInfo(path, dsb);
pd.delConfigInfo(path, "yyy001");
}
--------------------------------------
ds.config.xml 配置文g
--------------------------------------
<ds-config>
<pool>
<type>mysql</type>
<name>user</name>
<driver>com.mysql.jdbc.driver</driver>
<url>jdbc:mysql://localhost:3306/user</url>
<username>sa</username>
<password>123456</password>
<maxconn>100</maxconn>
</pool>
<pool>
<type>mysql</type>
<name>user2</name>
<driver>com.mysql.jdbc.driver</driver>
<url>jdbc:mysql://localhost:3306/user2</url>
<username>sa</username>
<password>1234</password>
<maxconn>10</maxconn>
</pool>
<pool>
<type>sql2000</type>
<name>books</name>
<driver>com.microsoft.sqlserver.driver</driver>
<url>jdbc:sqlserver://localhost:1433/books:databasename=books</url>
<username>sa</username>
<password></password>
<maxconn>100</maxconn>
</pool>
</ds-config>
3. q接池的使用
1。Connection的获得和释放
DBConnectionManager connectionMan=DBConnectionManager .getInstance();//得到唯一实例
//得到q接
String name="mysql";//从上下文得到你要讉K的数据库的名?br />
Connection con=connectionMan.getConnection(name);
//使用
。。。。。。?br />
// 使用完毕
connectionMan.freeConnection(name,con);//释放Q但q未断开q接
2。数据库q接的动态增加和q接池的动态增?br />
1。调用xml操作增加c?br />
2。重新实例华q接池管理池c?br />
<?gt;
]]>
先来丑և个JAVA中用到Context的例?br />
Q?QJNDI的一个类javax.naming.InitialContextQ它dJNDI的一些配|信息,q内含对象和其在JNDI中的注册名称的映信息。请看下面的代码
RMIAdaptor server=(RMIAdaptor)ic.lookup("jmx/invoker/RMIAdaptor");
q是一DJBoss中获取MBean的远E调用类的代码。在q里面通过InitialContext中JNDI注册的名U?#8220;jmx/invoker/RMIAdaptor”来获得RMIAdaptor
对象。这和JAVA集合中的MAP有点象,有一个String的keyQkeyҎ着它的对象?br />
Q?Q再来看看下面Spring中最常见的几句代码?span style="color: #000000">ApplicationContext 是内?/font>configuration.xml配置文g的信息,使得可以通过getBean用名U得到相应的注册对象?/font>
Object obj= ctx.getBean("Object_Name");
从上面的代码Q我很能体会到Context所代表的意义:公用信息、环境、容?...。所以我觉得Context译成上下文q不直观Q按照语a使用的环境,译?#8220;环境”?#8220;容器”可能更好?nbsp; (?
]]>
1. 概述
随着web应用的增多,新的模式解决Ҏ中以web为核心的应用也越来越多, 很多公司各种应用的架构都?span lang="EN-US">B/S?span lang="EN-US">web应用ZQ但是有?span lang="EN-US">WEB试斚w的内容ƈ没有相应的ȝQ所以我在这里对web的测试方法和采用的测试技术进行ȝQ便于内部交?/font>
试Ҏ量늛webE序的各个方面,试技术方面在l承传统试技术的技术上l合web应用的特炏V?/font>
相关的测试和实现技术也有着很大的关p,׃本公怋?span lang="EN-US">J2EE体系Q也怾子中只有JAVAq_可以使用Q?span lang="EN-US">.NETq_试技术暂时不涉及Q如果你有请与我联系?/font>
2. 试Ҏ
说明Q测试方法的选择取决你的试{略?/font>
一般的web试和以往的应用程序的试的侧重点不完全相同,基本包括以下几个斚w?/font>
当然圆满的完成测试还要有好的团体和流E等的方斚w面的支持Q你同样应该对这些方面进行注意?/font>
有些试Ҏ设计C程Q哪些应该在你的试团队中徏立?
2.1 界面试
现在一般h都有使用览器浏览网늚l历Q用戯然不是专业h员但是对界面效果的印象是很重要的。如果你注重q方面的试Q那么验证应用程序是否易于用就非常重要了。很多h认ؓq是试中最不重要的部分Q但是恰恰相反界面对不懂技术的客户来说那相当关键,慢慢体会你会明白的?/font>
Ҏ上可以根据设计文档,如果够专业的话可以专业美工h员,来确定整体风格页面风|然后Ҏq个可以面人员可以生成静态的HTMLQ?span lang="EN-US">CSS{甚至生成几套不用的Ҏ来讨论,或者交l客戯审,最后Ş成统一的风格的面/框架。注意不要靠E序员的术素养形成你的web风格Q那样可能会很糟p?/font>
主要包括以下几个斚w的内容:
站点地图和导航条 位置、是否合理、是否可以导航等内容布局 布局是否合理Q滚动条{简介说?说明文字是否合理Q位|,是否正确
背景/色调 是否正确、美观,是否W合用户需求;
面在窗口中的显C是否正、美观(在调整浏览器H口大小Ӟ屏幕h是否正确Q表单样?大小Q格式,是否Ҏ交数据进行验证(如果在页面部分进行验证的话){?/font>
q接 q接的Ş式,位置Q是否易于理解等
web试的主要页面元?/font>
面元素的容错性列表(如输入框、时间列表或日历Q?/font>
面元素清单Qؓ实现功能Q是否将所需要的元素全部都列出来了,如按钮、单选框、复选框、列表框、超q接、输入框{等Q?/font>
面元素的容错性是否存?/font>
面元素的容错性是否正?/font>
面元素基本功能是否实现Q如文字Ҏ、动ȝ效、按钮、超q接Q?/font>
面元素的外形、摆放位|(如按钮、列表框、核选框、输入框、超q接{)
面元素是否昄正确Q主要针Ҏ字、图形、签章)
元素是否昄Q元素是否存在)
面元素清单Qؓ实现功能Q是否将所需要的元素全部都列出来了,如按钮、单选框、复选框、列表框、超q接、输入框{等Q?/font>
试技?/font>
通过面走查Q浏览确定用的面是否W合需求。可以结合兼Ҏ测试对不用分L率下面昄效果Q如果有影响应该交给设计人员提出解决Ҏ?/font>
可以l合数据定义文档查看表单的内容Q长度等信息?/font>
对于动态生成的面最好也能进行浏览查看。如Servelet部分可以l合~码规范Q进行代码走查。是否支持中文,如果数据?span lang="EN-US">XM 装要做的工作会多一点等{?
界面试要素:
W合标准和规?span lang="EN-US">,灉|?span lang="EN-US">,正确?span lang="EN-US">,直观?span lang="EN-US">,舒适?span lang="EN-US">,实用?span lang="EN-US">,一致?/font>
1.直观?/font>:
用户界面是否z净,不唐H?span lang="EN-US">,不拥?span lang="EN-US">.界面不应该ؓ用户刉障?span lang="EN-US">.所需功能或者期待的响应应该明显,q在预期出现的地?/font>.
界面l织和布局合理?span lang="EN-US"> 是否允许用户LC一个功能{到另一个功?span lang="EN-US"> 下一步做什么明昑 M时刻都可以决定放弃或者退?span lang="EN-US">,退出吗 输入得到承认了吗 菜单或者窗口是否深藏不?/font>
有多余功能吗 软g整体抑或局部是否做得太?span lang="EN-US"> 是否有太多特性把工作复杂化了 是否感到信息太庞?/font>
如果其他所有努力失?span lang="EN-US">,帮助pȝ真能帮忙?/font>
2.一致?/font>
快速键和菜单选项.?span lang="EN-US">Windows 中按F1键L得到帮助信息
术语和命?span lang="EN-US">.整个软g使用同样的术语吗 Ҏ命名一致吗 例如,Find是否一直叫Find,而不是有时叫Search
软g是否一直面向同一U别用户 带有花哨用户界面的趣呌卡程序不应该昄泄露技术机密的错误提示信息.
按钮位置和等L按键.大家是否注意到对话框?span lang="EN-US">OK按钮?span lang="EN-US">Canc e按钮?span lang="EN-US">,OK按钮L在上Ҏ者左?span lang="EN-US">,?span lang="EN-US">Canc e按钮L在下Ҏx 同样原因,Canc e按钮的等h键通常?span lang="EN-US">Esc,而选中按钮的等h钮通常?span lang="EN-US">Enter.保持一?/font>.
3.灉|?/font>
状态蟩?span lang="EN-US">.灉|的Y件实现同一d有多U选择方式.
状态终止和跌,h定w处理能力.
数据输入和输?span lang="EN-US">.用户希望有多U方法输入数据和查看l果.例如,在写字板插入文字可用键盘输入,_脓,?span lang="EN-US">6U文件格式读?span lang="EN-US">,作ؓ对象插入,或者用鼠标从其他程序拖?/font>.
4.舒适?/font>
恰当.软g外观和感觉应该与所做的工作和用者相W?/font>.
错误处理.E序应该在用h行严重错误的操作之前提出警告,q允许用h复由于错误操作导致丢q数据.如大家认?span lang="EN-US">undo /redo是当然的.
性能.快不见得是好?span lang="EN-US">.要让用户看得清程序在做什?span lang="EN-US">,它是有反应的.
2.2 功能试
对功能测试是试中的重点
主要包括一下几个方面的内容
q接q个q接和界面测试中的连接不同那里注重的是连接方式和位置Q如是图像还是文字放|的位置{,q是其他的方式。这里的q接注重功能。如是否有连接,q接的是否是说明的位|等?/font>
表单提交应当模拟用户提交Q验证是否完成功能,如注册信息,要测试这些程序,需要验证服务器能正保存这些数据,而且后台q行的程序能正确解释和用这些信息。还有数据正性验证,异常处理{,最好结合易用性要求等?span lang="EN-US">B/Sl构实现的功能可能主要的在q里Q提交数据,处理数据{如果有固定的操作流E可以考虑自动化测试工L录制功能Q编写可重复使用的脚本代码,可以在测试、回归测试时q行以便减轻试人员工作量?/font>
Cookies 验证 如果pȝ使用?span lang="EN-US">cookieQ测试h员需要对它们q行。如果在 cookies 中保存了注册信息Q请认?span lang="EN-US"> cookie能够正常工作而且已对q些信息已经加密。如果?span lang="EN-US"> cookie 来统计次敎ͼ需要验证次数篏计正。关?span lang="EN-US">cookie的用可以参考浏览器的帮助信息。如果?span lang="EN-US">B/Sl构cookies中存攄信息更多。功能易用性测?完成了功能测试可以对应用性进行了解,最好听听客L反映Q在可以的情况下对程序进行改q是很有必要的,和客户保持互动对pȝ满意度也是很有帮助的?/font>
试技术功能测试的试技术可是很多的Q我们可以结合实际环境选择使用
白盒试技?span lang="EN-US">(White Box Testing) 深入C码一U的试Q用这U技术发现问题最早,效果也是最好的。该技术主要的特征是测试对象进入了代码内部,Ҏ开发h员对代码和对E序的熟悉程?span lang="EN-US">,Ҏ需要的部分q行在Y件编码阶D,开发h员根据自己对代码的理解和接触所q行的Y件测试叫做白盒测试。这一阶段试以Y件开发h员ؓ主,?span lang="EN-US">JAVAq_使用Xunitpd工具q行试Q?span lang="EN-US">Xunit试工具是类一U的试工具Ҏ一个类和该cȝҎq行试?/font>
黑盒试技术(Black Box TestingQ黑盒测试的内容主要有以下几个方面,但是主要q是功能部分。主要是覆盖全部的功能,可以l合兼容Q性能试{方面进行,Ҏ软g需求,设计文档Q模拟客户场景随pȝq行实际的测试,q种试技术是使用最多的试技术涵盖了试的方斚w面,可以考虑以下斚w
正确?span lang="EN-US"> (Correctness)Q计结果,命名{方?/font>
可用?span lang="EN-US"> (Usability)Q是否可以满Y件的需求说明?/font>
边界条g (Boundary Condition)输入部分的边界?span lang="EN-US">,是使用一般书中说的等L划分,试试最大最和非法数据{等.
性能 (Performance) 正常使用的时间内pȝ完成一个Q务需要的旉,多h同时使用的时候响应时?span lang="EN-US">,在可以接受范围内.J2EE技术实现的pȝ在性能斚w更是需要照儡,一般原则是3U以下接?span lang="EN-US">,3-5U可以接?span lang="EN-US">,5U以上就影响易用性了. 如果在测试过E中发现性能问题Q修复v来是非常艰难的,因ؓq常常意味着
E序的算法不好,l构不好Q或者设计有问题。因此在产品开发的开始阶D,p考虑到Y件的性能问题
压力试 (Stress) 多用h?可以考虑使用压力试工具,压力和性能试l合hq行.如果有负载^衡的话还要在服务器端打开监测工具,查看服务?span lang="EN-US">CPU使用?span lang="EN-US">,内存占用情况,如果有必要可以模拟大量数据输?span lang="EN-US">,对硬盘的影响{等信息.如果有必要的话必进行性能优化(软硬仉可以).q里的压力测试针对的是某几项功能,.
错误恢复 (Error Recovery) 错误处理Q页面数据验?span lang="EN-US">,包括H然间断?span lang="EN-US">,输入脏数据等.
安全性测?span lang="EN-US">(Security)q个领域正在研究?span lang="EN-US">,不过防火?span lang="EN-US">,补丁?span lang="EN-US">.杀毒Y件等的就不必说了,不过可以考虑破坏性测试时L.看了一些资料后得知,q里面设计到的知?span lang="EN-US">\内容可以写本书了,不是一两句可以说清?span lang="EN-US">,特别是一些商务网?span lang="EN-US">,或者跟钱有?span lang="EN-US">,或者和公司U密有关?span lang="EN-US">web更是,需要这斚w的测?span lang="EN-US">,在外国有一U专门干q一行的人叫安全N,可以审核代码,提出安全,出现紧急事件是的处理办法等,在国内没有听说哪里有专门搞安全技术测试的内容.
兼容?span lang="EN-US"> (Compatibility) 不同览器,不同应用E序版本在实现功能时的表?span lang="EN-US">,不同的上|方?span lang="EN-US">,如果你测试的是一个公q站的?/font>.
兼容性测试内容详q?/font>
gq_
览器Y件和版本:览器插?span lang="EN-US">,览器选项,视频分L率和色深.文字大小,调制解调器速率.
软g配置 (Configuration) ?span lang="EN-US">IE览器的不用选项-安全讑֮最?span lang="EN-US">,用脚本E序,{等,你们的程序在各种不用的设|下表现如何.
单元试技?/font>(Unit Test):
2.2.1 下面是对白盒试和单元测试的区别的论q?/font>:
单元试和白盒测试是不同?span lang="EN-US">,虽然单元试和白盒测试都是关注功能虽然他们都需要代码支?span lang="EN-US">,但是U别不同,白盒试x的是cM一个方法的功能是更的单位,但是完成一个单元测试可能需?span lang="EN-US">N多类,所以说作单元测试需要什么写驱动和稳定桩,比如查询单元是一个查询包?span lang="EN-US">N多的试c?span lang="EN-US">,试数据,q行他需要提供数据的部分,输入参数和发出命令的驱动{等.是比cd的一个整体进行的.
另一个明昄区别是白盒测试不会关注类接口,但是单元试主要的内容就是类接口试.
不过很多时候是很少区分?span lang="EN-US">,因ؓq两U技术实现v来有很多怺兌的部?span lang="EN-US">.不过要看你对质量的关注程度来军_.
2.2.2 功能试边界试\界试技术详q?/font>
边界条g
边界条g是指软g计划的操作界限所在的边缘条g.
如果软g试问题包含定的边?span lang="EN-US">,那么数据cd可能?/font>:
数?速度 字符 地址 位置 寸 数量
同时,考虑q些cd的下q特?/font>:
W一?span lang="EN-US">/最后一?最?span lang="EN-US">/最大?/font>
开?span lang="EN-US">/完成 过/在内
I?span lang="EN-US">/?最?span lang="EN-US">/最?/font>
最?span lang="EN-US">/最?最?span lang="EN-US">/最q?/font>
最?span lang="EN-US">/最?最?span lang="EN-US">/最?/font>
盔R/最q?/font>
界试
通常是简单加1或者很的?span lang="EN-US">(对于最大?span lang="EN-US">)和减?span lang="EN-US">1或者很的?span lang="EN-US">(对于最?span lang="EN-US">),例如:
W一个减1/最后一个加1
开始减1/完成?/font>1
IZ再减/满了再加
慢上加慢/快上加快
最大数?span lang="EN-US">1/最数?/font>1
最值减1/最大值加1
刚好过/刚好在内
短了再短/长了再长
早了更早/晚了更晚
最高加1/最低减1
另一些该注意的输?span lang="EN-US">:默认,I白,I?span lang="EN-US">,零值和?span lang="EN-US">;非法,错误,不正和垃圾数据.
2.2.3 状态测试技?/font>
软g可能q入的每一U独立状?/font>;
从一U状态{入另一U状态所需的输入和条g;
q入或退出某U状态时的设|条件及输入l果.
具体试Ҏ可以参考如?/font>:
每种状态至访问一?/font>;
试看v来最常见最普遍的状态{?/font>;
试状态之间最不常用的分支
试所有错误状态及其返回?/font>
试随机状态{?/font>
2.2.4 竞争条g试技?/font>
竞争条g典型情Ş参考如?/font>:
两个不同的程序同时保存或打开同一个文?/font>
׃n同一台打印机,通信端口或者其他外围设?/font>
当Y件处于读取或者修改状态时按键或者单击鼠?/font>
同时关闭或者启动Y件的多个实例
同时使用不同的程序访问一个共同数据库
2.3 负蝲\压力试(StressTest)
在这里的负蝲\压力和功能测试中的不?span lang="EN-US">,他是pȝ试的内?span lang="EN-US">,是基本功能已l通过后进行的.可以在集成测试阶D?span lang="EN-US">,亦可以在pȝ试阶段q行.
使用负蝲试工具q行,虚拟一定数量的用户看一看系l的表现,是否满定义中的指标.
负蝲试一般用工具完成, oadrunnerQ?span lang="EN-US">web oadQ?span lang="EN-US">wasQ?span lang="EN-US">ew Q?span lang="EN-US">e-test{,主要的内定w是编写出试脚本Q脚本中一般包括用户一般常用的功能Q然后运行,得出报告。所以负载测试包括的主要内容׃介绍了?/font>
负蝲试技术在各种极限情况下对产品q行试 (如很多h同时使用该YӞ或者反复运行该软g)Q以查品的长期E_性。例如,使用压力试工具?span lang="EN-US">web服务器进行压力测?span lang="EN-US">. 本项试可以帮助扑ֈ一些大型的问题Q如L、崩损、内存泄漏等Q因为有些存在内存泄漏问题的E序Q在q行一两次时可能不会出现问题,但是如果q行了成千上万次Q内存泄漏得来多Q就会导致系l崩滑。用J2EE实现的系l很但是ƈ不是没有内存问题.
无论什么工具基本的技术都是利用线E技术模仿和虚拟用户Q在q里主要的难点在与测试脚本的~写Q每U工具用的脚本都不一P但是大多数工具都提供录制功能q是不会编码的试人员同样可以试?/font>
对负载工L延使用可以q行pȝE_性测试,pȝ极限试Q如使用100?span lang="EN-US"> oad Sizeq箋使用24时Q微软定义的通过准则是通过72时试的程序一般不会出现稳定性的问题?
2.4 回归试 (Regression Test)
q一D|间以后,再回q头来对以前修复q的Bug重新q行试Q看?span lang="EN-US">Bug 是否会重新出现?/font>
回归试技术可以在试的各个阶D出玎ͼ无论是单元测试还是集成测试还是系l测试。是对以前问题进行验证的q程?/font>
回归试的目的就是保证以前已l修复的Bug不会再出现。实际上Q许?span lang="EN-US">Bug都是在回归测试时发现的,在此阶段Q我们首先要查以前找到的Bug 是否已经更正了。值得注意的是Q已l更正的Bug 也可能又回来了,有的Bug l过修改之后可能又生了新的Bug。所以,回归试可保证已更正?span lang="EN-US">Bug不再重现Q不产生新的Bug?/font>
2.5 A pha ?span lang="EN-US">Beta 试 (A pha and Beta Test):
在正式发布品之前往往要先发布一些测试版Q让用户能够反馈出相关信息,或者找到存在的BugQ以便在正式版中得到解决?/font>
特别是在有客户参加的情况下,对系l进行测试可能会出现一些我们没有考虑的情况,q可以解决一些客户实际关心的问题
不同的测试技术区?/font>
3.1 覆盖试技?/font>
说明:试覆盖率可以看出测试的完成?span lang="EN-US">,在测试分析报告中可以作ؓ量化指标的依据,试覆盖率越高效果越好?/font>
覆盖试可以是程序代码的执行路径覆盖Q亦可以是功能实现的步骤覆盖Q可以理解成程囄路径覆盖Q?/font>
该技术可以用在Q何测试阶D,包括单元坏歅R集成测试、系l测试?/font>
使用该技术时可以使用以上的Q何测试方法和试技术?/font>
3.2 白盒试和黑盒测试技?/font>
白盒试技?span lang="EN-US"> (White Box Testing)该技术主要的特征是测试对象进入了代码内部,Ҏ开发h员对代码和对E序的熟悉程?span lang="EN-US">,Ҏ需要的部分q行在Y件编码阶D,开发h员根据自己对代码的理解和接触所q行的Y件测试叫做白盒测试。这一阶段试以Y件开发h员ؓ主,使用Xunitpd工具q行试Q可以包括很多方面如功能性能{?/font>
黑盒试 (Black Box Testing)试的主体部分黑盒测试的内容主要有以下几个方面,但是主要q是功能部分。主要是覆盖全部的功能,可以l合兼容Q性能试{方面进?span lang="EN-US">,包括的不同测试类型请参考以上内宏V?/font>
3.3 手工试和自动化试
手工试Q?span lang="EN-US">Manual TestingQ:即依靠h力来查找Bug。方法可以参考上边的试Q也可以Ҏ对实现技术及l验{进行不同的试?/font>
自动试Q?span lang="EN-US">Automation TestingQ用有针对工具实行。可以作动化试的计?span lang="EN-US">,对可以进行自动化试的部分编写或者录制相应的脚本,可以加入功能,定w,表单提交{?span lang="EN-US">,可以参?span lang="EN-US">MI,Rationa 或者其他类试工具说明.
Ҏ权威的Y件测试经验,手工试q是主要的测试方法,自动试不够灉|Q在q里不再详述。微软的试q程80Q还是手工完成?/font>
自动试永远也代替不了手工测试,但是手工试的工作量很大是不争的事实?/font>
3.4 ҎRUP标准按阶D区分测?/font>
单元试在上Ҏ详细的叙qͼq有针对单元试和集成测试的Q请参考?/font>
集成试分ؓ功能集成试和系l集成测试,怺有调用的功能集成Q在pȝ环境下功能相互调用的影响{,使用Ҏ可以L选用上面的内宏V注重功能方面?/font>
pȝ试在功能实现的基础上,可以加入兼容性,易用性,性能{等
验收试可以包括Alpha?span lang="EN-US">Beta试Q在q里׃再详q?
4. 存在风险及解x?/font>
说明Q测试不能找出所有的问题Q只是尽量将问题在开发阶D解军_多数的问题而已?/font>
试风险如下Q?/font>
软硬件的试环境提供上也Ҏ试结果有很大的媄响?/font>
试团队的水qIl验Q合作效果等
整个开发流E对试的重视程度,试的进入时间等
׃试环境操作pȝQ网l环境,带宽{情况可能生的试l果可能不同q是需要经验以及对试环境的保护等斚w下一些功夫?/font>
5. 软g~陷的原?/font>
软g~陷区别于Y?span lang="EN-US">bug,它是在测试过E中出现的对pȝ有媄响的,但是在设计中没有的或者对修改后的bug试和开发h员有不同意见{?/font>
软g未达C品说明书标明的功能?/font>
软g出现了品说明书指明不会出现的错误?/font>
软g功能出产品说明书指明范围?/font>
软g未达C品说明书虽未指出但应辑ֈ的目标?/font>
软g试员认Y仉以理解、不易用、运行速度~慢Q或者最l用戯Z好?
6. 文档试
产品说明书属性检查清?/font>
完整.是否有遗漏和丢失 完全?span lang="EN-US"> 单独使用是否包含全部内容
准确.既定解决Ҏ正确?span lang="EN-US"> 目标明确?span lang="EN-US"> 有没有错?/font>
_,不含p?span lang="EN-US">,清晰.描述是否一清二?span lang="EN-US"> q是自说自话 Ҏ看懂和理解吗
一?span lang="EN-US">.产品功能能描q是否自相矛?span lang="EN-US">,与其他功能有没有冲突
贴切.描述功能的陈q是否必?span lang="EN-US"> 有没有多余信?span lang="EN-US"> 功能是否原来的客戯?/font>
合理.在特定的预算和进度下,以现有h?span lang="EN-US">,物力和资源能否实?/font>
代码无关.是否坚持定义产品,而不是定义其所信赖的Y件设?span lang="EN-US">,架构和代?/font>
可测试?span lang="EN-US">.Ҏ能否测?span lang="EN-US"> 试员徏立验证操作的试E序是否提供_的信?/font>
产品说明书用语检查清?/font>
说明 寚w题的描述通常表现为粉饰没有仔l考虑的功?span lang="EN-US">----可归l于前文所q的属?span lang="EN-US">.从品说明书上找L用语,仔细审视它们在文中是怎样使用?span lang="EN-US">.产品说明书可能会为其掩饰和开?span lang="EN-US">,也可能含p其?span lang="EN-US">----无论是哪一U情况都可视Y件缺?/font>.
L,每一U?span lang="EN-US">,所?span lang="EN-US">,没有,从不.如果看到此类l对或肯定的,切实认定的叙q?span lang="EN-US">,软g试员就可以着手设计针锋相对的案例.
当然,因此,明显,昄,必然.q些话意图诱使接受假定情?span lang="EN-US">.不要中了圈套.
某些,有时,常常,通常,惯常,l常,大多,几乎.q些话太q模p?span lang="EN-US">."有时"发生作用的功能无法测?/font>.
{等,诸如此类,依此cL.以这L词结束的功能清单无法试.功能清单要绝Ҏ者解释明?span lang="EN-US">,以免让hqh,不知如何推论.
良好,q?span lang="EN-US">,廉h,高效,?span lang="EN-US">,E_.q些是不定的说?span lang="EN-US">,不可试.如果在品说明书中出?span lang="EN-US">,必进一步指明含?/font>.
已处?span lang="EN-US">,已拒l?span lang="EN-US">,已忽?span lang="EN-US">,已消?span lang="EN-US">.q些廉洁可能会隐藏大量需要说明的功能.
如果...那么...(没有否则).扑և?span lang="EN-US">"如果...那么..."而缺配套的"否则"l构的陈q?span lang="EN-US">.想一?span lang="EN-US">"如果"没有发生会怎样.
MyAnnotation0.java
MyAnnotation0Z个无MҎ和属性的annotation?br />
使用MyAnnotation0Q?br />
h一个valueҎAnnotation范例Q?/p>
MyAnnotation1.java
MyAnnotation1h一个名为value的方法?br />
MyAnnotation1使用Q?br />
TestMyAnnotation1.java
可以通过@Annotation?Ҏ?=?, Ҏ?=?, …)的Ş式给annotation赋倹{只有一个方法的时候,可以直接省略为:@Annotation??) 的赋值Ş式。当Ҏq回一个数l时Q可以用 Ҏ?{?, ?, …}l其赋倹{?br />
如果必要Q还可以在annotation里ؓ其定义属性。如下:
MyAnnotation2.java
其中QmyProperty只能x为public或无public修饰Q无public修饰时也默认为publicQؓstatic, final属性(即不写也默认ؓstatic, finalQ?br />
使用例:
TestMyAnnotation2
上例会打印出Q?/p>
本节介绍较ؓ复杂的annotation定义与用?br /> 先看代码Q?br /> MyAnnotation3.java
MyAnnotation3h一个返回String的valueҎQ返回String[]的multiValues ҎQ还有一个返回int 的numberҎ。其中numberҎh默认??br />
使用例:
TestMyAnnotation3.java
numberh默认|所以标注时可以不ؓ其赋倹{其余方法则必须通过上面介绍的方法赋倹{multiValuesq回一个String[]数组Q所以可以通过multiValues={"1", "2"}为其赋倹{?br />
RetentionPolicy的?/th> | 说明 |
---|---|
RetentionPolicy.CLASS | annotation信息被~译器编译时保存在class文g中,但执行时不会在VM装蝲。也是说不能在执行时动态取得annotation信息。未讄@Retention时这是默认讄倹{? |
RetentionPolicy.RUNTIME | annotation信息被~译器编译时保存在class文g中,执行时也会被VM装蝲? |
RetentionPolicy.SOURCE | annotation信息被~译器编译时舍弃掉? |
ElementType?/th> | 说明 |
---|---|
ElementType.ANNOTATION_TYPE | 应用于其他注解的元注?/td> |
ElementType.CONSTRUCTOR | 构造函?/td> |
ElementType.FIELD | 字段 |
ElementType.LOCAL_VARIABLE | Ҏ中的本地变量 |
ElementType.METHOD | Ҏ |
ElementType.PACKAGE | ?/td> |
ElementType.PARAMETER | Ҏ的参?/td> |
ElementType.TYPE | c,接口或者枚丑֣?/td> |
化不是功能的减少
化一个编E模型ƈ没有减少它的功能。简化只是把复杂的逻辑隐藏C框架代码或可重用的组件中M。根本上Q它是把复杂的东西从需要应用开发者直接管理的地方转移C大多数开发者看不到的地斏V?/p>
上述的模板和工具让初学者更Ҏ上手Q同时也提高了有l验的Java开发者的生力,现在它们正在被JCP合ƈC一代的Java EE标准?比如:EJB 3.0)。由Java开发h员Raghu Kodali最q所做的研究昄:Java EE的示例程序RosterApp从EJB 2.1转到EJB 3.0可以减少癑ֈ之五十以上的代码?/p>
@Stateless public class CalculatorBean implements Calculator { public double calculate (int start, int end, double growthrate, double saving) { double tmp = Math.pow(1. + growthrate / 12., 12. * (end - start) + 1); return saving * 12. * (tmp - 1) / growthrate; } } |
你也可以Z个session bean指明多个接口-一个ؓ本地客户服务Q一个ؓq程客户服务。只要用@Local和@Remote注释来区分。下面的代码片断昄了同时实C本地和远E接口的CalculatorBean。如果你没有@Local和@Remote注释Qsession bean接口默认为本地接口?br />
@Stateless @Local () @Remote () public class CalculatorBean implements Calculator, RemoteCalculator { public double calculate (int start, int end, public String getServerInfo () { |
Session bean用户通过JNDI得到bean的一个存?Stub)对象。容器所提供的存根对象实Csession bean的商业接口。所有针对存根的调用都被引向了容器,由容器调用相应的实现cM的接口。对于有状态的的session beanQ你必须自己在客L~存存根对象Q这样在每次的后l调用时Q容器才知道要提供相同的的bean实例。下面的片断昄如何调用session bean.在后面,你将会学到获取存根对象的更简单的Ҏ?/p>
InitialContext ctx = new InitialContext(); cal = (Calculator) ctx.lookup(Calculator.class.getName()); double res = cal.calculate(start, end, growthrate, saving); Session bean生命周期的管?/p> |
到藕合松散的目的Q应用把session bean实例的创建、缓存、销毁全部交lEJB 3.0容器(也就是,反向控制设计模式)。应用只和bean的商业接口打交道?/p>
但如果应用需要对session对象更好的控制呢?比如_应用可能需要在创徏session bean的时候初始化数据库联接,而在销毁bean时关闭外部的联接。上q这些,你都可能通过在beancM定义生命周期的回调方法来实现。这些方法将会被容器在生命周期的不同阶段调用(?创徏或销毁时)。通过使有下面所列的注释QEJB 3.0允许你将MҎ指定为回调方法。这不同于EJB 2.1QEJB 2.1中,所有的回调Ҏ必须实现Q即使这是空的。EJB 3.0中,bean可以有Q意数量,L名字的回调方法?/p>
@PostConstruct:当bean对象完成实例化后Q用了q个注释的方法会被立卌用。这个注释同旉用于有状态和无状态的session bean?/p>
@PreDestroy:使用q个注释的方法会在容器从它的对象池中销毁一个无用的或者过期的bean实例q前调用。同旉用于有状态和无状态的session bean.
@PrePassivate:当一个有状态的session bean实例I闲q长的时_容器会钝化它,q把它的状态保存下来。用这个注释的Ҏ会在容器钝化bean实例之前调用。适用于有状态session bean?/p>
@PostActivate:当客L再次使用已经被钝化的的有状态session beanӞ新的实例被创建,状态被恢复。用此注释的session bean会在bean的激zd成时调用?/p>
@Init:q个注释指定了有状态session bean初始化的Ҏ。它区别于@PostConstruct注释在于:多个@Init注释Ҏ可以同时存在于有状态session bean 中,但每个bean实例只会有一个@Init注释的方法会被调用。这取决于bean是如何创建的(l节LEJB 3.0规范)。@PostConstruct在@Init之后被调用?/p>
另一个有用的生命周期Ҏ注释是@RemoveQ特别是对于有状态session bean。当应用通过存根对象调用使用了@Remove注释的方法时Q容器就知道在该Ҏ执行完毕后,要把bean实例从对象池中移走?br />
@Stateful public class CalculatorBean implements Calculator, Serializable { // ... ... |
消息驱动bean
Session bean服务提供了同步调用的Ҏ。另一个重要的藕合松散服务cd是一U通过q入的消息来触发的异步服?比如:email或Java消息服务产生的消?。EJB 3.0的消息驱动bean(MDB)是设计用来专门处理基于消息请求的lg?/p>
一个MDBcdd现MessageListener接口。当容器到bean守候的队列一条消息时Q就调用onMessage()ҎQ将消息作ؓ参数传入。MDB在OnMessage()中决定如何处理该消息。你可以用注释来配置MDB侦听哪一条队列。当MDB部vӞ容器会用到其中的注释信息。在下面的例子中QCalculatorBean MDB会在JMS队列queue/mdb有消息进入时调用。MDB解析消息QƈҎ消息内容计算投资?br />
@MessageDriven(activateConfig = { @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"), @ActivationConfigProperty(propertyName="destination", propertyValue="queue/mdb") }) public class CalculatorBean implements MessageListener { public void onMessage (Message msg) { int start = Integer.parseInt(st.nextToken()); double result = } catch (Exception e) { // ... ... |
依赖注入
在上一节中Q你学到了如何开发藕合松散的服务lg。但是,Z存取那些服务对象Q你需要通过服务器的JNDI来查扑֭根对?session bean)或消息队?MDB)。JNDI查找是把客户端与实际的服务端实现解藕的关键步骤。但是,直接使用一个字W串来进行JNDI查找q不优雅。有q样几个原因:客户端与服务端必L一致的Z字符串的名字。它没有在编译时得到认证或在布v时得到检查?/p>
从JNDIq回的服务对象的cd没有在编译时q行查,有可能在q行时出现{?casting)错误?/p>
冗长的查找代码,有着自己的try-catch代码块,在应用之间是重复的和杂ؕ?/p>
EJB 3.0Q对MPOJO,提供了一个简单的和优雅的Ҏ来解藕服务对象和资源。用@EJB注释Q你可以EJB存根对象注入CQ何EJB 3.0容器理的POJO中。如果注释用在一个属性变量上Q容器将会在它被W一ơ访问之前赋值给它正的倹{下面的例了演示了怎样把CalculatorBean无状态session bean的存Ҏ入到CalculatorMDB MDBcM?/p>
public class CalculatorMDB implements MessageListener {
@EJB Calculator cal;
// Use the cal variable
// ... ...
}
注释如果被用在JavaBean风格的setterҎ上时Q容器会在属性第一ơ用之前,自动地用正确的参数调用bean的setterҎ。下面的片断演示了这是如何做?
public class CalculatorMDB implements MessageListener {
Calculator cal; |
除@EJB注释之外QEJB 3.0也支持@Resource注释来注入来自JNDI的Q何资源。下面的例子中,我演CZ如何注入服务器端默入的TimerService和SessionContext对象Q也演示了如何注入来自JNDI的命名数据库和JMS资源?br />
@Resource TimerService tms; @Resource @Resource (name="DefaultDS") @Resource (name="ConnectionFactory") @Resource (name="queue/A") |
此外Q你也可以把一个容器管理的持久化管理器(也就是,EntityManager-cM于Hibernate session对象)注入到EJB 3.0 POJO中?/p>
把容器服务交lPOJO
除了理生命周期和访问藕合松散的服务对象外,EJB 3.0通过单的注释也ؓPOJO提供了运行时L务?/p>
事务
最有用的容器服务可能就是事务管理服务,当应用出现失败或异常Ӟ它保证了数据库的完整性。你可以单地ؓ一个POJOҎx它的事务属性。这样容器就可以在合适的上下文中q行q个Ҏ。D例来_下面的代码申明了容器在运行updateExchangeRate()时必dZ个新的事务。当q个Ҏ退出时提交事务。实际上Q所有在updateExchangeRate()中被调用的方法都在此事务中运行,除非有特别申明。在updateExchangeRate()中的数据库操作要么全部成功,要么全部p|?br />
@Stateless public class CalculatorBean implements Calculator { // ... ... @TransactionAttribute(TransactionAttributeType.REQUIRED) |
容器也提供了安全服务来进行用戯证和Ҏ用户规则来限制对POJO的访问。对每一个POJO来说Q你可以通过使用@SecurityDomain注释为它指定一个安全域, 安全域告诉容器到哪里L密码和用戯色列表。JBoss中的other域表明文件是classpath中的users.propertes和roles.properties。这PҎ一个方法来_我们可以使用一个安全限制注释来指定谁可以运行这个方法。比如,下面的例子,容器Ҏ有试图调用addFund()的用戯行认证,只允许拥有AdminUser角色的用户实际运行它。如果你没有d或者没有以理员的w䆾dQ一个安全意外将会抛出?br />
@Stateless @SecurityDomain("other") public class CalculatorBean implements Calculator { @RolesAllowed() @RolesAllowed() @PermitAll @RolesAllowed() |
通用拦截?/p>
事务和安全服务都可以被看作是容器理的运行时L截器。容器拦截了对EJB存根的调用,q在其上应用事务上下文或q行安全限制?/p>
在EJB 3.0中,你可以自己写拦截器来扩展容器服务。用@AroundInvoke注释Q你可以Q意beanҎ作ؓ拦截器方法在LbeanҎ之前和之后运行。下面的例子中,log()Ҏ是一个拦截器Q它计算和记录了其它beanҎ的执行时?
@Stateful public class CalculatorBean implements Calculator { // Bean methods that are to be intercepted by "log()" String className = ctx.getBean().getClass().getName(); long start = System.currentTimeMillis(); cal.setTrace(cal.getTrace() + " |
那么Q作为程序员的你Q面Ҏ的规范,该做哪些准备呢?
处理好架构问?/strong>
首先要确保你的架构可以利用持久性方面的标准及认可的设计模式。实际上Q这可能需要改动你的应用程序,不过如果你期望应用程序能l得h间的考验Q那么进行这U投入是值得的。用会话外观、数据访问对象(DAOQ层或者服务层L好主意,不过在这里它们都臛_重要。如果你的应用程序已l用远E实体构成——虽然这U做法ƈ不常见,那么需要重新设计架构。访问持久性对象之前,应当先部|可q程化服务层。如果要使用实体Q它应当完全是本地实体?
不过Q用本地实体不是目的,因ؓ实体qؓ部v人员提供了ؓ实体声明事务?a class="channel_keylink" target="_blank">安全需求的功能。EJB 3.0不允怓Q何这些属性在实体层面q行讑֮。相反,实体的运行环境将p用者来定Q所以所需的Q何事务或?a class="channel_keylink" target="_blank">安全环境由负责闭的J2EElg来安装或者声明?
CMP应用E序
如果你已l是容器理持久性(CMPQ用P那么你可能迫不及待地惌得新Ҏ,希望抛弃无关的接口、不必要的bean代码以及J琐的XML部v描述W,q些是与以前的实体bean开发相关的一些烦人问题。分别要扩展EJBObject和EJBLocalObject的远E和本地接口再也不需要了Q现在实体只要实现普?a class="channel_keylink" target="_blank">Java接口QPOJIQ即可,如果它们选择q么做的话?
其次Q你可能在想如何更容易地在容器中部vEJBQ或者甚x本不用部|Ԍ而是在独立环境中的容器外面进行测试。因为实体是具体的普?a class="channel_keylink" target="_blank">Java对象QPOJOQ,你就可以像一直以来创建Java对象的方式那P即用new()来创建?
POJO应用E序
可以通过实体理器(EntityManagerQ访问大部分新的持久性API。实体管理器可以注入C话bean里面Q或者用Java命名和目录接口(JNDIQ进行查询。实体管理器代表事务的持久性上下文。一旦发现操作单元或者实体管理器理的对象在事务l束?#8220;很脏”Q就会被写到外面的数?a class="channel_keylink" target="_blank">存储区?
应用E序可以通过抽取含有操作单元/会话工gQartifactQ的代码Q让自己不受全面变化的媄响。这样一来,可以通过可插入方式来获得所用的实际会话。定义会话、然后允许包围层把它与外界隔d来,q类似EJB 3.0容器所采用的依赖注入范例(dependency injection paradigmQ。应用程序中使用的所有资源应当采用这U模式。在EJB 3.0中,标准资源由应用E序声明Q随后在q行时被注入到bean里面?
采用标准Ҏ?/strong>
EJB 3.0的许多特性可以在TopLink存在已久的特性当中找到媄子。只要用这些特性,你就能够拥有EJB 3.0的功能,虽然APIq没有完成?
查询是q样一个方面,你现在可以开始用EJB 3.0的特性。EJB 3.0查询可以从实体管理器获得Qƈ且可以在上面执行。可以在你需要直接查询SLQ、ƈ通过查询q回对象的少数情况下Q创建本地SQL查询?
查询语言往往是造成q移问题的根源,因ؓ不编写实质性或者穷举性的转换分析工具Q就很难实现自动转换。EJB查询语言q种合理、有效的Ҏ可对关系查询语言q行抽象处理Q将受益于新增的几项Ҏ。现有的EJB查询语言仍可以适用Q不qEJB查询语言斚w的更多构件和功能进一步改q查询语a。用EJB查询语言~写查询是明智之DQ因为查询语a不会出现重大改变Q除了功能上有所d外?
l承
EJB 2.1从来没有指定真正的、自然的l承。实际上Q只有在厂商不设|障的情况下才有可能实现承,不过仍很隑֮义及理。EJB 3.0却不会这栗由于具体的Java对象能够彼此l承Q也用不着定义U束l承范围的方法,所以你能够创徏L深度及广度的实体l承层次?
目前可以通过TopLink Mapping Workbench或JDeveloper、映Java对象的GUI工具以及用于映射对象的基于Java的APIQ获得同Lq种灉|性。现在你可以创徏域模型(domain modelQ,以遵守适合你应用程序的l承{略Q而不必等规范发布?
乐观锁定
TopLink支持的乐观锁定(optimistic lockingQ模型现在将被采用到EJB 3.0模型里面。这U机制对应用E序非常有用Q不仅仅是因为在?写访问比通常?0Q?0的情况下Q它可以大大提高性能Q还因ؓ它有助于获得Cpȝ所需的那U可扩展架构。业界的众多应用E序使用q种主要的锁定范例,来获得Web应用E序所需的可扩展性。只要用简单方法:为每个乐观锁定的对象采用数据库列和对象版本字D,很Ҏ获得可移植性?
q种锁定带来了另外的好处Q能够用非q接模式的对象。只要把数据重新q入事务、然后通过乐观锁值验证已改动的对象是不是失效副本Q就很容易支持在ȝ状态下改动数据和关pȝ功能?
对象Q关pL?/strong>
想编写面向对象的JavaE序Q却把数据保存在关系数据库里面,q在以前是困扰应用程序开发的一个重大问题?
军_把用于对象关pL的标准化元数据和语义添加到EJB 3.0内,q向实现下列功能q出了一大步Q让应用E序能够灉|地把应用E序攑֜不同数据库上面运行,甚至可以使用不同的持久性框架来q行q行。对象-关系映射标准的第一个阶D将包括如今Z用来映射域模型的几种最行的映方法,譬如数据转换和一对一及多对多关系{。随后还会添?#8220;高”映射Ҏ?/p>
Enterprise JavaBean (EJB) 是可重用的、可UL?J2EE lg。EJB 由封装业务逻辑的方法组成。譬如说QEJB 可能有这L业务逻辑Q包含了更新数据库中客户数据的方法。众多远E和本地客户端可以调用该Ҏ。另外,EJB 在容器里面运行,q样开发h员只要关注bean里面的业务逻辑Q不必担心复杂、容易出错的问题Q譬如事务支持?a class="channel_keylink" target="_blank">安全性和q程对象讉K{。EJB 作ؓ普通Java对象QPOJOQ的形式来开发,开发h员可以用元数据注释Qmetadata annotationsQ来指定容器如何理q些bean?
EJB 包括三种主要cdQ会?bean、实?bean和消息驱动的bean。会话bean执行独立的、解除耦合的Q务,譬如查客L信用记录。实体bean是一个复杂的业务实体Q它代表数据库中存在的业务对象。消息驱动的bean用于接收异步JMS 消息。下面,让我们进一步研IEJB 3.0规范中的q些cd?
一、会话bean
会话bean通常代表业务程里面的操作,譬如“处理订单”。可Ҏ对话状态的保持性,x状态和无状态对会话beanq行分类?
无状态的会话 bean没有内部状态。它们不跟踪记录从一个方法调用传递到另一个方法调用的信息。因此,每次调用无状态的业务Ҏ都独立于前一ơ调用,譬如计算E款或者运贏V用某个应税D用计税ƄҎӞ对税ƑրD行计ƈq回l调用方法,而不必保存调用者的内部状态供以后调用。因些beanq不保持状态,所以容器对它们q行理很单。客Lh无状态的bean实例Ӟ可以从容器保持的无状态的会话bean 实例池当中接收一个实例。另外,因ؓ无状态的会话 bean可以׃nQ所以容器可保持数量较少的实例ؓ许多客户端提供服务。想指定Java Bean作ؓ无状态的会话bean加以部v及管理,只需要ؓ该beand注释@Stateless?
有状态的会话 bean在方法调用时可保持对话状态,譬如客户的网上购物R。客户开始网上购物时Q可以从数据库中索客L详细信息。客户往购物车里面添加商品或者从里面删除商品、下订单{时调用的其他方法也可以使用q些详细信息。不q,有状态的会话bean是暂时性的Q因为出C话终止、系l崩溃或者网l故障后Q状态不复存在。客Lh有状态的会话bean实例Ӟ׃ؓ该客L分配一个有状态的实例Qƈ客户端保持该lg的状态。要指定容器在某个方法完成后删除有状态的会话bean实例Q只要ؓ该方法添加注释@Remove?
会话 beanCZ如下Q?
import javax.ejb.Stateless.*;
/*A simple stateless session bean implementing the incrementValue() method of the * CalculateEJB interface.*/
@Stateless(name="CalculateEJB")
public class CalculateEJBBean
implements CalculateEJB
{
int value = 0;
public String incrementValue()
{
value++;
return "value incremented by 1";
}
}
二、实体bean
实体bean是管理持久性数据的一个对象,有可能用几个相关的Java对象Qƈ可以通过主键实现惟一性。通过d@Entity注释Q可以把某类指定为实体bean。实体bean代表数据库中的持久性数据,如客戯中的一行或者员工表中的一条员工记录。实体beanq可以在多个客户端之间共享。譬如说Q某个员工实体bean可以由多个客L用于计算某员工的q薪或者更新员工地址。实体bean对象的特定字D可以成为持久性字Dc实体bean中没有被@Transient注释标记的所有字D都被视为持久性字DcEJB 3.0的一个主要特性就是,能够使用元数据注释来创徏包含对象/关系映射的实体bean。譬如说Q想指定把实体bean的empId字段映射?Employees表中的EMPNO属性,p使用@Table(name="Employees") 来注释表名,使用 @Column(name="EMPNO") 来注释字D,如下面的例子所C。另外,EJB 3.0 的一个特性是Q在开发期间可以方便地试实体beanQ因为现在?Oracle 应用服务?/a>实体试工具Q就可以在容器外面运行实体bean?
实体 beanCZ如下Q?
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Collection;
@Entity
@Table(name = "EMPLOYEES")
public class Employee implements java.io.Serializable
{
private int empId;
private String eName;
private double sal;
@Id
@Column(name="EMPNO", primaryKey=true)
public int getEmpId()
{ return empId;}
public void setEmpId(int empId)
{ this.empId = empId; }
public String getEname()
{ return eName; }
public void setEname(String eName)
{ this.eName = eName; }
public double getSal()
{ return sal; }
public void setSal(double sal)
{ this.sal = sal; }
public String toString()
{StringBuffer buf = new StringBuffer();
buf.append("Class:")
.append(this.getClass().getName()).append(" ::")
.append(" empId:").append(getEmpId()).append(" ename:")
.append(getEname()).append("sal:").append(getSal());
return buf.toString();}
} 三、消息驱动的bean
消息驱动的beanQMDBQؓ实现异步通信提供了一U比使用直接的Java消息服务QJMSQ更单的Ҏ。MDB用于接收异步JMS消息。容器处理JMS队列和主题所需的大部分讄q程。它把所有消息发送给相关的MDB。MDB允许J2EE应用E序发送异步消息,随后q些消息由应用程序来处理。要把bean指定为MDBQ需要实现javax.jms.MessageListener接口Qƈ且用@MessageDriven注释该bean?
消息驱动的beanCZ如下Q?
import javax.ejb.MessageDriven;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.Inject;
import javax.jms.*;
import java.util.*;
import javax.ejb.TimedObject;
import javax.ejb.Timer;
import javax.ejb.TimerService;
@MessageDriven(
activationConfig = {
@ActivationConfigProperty(propertyName="connectionFactoryJndiName",
propertyValue="jms/TopicConnectionFactory"),
@ActivationConfigProperty(propertyName=
"destinationName", propertyValue="jms/myTopic"),
@ActivationConfigProperty(propertyName=
"destinationType", propertyValue="javax.jms.Topic"),
@ActivationConfigProperty(propertyName=
"messageSelector", propertyValue="RECIPIENT = 'MDB'") } )
/** A simple Message-Driven Bean that listens to the configured JMS Queue or
Topic and gets notified via an * invocation of it's onMessage() method
when a message has been posted to the Queue or Topic.The bean
* prints the contents of the message. */
public class MessageLogger implements MessageListener, TimedObject
{
@Inject javax.ejb.MessageDrivenContext mc;
public void onMessage(Message message)
{ System.out.println("onMessage() - " + message);
try
{
String subject = message.getStringProperty("subject");
String inmessage = message.getStringProperty("message");
System.out.println("Message received\n\tDate:" + new java.util.Date()
+ "\n\tSubject:" + subject + "\n\tMessage:" + inmessage + "\n");
System.out.println("Creating Timer a single event timer");
TimerService ts = mc.getTimerService();
Timer timer = ts.createTimer(30000, subject);
System.out.println("Timer created by MDB at:"
+ new Date(System.currentTimeMillis()) +" with info:"+subject); }
catch (Throwable ex)
{ ex.printStackTrace(); }
}
public void ejbTimeout(Timer timer)
{ System.out.println("EJB 3.0:Timer with MDB");
System.out.println("ejbTimeout() called at:"
+ new Date(System.currentTimeMillis()));
return; }
} 四、?EJB 3.0
新徏一cdEJB3.0,开发EJB所需要的jar包导入便OK。详l步骤如下:菜单中的“工具”选项里选择“?#8221;Q在弹出的窗体中建立新库q命名ؓEJB3.0, 然后EJB所需要的 jar?所有的jar包都可以在GlassFish的安装目录下的lib目录?导入到此cd里便OK?br />
四、启动应用服务器
启动服务器很单,在菜单里选择“H口”里面?#8220;服务”选项Q在弹出的窗体里选中“服务?#8221;Q点开子节点,里面有个GlassFish V2的选项Q右?->启动?br />
OKQ到q里我们的准备工作算是告一D落了,我们来测试下服务器是否可以正常启动?nbsp;打开览器,在地址栏里输入Qhttp://localhost:4848,看是不是出来如下d界面?nbsp;
输入用户名和密码登陆试试?br />
User Name:admin
Passwrod:adminadminQ成功登录后的控制界面如下所C:
EJB开发的环境搭徏基本上就搭徏好了Q本文就此结?/p>