public class MapForm extends ActionForm
{
private Map map = null;
public void setMap(Map map) {
this.map = map;
}
public Map getMap() {
return this.map;
}
同时增加一个属性方法去讄和获取Map中的数据?BR>public void setAttribute(String attributeKey, Object attributeValue)
{
getMap().put(attributeKey, attributeValue);
}
public Object getAttribute(String attributeKey)
{
Object keyValue = getMap().get(attributeKey);
return keyValue;
}
q样我们在jsp面中,我们可以用Struts的标{接触这些Map数据?BR><html:text property="attribute(key)"/>
q样q些数据可l护啦,q对查询条g较多的情况非帔R用Q你无需在维护这些查询信息在各个面的过渡,Struts帮?zhn)完成了一切?/P>
下面我们q一下如何用MapForml织master/detail方式的数据,我们以一个订单做为样例?/P>
1 首先建立一个Form对象Q承MapFormQ同时声明主要的属性,如订单编码、定购h{?BR>public class OrderForm extends MapForm
{
private Integer id;
private String orderMan;
2 我们拟定以Map方式保存采购信息,同一采购w用统一前缀Q可选择行编码,如row123_ productCode,row123_ productId,row123_ amount{,q样某一采购信息将被输入到Map中,不同的采购项的前~不一P前缀由row+行编码组成,同时~写可获取行~码的函敎ͼq样可取得某一采购的所有信息,参数rowPrefix为某一字符Ԍ如“row”、“item”等Q不包含数字~码信息Q同时你可以~写C(j)omparatorQ进行排序?
public Collection getRowIdList(String rowPrefix)
{
if (map.isEmpty()) return new ArrayList();
Collection allRowId = new TreeSet(new RowIdComparator(rowPrefix));
Iterator allKey = map.keySet().iterator();
while (allKey.hasNext())
{
String key = (String) allKey.next();
if (key.indexOf(rowPrefix) != -1)
{
key = key.substring(0, key.indexOf('_'));
allRowId.add(key);
}
}
return allRowId;
}
3 在jsp面中你可以通过jstlQ就可以完成采购明细的显C?BR><c:forEach var="rowId" items="${OrderForm.getRowIdList('row')}">
<tr align="center" id="${rowId}" onclick="clickRow()">
<td ><html:text property="attribute(${rowId}_productCode)" size="8" onkeydown="fillProductInfoWithKeyDown('${rowId}',this)" /><html:hidden property="attribute(${rowId}_productId)"/> <a href="javascript:selectproduct('${rowId}')">选择</a></td>
<td ><html:text property="attribute(${rowId}_productQty)" size="8" /> </td>
<td ><html:text property="attribute(${rowId}_productPrice)" size="8" /></td>
<td ><html:text property="attribute(${rowId}_productName)" readonly="true" size="16" /></td>
<td ><html:text property="attribute(${rowId}_productPackaging)" readonly="true" size="12"/></td>
<td ><html:text property="attribute(${rowId}_productUnit)" size="6" readonly="true" /></td>
</tr>
</c:forEach>
4 q样Struts帮你完成了所有的信息处理Q你需要完成你的保存就可以啦?/P>
提示QMap中的数据值默认都是Stringcd的,如果你想转换成你惌的类型,可以在你的Form~写一个工h法,完成cd的{换?BR>public Map getTypedMap() {
Map map = this.getMap();
String keyString = (String) map.get("key");
Integer keyInteger = new Integer(keyString);
map.put("key",keyInteger);
return map;
}
ȝQMapForm各个功能很少被开发h员用,如果使用得当Q功能非常强大。ActionForm个h认ؓ(f)q是个设计~陷Q结合BeanUtils{工具包Q{换和信息处理非常方便?/P>
http://www.infomall.cn/cgi-bin/mallgate/20040310/http://hedong.3322.org/archives/000336.html
for(Iterator i = people.iterator(); i.hasNext(); ) {
Person p = (Person) i.next();
p.print();
}
}
}
************AddressBookDigester*********
import org.apache.commons.digester.Digester;
/**
* Usage: java Example1 example.xml
*/
public class AddressBookDigester {
public static void main(String[] args) {
if (args.length != 1) {
usage();
System.exit(-1);
}
String filename = args[0];
// 创徏一个Digester实例
Digester d = new Digester();
// 创徏AddressBook实例Qƈ其压入栈顶?BR> AddressBook book = new AddressBook();
d.push(book);
// 增加规则
addRules(d);
// 处理输入的xml文g
try {
java.io.File srcfile = new java.io.File(filename);
d.parse(srcfile);
}
catch(java.io.IOException ioe) {
System.out.println("Error reading input file:" + ioe.getMessage());
System.exit(-1);
}
catch(org.xml.sax.SAXException se) {
System.out.println("Error parsing input file:" + se.getMessage());
System.exit(-1);
}
// 解析出的地址数据打印出来
book.print();
}
private static void addRules(Digester d) {
// 当遇?lt;person>Ӟ创徏cPerson的一个实例,q将其压入栈?BR> d.addObjectCreate("address-book/person", Person.class);
// ?lt;person>标签的属?attribute)与栈Personcd象的属?property)讄Ҏ(gu)Ҏ(gu)各自的名字进行映,(例如Q将标签属性id与属性设|方法setIdq行映射Q将标签属性category与属性设|方法setCategoryq行映射)Q然后将属性的g参数传递给执行相应的方法?BR> // 如果某标{ֱ性没法通过名字扑ֈ相应的属性设|方法,则此标签属性被忽略(如example.xml中第一?lt;person>的try属??BR> d.addSetProperties("address-book/person");
// 调用W二栈顶对象(AddressBook实例)的addPersonҎ(gu)Q以栈对?Person实例)的对象ؓ(f)参数
d.addSetNext("address-book/person", "addPerson");
// 当遇?lt;person>的子元素<name>Ӟ调用栈顶对象(Person实例)的setNameҎ(gu)?BR> // 此处addCallMethodҎ(gu)的第一参数是规则,W二个参数是Ҏ(gu)的名字,W三个是参数的数??Ӟ表示只有一个参敎ͼ且参数的值是元素的内?
d.addCallMethod("address-book/person/name", "setName", 0);
// 当遇?lt;person>的子元素<email>Ӟ调用栈顶对象(Person实例)的addEmailҎ(gu),addEmailҎ(gu)有两个参敎ͼ取值分别来?lt;email>的属性type的值和<email>本n的内宏V?BR> // 此处addCallParamҎ(gu)的第一参数是规则,W二个参数是指明被调用方?addEmail)参数的序PW三个是参数为字W串时指属性的名字)
d.addCallMethod("address-book/person/email", "addEmail", 2);
d.addCallParam("address-book/person/email", 0, "type");
d.addCallParam("address-book/person/email", 1);
}
private static void usage() {
System.out.println("Usage: java Example1 example.xml");
}
}
属?/TD> | 描述 |
classLoader | 指定c装载器(class loader)。ObjectCreateRule ?FactoryCreateRule两个规则中,需要动态加载一些类Q如那些盛放XML解析出来的数据的javaBean{)Q装载器可以在次指定。如果不指定Q对q此cȝ加蝲会(x)利用U程上下文中的加载器Q当useContextClassLoadergؓ(f)真时Q或利用加蝲Digester的那个加载器?/TD> |
errorHandler | 指定 SAX ErrorHandlerQ以在出现此c错误时调用。默认情况下QQ何解析错误都?x)被记入日志QDigest?x)l进行解析?/TD> |
namespaceAware | 一个布?yu)(dng)|为真时对XML文g的解析时?x)考虑元素的域名空_(d)如不同的域名I间的同名元素会(x)视ؓ(f)不同的元素) |
ruleNamespaceURI | 指定后箋加入的规则所属的命名I间Q如果此gؓ(f)null,则加入的规则不与M命名I间相联pR?/TD> |
rules | 讑֮规则模板与XML元素的匹配处理程序。由于这个匹配程序是插g式的Q所以匹配工作的完成可以用用户定义的匚wE序未完成。默认情况下Q用Digester提供的匹配器?/TD> |
useContextClassLoader | 一个布?yu)(dng)|为真时FactoryCreateRule ?ObjectCreateRule 两个规则中对cȝ装蝲会(x)采用当前U程上下文中指定的加载器。默认情况下Q对cȝ动态加载会(x)利用加蝲Digester的那个装载器?/TD> |
validating | 一个布?yu)(dng)|为真时解析器?x)根据DTD内容对XML文档q行合法性检查,默认值是假,解析器只是检查XML是否格式良好(well formed). |
首先Q我们去看一下它的效果?/FONT>http://www.raibledesigns.com/struts-menu/。可以看刎ͼ如此丰富多彩的菜单效果,都是在演CZ个配|文仉的内宏V这是一个非常好的数据与表示相分ȝ实现。我们打开它的源码来看。首先看一下它的包?/FONT>
共有五个包,其中menu自然是完成数据组l功能,是核心之一Qdisplayer是显C方式包Q完成数据显C大部分功能。也是核心之一。taglib意义明显。example自然是一些example。util是读取资源文件的包。因些,我们重点研究的包只有三个menu,displayer和taglib?/FONT>
首先我们来看menu包的cd
首先是MenuPlugInq个cR这个类的功能很明显Q就是一个struts的plug-in。可以看刎ͼ它只有一个参数menuConfigQ就是menu的配|文件\径。果Ӟ在struts-conf文g中有q么一D?/FONT>
<!-- ========== Plug Ins Configuration ================================== --> <plug-in className="net.sf.navigator.menu.MenuPlugIn"> |
说明配置文g来自?WEB-INF/menu-config.xmlQ当Ӟ我们可以扑ֈ相应路径下找到这个文件。如果你以前没有做过struts的plug-inQ现在该知道怎么做了吧,p么简单。通过阅读初始化函敎ͼ知道它的功能是调用MenuRepository来徏立菜单。因此。我们知道MenuRepository必然是一个组l管理管理菜单的l织cR?/FONT>
public void init(ActionServlet servlet, ModuleConfig config)
throws ServletException {
if (log.isDebugEnabled()) {
log.debug("Starting struts-menu initialization");
}
this.servlet = servlet;
repository = new MenuRepository();
repository.setLoadParam(menuConfig);
repository.setServlet(servlet);
try {
repository.load();
servlet.getServletContext().setAttribute(
MenuRepository.MENU_REPOSITORY_KEY,
repository);
if (log.isDebugEnabled()) {
log.debug("struts-menu initialization successfull");
}
} catch (LoadableResourceException lre) {
throw new ServletException(
"Failure initializing struts-menu: " + lre.getMessage());
}
} |
打开MenuRepositoryc,我们可以看到q个cM很简单,不过已经有少可以学习(fn)的了。首先是FastHashMapQ可以看刎ͼq个c里有三个FastHashMap。顾名思议Q是快速HashMap了,再看一下,它来自org.apache.commons.collections.FastHashMap;。看到org.apache.commonsq个著名的包?如果你以前从没用过它,那么你花上一D|间去研究使用它,我保证物有所倹{?/FONT>
protected FastHashMap menus = new FastHashMap(); protected FastHashMap displayers = new FastHashMap(); protected FastHashMap templates = new FastHashMap(); |
接下来我们看到l(f)og的定义。对了,logQ调试的核心之一。而下面这一句则是commons log的最常用的用方法。快快让你的E序使用上commons log吧,W一Q它功能强大Q第二,它用简单,是q么单?/FONT>
private Log log = LogFactory.getLog(getClass().getName()); |
下面看一个的函数
protected Digester initDigester() { Digester digester = new Digester(); digester.setClassLoader(Thread.currentThread().getContextClassLoader()); digester.push(this); //digester.setDebug(getDebug()); // 1 digester.addObjectCreate("MenuConfig/Menus/Menu", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu"); digester.addSetNext("MenuConfig/Menus/Menu", "addMenu"); // 2 digester.addObjectCreate("MenuConfig/Menus/Menu/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); // 3 digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); // 4 digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); // 5 digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); // 6 digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); // 7 digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); digester.addObjectCreate("MenuConfig/Displayers/Displayer", "net.sf.navigator.displayer.MenuDisplayerMapping", "mapping"); digester.addSetProperties("MenuConfig/Displayers/Displayer"); digester.addSetNext("MenuConfig/Displayers/Displayer", "addMenuDisplayerMapping", "net.sf.navigator.displayer.MenuDisplayerMapping"); digester.addSetProperty("MenuConfig/Displayers/Displayer/SetProperty", "property", "value"); return digester; } |
q里又是一个经?digesterQDigester的用,如果你需要读一个XML配置文gQƈ且不想与DOM直接打交道的话,Digester是一个很好的选择。实际上我们看到l(f)oad函数调用一?digester.parse(input);已l把menu-config.xml建立到内存里了,p么简单。如果你惌初始化你的系l,q种Ҏ(gu)是不是可以学?fn)呢Q?工欲善其事,必先利其?。我们可以看到Raible是怎么样利用现有的工具来减d发量的?/FONT>
׃MenuRepositoryN若轻的初始化q程Q甚至都没有让我们看到树(wi)形结构是怎么建立到内存里ȝ。不q不要着急,cdl我们了明示?/FONT>
看到MenuBasecM吗?对了Q看名字q道是一个Menu的基cR可以看刎ͼ它是一个简单的JavaBean。而且怿它的每个属性大家根据名字也能猜出来。所以重点讲解是MenuComponentQ一个简化的 "Composite"模式?/FONT>
如上图所C。由于此处的Leaf没有MҎ(gu)Q只有属性。因此Leaf和Composite收羃成了一个MenuComponentcR大安知道QComposite模式是实现树(wi)形结构最好的Ҏ(gu)。如果你以前没有Z(x)实现或者没有从Composite模式得到好处Q那么,从这里看一下用Composite模式得到的好处。首先看它的单,MenuComponet的实际代码很,加v来不到十行?/FONT>
public void addMenuComponent(MenuComponent menuComponent) { menuComponents.add(menuComponent); menuComponent.setParent(this); if ((menuComponent.getName() == null) || (menuComponent.getName().equals(""))) { menuComponent.setName(this.name + menuComponents.size()); } } public MenuComponent[] getMenuComponents() { MenuComponent[] menus = (MenuComponent[]) menuComponents.toArray(_menuComponent); return menus; } |
如果你用十行来实C个树(wi)型结?q且q是通用?Q你愿不愿意Q就是通过单的q么一些代码,实现的在内存中徏立树(wi)型结构的目标?/FONT>
下面我们来看DispLay包,q个包的功能也是很清楚的Q就是用来显C啦。这个包的类N常漂亮,遗憾的是也非常大。只能羃?yu)了l大家看了?/FONT>
从类图中可以看到一个非常极漂亮的面象对象的设计思\。通过一个接口,利用模板Ҏ(gu)。最后具体实现树(wi)型结构的昄。其主要Ҏ(gu)是displayComponents和displayq两Ҏ(gu)QinitҎ(gu)则实C初始化的工作Q读取javascript和图片等文g。displayComponents是一个P代函数。从而可以遍历一个MenuCompont?wi)。ƈ其昄出来?/FONT>
应该_(d)Menu包是一个M层,而Dispplya包是一个view层,而加上TagLib包,实CMVC的完整结构?/FONT>
两个Tagcd清楚Q首先我们从怎么使用它来看它们实现的功能
<menu:useMenuDisplayer name="ListMenu" bundle="org.apache.struts.action.MESSAGE"> <menu:displayMenu name="ToDoListMenuFile"/> <menu:displayMenu name="ToDoListMenuEdit"/> <menu:displayMenu name="CaseDetailMenuCase"/> <menu:displayMenu name="Standalone"/> </menu:useMenuDisplayer> |
显而易见。useMenuDisplayerq个cL实现使用哪一U显C方式。在menu-config里我们看到ListMenu的定?/FONT>
<Displayer name="ListMenu" type="net.sf.navigator.displayer.ListMenuDisplayer"/> |
displayMenu则是取得一菜单Qƈ其昄出来Q同样在menu-config也能扑ֈ?/FONT>
<Menu name="ToDoListMenuEdit" title="EDIT"> |
查看 DisplayMenu的代码,可以看到。它完成的功能只是从context里取得MenuComponent对象Q然后通过 displayer.display(menu);把它交给一个MenuDisplayer的实例来负责d来?/FONT>
因此QControl层很好的完成了控制的功能?/FONT>
lg所q。通过q样一个优的设计Q把各个功能都他d来了。如果我们需要增加一U显C方式,只要l承MenuDisplayer或它的一个子c,然后写出我们的方法,而不需要修改系l的其他部分。同L(fng)Q如果我们的菜单不准备存攑֜ServletContext而准备存攑֜比如Session里了Q那么我们也只需要修改control部分和生成部??SPAN class=style1>MenuRepository)部分。而不影响Display部分?/FONT>
OKQ对struts-menu的介l结束了Q下一文章将是如果用struts-menu和数据库技术动态生成菜单了。请大家l箋x我的Blog?/FONT>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
惌用好Struts应用框架Q必M解J2EE WebUJSP和Servlet技术存攑օ享对象的几种方式。同Ӟ要利用J2EE 开发Web应用E序也必L握组仉对象׃n的机制?BR> 像JavaE序有类U别变量、方法别变量一PJ2EE Web应用E序有四个对象存攑օ享对象。这些共享对象存攑֜那里Q以便存放者或者其它程序代码日后用。这四个对象分别是页面、请求、会(x)话和应用E序Q它们都是以数据l构?值对的Ş式保存的。同时这四个对象形成了四个别的׃n对象存放圎ͼ卛_用程序对象中的共享对象是全局性的Q在整个应用E序的生命周期内有效Q当然主动去掉除外)Q属于所有的上网用户Q会(x)话对象中的共享对象是在一个会(x)话期内有效,属于用户的当前会(x)话;h对象中的׃n对象在一个请求期内有效,属于用户发送的当前hQ页面对象中的共享对象只属于当前面的执行实例。本文主要分析共享对象的讄和访问方法,包括׃n对象的有效范围、具体访问方法、辅助显C手D和多线E下的实现策略?BR> Servletq行时已l准备好了这些范围对象,如表1所C?BR>
Servlet中的׃n对象如表2?BR>
在JSP技术中Q有两种把资源片断组合成一个资源的技术:(x)include 指示W和jsp:include元素。指C符的语法ؓ(f)Q?BR>
当一个JSP被翻译成ServletӞ它会(x)被处理。jsp:include元素的语法是Q?BR>
当这个JSP面被执行时Q它?x)被处理。指C符是代码的l合Q元素则是结果的l合。fragmentresource.jsp和主面h一L(fng)上下文,如页面对象;而included.jsp不具有和主页面一L(fng)面对象Q但h对象是同一个?BR> 在Servlet中,RequestDispatcher.include(request,response)实现l果的整?CZ代码如下Q?BR>
在利用RequestDispatcher.forware(request,response)把控制传l另一个Weblg讄形成一个控制管道时Q要严格遵@“前面的lg处理requestQ最后的lg处理response”的准则。前面的lg甚至不能企图获取response输出的引用。这个控制管道中的组件具有同一个request对象Q不h相同的pageContext对象Q针对JSPQ。Servlet中传递控制的例子如下Q?BR>
JSP中传递控制的例子如下Q?BR>
在JSP中,可以通过jsp:param元素来增加请求对象的参数Q适用于jsp:include和jsp:forward两元素?CZ如下Q?BR>
![]() 在网上昄各个U别的共享对象是一个非怸错的调试手段。下面的昄׃n对象cdQ如?Q实Cq个功能。它以类org.i18.struts.AttributeUtils为核心,q个c负责把四个对象的共享对象保存ؓ(f)?值对的HashMap对象。通过它可以得到请求对象中的共享对象及(qing)参数信息Q以?qing)页面对象、会(x)话对象、应用对象这些共享对象的函数接口。在JSP面中,通过下面的代码可以给q个cȝ对象提供输入Q?BR>
JSP面可以利用Struts提供的logic标签库显C些共享对象:(x)
在Servlet中,AttributeDisplayHelper帮助者类完成JSP中logic标签库相应的功能。帮助者类完成工作后,AttrServlet把帮助者类完成的结果放在request中的一个共享属性Attr中,接着把控制传lservletAttr.jspQ再由它讉KAttrServlet在request中设|的׃n属性AttrQƈ昄l果? 使用者可以通过attr.jsp、AttrServlet的url映射和index.jsp的提交按钮来查看当前上下文所有别的׃n对象?BR> J2EEpd规范中,EJB规范保证了组件开发者在单线E的环境下编E,但Servlet规范没有规定Servlet的系列服务方法在单线E模式下q作Q所以开发者在使用׃n对象时要注意U程同步问题。一个比较通用的原则是Q在一个专职的lg中设|共享对象,当设|和讉K破坏数据的一致性时Q用Java的同步控制?BR> 一个Servlet(包括JSP)的生命周期由其所在的容器控制。当有一个ServlethӞ容器执行如下步骤Q?BR> 1.如果此Servlet的实例不存在Q容器先装蝲Servlet的类代码Q创Z个Servlet实例Q接着调用q个实例的initҎ(gu)?BR> 2.如果此Servlet的实例存在,容器分配一个处理用戯求的工作U程。如果Servler实现了SingleThreadModel接口Q工作线E会(x)在这个实例上同步Qƈ且在取得讉K权限后调用实例的serviceҎ(gu)Q否则直接调用实例的serviceҎ(gu)。javax.servlet.http.HttpServlet的serviceҎ(gu)?x)根据用L(fng)HTTPhcd调用相应的doxxxҎ(gu)?BR> 3.只有当所有的U程从这个实例中退出,容器在回收这个实例时才会(x)调用q个实例的destroyҎ(gu)?BR> Servlet实例U程图(如图2Q体Cq个生命周期模型?BR> ![]() 虽然可以让所有的Servlet实现SingleThreadModel接口Q但q会(x)严重影响E序的性能。要解决多线E的同步问题Q我们首先要分析׃n对象的访问模式。在一个WebE序中,׃n对象按访问模式可以分Z下两c:(x)一ơ设|、多ơ读取的׃n对象和多ơ设|、多ơ读取的׃n对象?BR> 一ơ设|、多ơ读?/B> 对于q种׃n对象Q可以开发一个事件监听器Q监听程序启动和停止事gQ代码如下:(x)
q样Q当Web应用E序启动Ӟ容器?x)调用监听器Q从而设|共享对象。共享对象的讉K只需直接调用getAttributeҎ(gu)卛_?BR> 多次讄、多ơ读?/B> 对于多次讄、多ơ读取的׃n对象Q必d用Java的同步机Ӟ讉K步骤如下Q?BR> W一步,首先设计一个同步类。这个同步类的代码非常简单:(x)
以上代码可以看出Q它其实什么都没做Q但l承了Object关于同步的方法和机制?BR> W二步,把这个类的实例放到应用对象中作ؓ(f)一个共享对象。可以看个同步对象属于一ơ设|、多ơ用的׃n对象。在应用E序启动事g监听器中讄它,请参见前面的代码?BR> W三步,讄׃n对象。如果有对象需要整个应用程序共享,可以在Servlet的service中利用同步机制来讄Q?BR>
W四步,d׃n对象。当需要访问多ơ设|、多ơ访问的׃n对象Ӟ同样需要利用同步机Ӟ代码如下Q?BR>
?x)话对象内共享对象的多线E问题可以和应用对象一样处理。请求对象和面对象正常情况下是U程安全的,除非开发者自己引入了额外的线E?BR> 本文分析了开发好的J2EE应用必须掌握的、组仉对象׃n的技术,q种分析技术同样适用于EJB中。在EJB容器中,信息存放的位|变成了实现JNDI的服务提供者,大家通过JNDI的接口方法查询、绑定共享对象。需要注意的是,同步对象不能在JNDI中实玎ͼ因ؓ(f)大家搜烦出来的不是同一个内存对象?BR> |
王和全(ok_winnerboy@sina.comQ?BR>2003q?8 ?/P> 作ؓ(f)ZMVC模式的Web应用最l典框架QStruts已经正式推出?.1版本Q该版本在以往版本的基上,提供了许多激动h心的新功能。本文就带你走qStruts 1.1L入地了解q些功能?/BLOCKQUOTE> |
![]() |