??xml version="1.0" encoding="utf-8" standalone="yes"?> ZSpring 2.0?个Web应用Q?/p>
Live在线书店Q?/font>http://www.livebookstore.net/ q是?a target=_blank>Spring 2.0核心技术与最佛_?/font>》一书的完整CZQ源代码在书的配套光盘中?/p>
JavaEE开发网Q?/font>http://www.javaeedev.com/ q是ZSpring 2.0的CMSpȝQ包括文档,博客Q论坛,公告{系l模块,源码在|站试运行一D|间后公布?nbsp; 两个架构均是完整的基于Spring 2.0?层轻量应用Q前端采用SpringMVCQ后端采用Hibernate 3Q扩展性强?/p>
摘要
拥有自己的音乐站Ҏ个不错的xQ相比传l的ASP?/SPAN>PHP站点Q我们将采用J2EE架构实现多层l构的,高度可扩展的站点。您看刎ͼ采用J2EE技术,加上强大?/SPAN>WebLogicq_Q我们能更容易的实现一个音乐站点应用,而非传统意义上的q面l构的网站?/SPAN>
?/SPAN>
随着J2EE的快速普及,来多的开发h员都想编写基?/SPAN>J2EE架构的分布式的企业应用E序。ؓ了降低开发难度,J2EE提供的基于组件的Q分层的分布式应用模式,使具有可伸羃Q可扩展和易l护{优炏V利?/SPAN>J2EEQ可以快速开发、部|和理多层l构、面?/SPAN>Web的,以服务器Z心的企业U应用?/SPAN>
在下面的q个CZ中,我们构Z个基?SPAN>J2EE的音乐站点,暂定名ؓJetMusicQ计划给用户提供览Q下载等在线服务。事实上Q?/SPAN>Internet上已有很多这U类型的站点Q但是它们大多是传统的基于两层结构的模型Q用了混合HTML?/SPAN>ASP?/SPAN>PHP脚本代码的页面?/SPAN>
管我们?SPAN>JetMusic站点可能现在规模不大Q仅仅?SPAN>JSP/JavaBeans/数据库就已经_了,但是作ؓCZQ我们还是打用J2EE来实现它。借助J2EE的强大功能,我们能够L地快速创样一个音乐站点,q且使我们的~码量最。更重要的是Q它是完全的3层分布式l构Q具有很强的扩展性?/SPAN>
我们的站炚w要对用户提供览Q下载等服务Q还要允许系l管理员能随时登陆ƈ理站点资源。对于我们的JetMusic站点Q我们计划用树型结构来l织分类、艺术家、专辑、歌Ԍ一个简单的CZ囑֦下:
对用戯言Q我们需要提供以下服务:
用户可以览分类、艺术家、专辑、歌曌Ӏ歌词?/P>
用户可以按关键字模糊搜烦歌曲Q专辑,艺术家和歌词?/P>
用户可以Ҏ一首歌评分?/P>
如果歌曲文g存在Q用户可以下载歌曌Ӏ?/P>
如果歌词存在Q用户可以浏览歌词?/P>
Z理站点资源Q我们把使用者分为用户和理员两c:
用户可以注册Q更改自q信息?/P>
理员可以创建或删除分类Q艺术家Q专辑,歌曲?/P>
以上可以单看作是我们分析的全部用例。我们将采用J2EE标准的三层结构模型,系l分C层Q逻辑层和持久层(EIS层就免了Q因为我们ƈ没有所谓的“遗产系l”)Q下面将详细讨论设计Ҏ?/SPAN>
通常Q设计L从域模型开始的Q我们通过对用例的分析Q可以徏立以下实体:
分类对象Q?SPAN>CategoryQ,艺术家对象(ArtistQ,专辑对象Q?/SPAN>AlbumQ,歌曲对象Q?/SPAN>SongQ,q些对象是简单的一对多关系。我们把q些对象都一一映射为实?/SPAN>BeanQ由于这些对象最l映ؓ数据库表Q这些表的结构相对简单,因此我们选择CMP实现。相?/SPAN>BMPQ?/SPAN>CMP不仅大大化了数据库访问的代码Q而且由容器管理的Ҏ通常?/SPAN>CMP?/SPAN>BMPh更好的缓冲性能和可UL性?/SPAN>
在逻辑层,毫无疑问Q会?SPAN>Bean是最好的选择。如果不使用Session BeanQ您可能需要自行处理多U程同步Q安全,事务处理{问题,q将是一复杂而艰巨的工程。由于容器自动ؓ我们处理了大量的如事务,安全Q多U程{底层服务操作,我们只需x于我们的业务逻辑。此外,对于异步调用Q消息驱?/SPAN>Bean是不错的选择?/SPAN>
在表C层Q我们用JSP面界面呈现给用户。有q网站开发经验的开发h员一定有q这L深刻感受Q?/SPAN>HTML元素?/SPAN>JSP代码混合在一h多么巨大的灾难,它将D可维护性随着面数量的增加呈指数增长?/SPAN>
开源的Struts模型通过MVC架构大大化了JSP前端的开发。但是,使用Strutsq要求开发h员对?/SPAN>Struts底层l构和配|文件有相当的了解?/SPAN>WebLogic提供?/SPAN>Java Page Flow技术完全基?/SPAN>Struts技术,q且隐藏了更多的底层l节Q提供了囑Ş化的面D功能Q大大简化了Web层的开发。此外,大量?/SPAN>NetUI标签和数据绑定技术,使您?/SPAN>JSP面立刻具备了可视化开发的功能。我们将采用各种实现方式来编?/SPAN>Web层?/SPAN>
J2EE仅仅是一个标准,众多的厂商提供了相应的实现^収ͼ其中Q?/SPAN>BEA?/SPAN>WebLogic无疑是最优秀?/SPAN>J2EEq_之一。我们将?/SPAN>JetMusic站点建立?/SPAN>WebLogic Platform 8.1q_上,q?/SPAN>WebLogic Workshop 8.1q个强大?/SPAN>IDE作ؓ开发工兗你可以免费?/SPAN>BEA官方站点上下载非商业目的开发用的5-IP限制版本?/SPAN>WebLogic Platform 8.1Q?/SPAN>
http://commerce.bea.com/showproduct.jsp?family=WLP&major=8.1&minor=2
我们的站点资源除了歌曲文件外Q将全部存放在关pL据库中。选用MS SQL Server 2000的原因是q个数据库对g资源的消耗要?SPAN>Oracle,适合于我们作开发用。如果您计划采用Oracle或其他数据库Q没有关p,您只需修改实体Bean的相关部|配|,卛_立刻UL到其他厂商的数据库上?/SPAN>
要流畅地q行WebLogic Server 8.1?SPAN>WebLogic Workshop 8.1Q您臛_需?SPAN>512M内存Q如果仅?/SPAN>256M内存Q您可以修改WebLogic的启动参敎ͼ指定较小?/SPAN>JVM内存Q如128MQ?/SPAN>
配置数据库:
启动MS SQL ServerQ打开企业理器,新徏数据?SPAN>musicQ请注意我们q不需要徏立Q何表Q稍候部|我们的应用?/SPAN>WebLogic会自动创建相应的表?/P>
配置WebLogicQ?SPAN>
新徏一个名为?SPAN>music”的配置Qƈ选择模板Basic Weblogic Workshop DomainQ以便能使用Workshopq行开发和调试?/P>
启动WebLogicQ打开览器,输入http://localhost:7001/consoleQ登陆,在左Ҏ?/SPAN>music?/SPAN>Services?/SPAN>JDBC?/SPAN>Connection PoolsQ新Z个连接池Q选择数据库?SPAN>MS SQL Server”,驱动E序?SPAN>BEAs MS SQL Server Driver(Type 4) Versions:7.0, 2000”,填好数据库名Q用户名Q口令等Q测试无误后部v。然后选择左边?/SPAN>music?/SPAN>Services?/SPAN>JDBC?/SPAN>Data SourcesQ新Z个名为?SPAN style="COLOR: blue">jdbc/MusicDataSource”的数据源,使用刚才建好的连接池Q部|Ӏ?/SPAN>
启动WorkshopQ选择New?/SPAN>Application新徏应用E序MusicQ然后选择BrowseQ找到服务器配置文g?/SPAN><BEA安装目录>user_projectsdomainsmusicconfig.xml”,定后创Z个应用程序?/SPAN>
Z有效l织和管理代码,我们把所有的实体Bean均放在包music.ejb.db中,所有的会话Bean攑֜music.ejb中,另外Z?/SPAN>music.shared包用于存放会?/SPAN>Bean和表C层JSP/Servlets׃n?/SPAN>Javac,包括各种自定义异常,传递的值对象等?/SPAN>
我们创?SPAN>3个工E,分别?/SPAN>MusicEjb ProjectQ实现持久层和逻辑层;MusicClient ProjectQ用于在命o行测试和调试逻辑层和持久层;MusicWeb ProjectQ用于实现表C层。创建好的结构在Workshop中显C如下:
我们首先开始设计持久层Q通过前面的分析,我们抽象Z下实?SPAN>BeanQ?/SPAN>
Category实体BeanQ代表一个分c;
Artist实体BeanQ代表一个艺术家Q?/SPAN>
Album实体BeanQ代表一个专辑;
Song实体BeanQ代表一首歌Ԍ
Account实体BeanQ代表一个用戗?/SPAN>
对应?SPAN>CMR关系为:
Category-ArtistQ一对多
Artist-AlbumQ一对多
Album-SongQ一对多
先徏立一个简单的名ؓCategory?SPAN>Entity BeanQ展开左侧MusicEjb?SPAN>music?/SPAN>ejb?/SPAN>dbQ选择菜单File?/SPAN>New?/SPAN>Entity BeanQ在设计视图中分别添?/SPAN>3?/SPAN>CMP字段Q一?/SPAN>CreateҎQ一?/SPAN>FinderҎQ另外我们还d了一?/SPAN>copyҎ用于q回一?/SPAN>Value Object对象Q?/SPAN>
然后在右边的属性面板中?SPAN>Entity Bean讄属性,在这里我们的主要讄如下Q?/P>
data-source-name: jdbc/MusicDataSource
default-transaction: Required
table-name: category
Local EJB
JNDI name: ejb/Category
Bean class name: CategoryLocal
Home class name: CategoryLocalHome
Z实现自增主键的功能,需要指定一?SPAN>Ejb-Gen属性:点击右键弹出菜单Q选择Insert EJB Gentag?SPAN>automatic-key-generationQ指定字D名idQ数据库cdSQLServer2000卛_?/SPAN>
Z提高CMP的性能Q我们全部采用本地接口,如果某些CMP字段需要提供只L口(?/SPAN>id,username字段Q,只需右键点击字段名,然后选择?/SPAN>Read Only”?/SPAN>
按相同方法徏?SPAN>Artist.ejbQ?SPAN>Album.ejbQ?SPAN>Song.ejbQ?SPAN>Account.ejbQ然后创?SPAN>CMR关系Q?/SPAN>
打开Category.ejb的设计视图,点击右键Q在弹出菜单里选择Add RelationQ徏立一对多?SPAN>CMR关系Q?/SPAN>
cM的,创徏Artist-Album?SPAN>Album-Song的一对多关系?/P>
注意我们q设计了一?SPAN>AccountUtilc,使用正则表达验证用户信息的有效性,比如用户名,口o是否W合要求?/P>
首先我们设计一?SPAN>JndiHelperc,用于装查找实体Bean?/SPAN>JNDI操作Q同时还对实?/SPAN>Bean?/SPAN>Home接口~存?/SPAN>
我们把所有的逻辑装?SPAN>3?/SPAN>Session Bean中:
MusicView.ejbQ用于封装浏览,下蝲{页面操作?/SPAN>
AccountManage.ejbQ用于封装注册,登陆{̎h作?/SPAN>
MusicManage.ejbQ用于封装管理员的各Ҏ作?/SPAN>
首先建立MusicView.ejbQ我们一p计了以下业务ҎQ?/P>
所有的Ҏ均ؓq程ҎQ这?SPAN>Web层和EJB可分开部v。当然你也可以将会话Bean接口全部Ҏ本地接口以进一步提高性能。方法看上去虽多Q但逻辑相当单,例如Q?/SPAN>getAlbums(int artistId)ҎQ?/SPAN>
基本逻辑大致为:
查找相应的实?SPAN>BeanQ获得实?/SPAN>Bean对应CMR关系的集合,在将其包装成值对象返回给客户端。你可以看到Q由于没?/SPAN>JDBC操作Q代码被大大的简化了Qƈ且由于容器处理了底层数据库连接,使我们的代码更加健壮?/SPAN>
在这里我们采用了值对象(Value ObjectQ模式,用于?SPAN>Web层和逻辑层之间传送对象。你可以很容易的写出每个实体Bean对应的值对象,比如Album.ejb对应的值对?/SPAN>AlbumVO?/SPAN>
然后创徏AccountManage.ejbQ用于处理用户帐L关的操作Q?/P>
如果要提高系l的安全性,使用MD5码存储用户口令,MD5法是一个单向函敎ͼ即获得数据库表也无法得知用户口令。如果要q一步防止用预先算好的MD5码攻击,q可以采用加盐处理。ؓ了简单v见,我们直接用户口令存储在数据库表的字D中Q因?/SPAN>login(String username, String password)看v来像q样Q?/SPAN>
如果扑ֈ了相应的实体BeanQ即数据库表中存在此记录Q,再判?/SPAN>password字段Q如果相W,验证通过Q否则,抛出一个自定义?SPAN>UnauthorizedException异常。这里也不涉?SPAN>JDBC操作Q因此代码非常简单?/SPAN>
搜烦功能是通过实体Bean?/SPAN>FinderҎ实现的,标准?/SPAN>EJB QL 2.0q不支持带参数的like关键字(?/SPAN>SUN J2EE SDK中编译就会失败)Q幸?/SPAN>WebLogic对其q行了扩充。ؓ了实现模p搜索,q里我们定义?/SPAN>Song.ejb的一?/SPAN>FinderҎfindByTitle(String title)Q对应的EJB QL为?/SPAN>SELECT OBJECT(o) FROM Song AS o WHERE lower(o.title) LIKE concat(%, concat(?1, %))”。如果用其他厂商的服务器,你需要查看厂商的EJB QL文档然后作相应的修改?/SPAN>
最后一个是MusicManage.ejbQ用于管理员创徏、删除信息:
创徏和删除实?SPAN>Bean的代码都非常单,唯一需要的是捕获相应的异常,我们?/SPAN>deleteSong(int songId)中将RemoveException包装成更一般的ApplicationException异常q回客户端:
需要特别注意的是,对存在一对多CMR关系的实?/SPAN>BeanQ如果指定了U联删除Q当删除“一”时Q对应的“多”会被自动删除,因此执行删除前要异常心Q至应该提C用P或者,只允许删除“多”ؓI的实体BeanQ比如删?/SPAN>Artist.ejbӞ
home = JndiHelper.getArtistLocalHome();
artist = home.findByPrimaryKey(new Integer(artistId));
Collection c = artist.getAlbums();
if(c.size()>0) throw new Exception("Cannot delete unless it is empty.");
Z安全赯Q我们在删除时不允许删除“多”一方不为空的实体?/P>
~译Q部|Ӏ?SPAN>OKQ小功告成!现在我们可以试一下我们的逻辑层。一个好的办法是使用JUnit试Q但是似?/SPAN>Workshop未集成JUnit。没关系Q我们自己写一个客LJavaE序来测试逻辑层?/SPAN>
?SPAN>MusicClientProject中徏?SPAN>Client.Javac,攑֜?SPAN>music.client中。ؓ了测试,我们首先在数据库中创Z些测试数据:
void initData () throws Exception { ?}
然后Q我们写一?SPAN>testGetCategories()ҎQ?/P>
void testGetCategoris(int parentCategoryId) throws Exception { ?}
cM的,我们Ҏ个业务逻辑都写一个相应的试ҎQ直到每一个业务逻辑都正无误。在一个独立的Java客户端程序中试EJB要比?/SPAN>JSP中方便得多,你可以方便地讄断点Q跟t以便查看变量,随时使用System.out.println()在控制台输出M调试信息?/P>
表示层用于向用户提供pȝ交互的接口。采?SPAN>J2EE
1Q在JSP中用业务代表模式:
通常Q在JSP/Servlets中直接调?SPAN>EJBq不是一个好LQ这需要大量的查找JNDI的代码,业务代表Q?/SPAN>Business DelegateQ是一个不错的模式Q它装了所有的EJB查找和调用,使得表示层和逻辑层的耦合度能降到最低,q且q可以进行一些缓存?/SPAN>
׃业务代表cdWeb?/SPAN>EJB隔开了,因此Q对?/SPAN>JSP/Servlets来说Q它们根本就不知?/SPAN>EJB的存在。这带来另一个好处:只要在设计时仔细定义了业务代表的接口Q?/SPAN>Web层和EJB的开发h员立卛_以同时开发各自的模块Q?/SPAN>Web层的开发h员可以首先对业务代表c进行硬~码Q以便返回他们希望的l果。等?/SPAN>EJB开发完成后Q再用实际的EJB调用替换卛_?/SPAN>
Z装?SPAN>MusicView.ejb的所有调用,我们建立一?SPAN>MusicViewBD的业务代表类Q?/P>
?SPAN>index.jsp中即可?SPAN>MusicViewBD以便输出CategoriesQ?/P>
2Q?SPAN>NetUI标签实现数据l定
WebLogic提供了大量的NetUI标签Q能够方便地?/SPAN>JSP中绑定数据。我们下一步将创徏一?/SPAN>viewCa.jsp面Q此面向用户展C分cR?/SPAN>
我们首先创徏一?SPAN>EJB Control?SPAN>WebLogic向我们提供的EJB Control完全装?SPAN>EJB调用。利?/SPAN>EJB ControlQ我们不用写一行代码,立刻可以在JSP中调?/SPAN>EJB。这?/SPAN>EJB Control可以看作是一个用标签装的业务代表模式的应用?/SPAN>
?SPAN>MusicWeb工程下新建文件夹music.controlQ用于存?SPAN>EJB Control。新?SPAN>EJB ControlQ命名ؓCallMusicViewQ?/P>
选择?SPAN>Browse application EJBs?/SPAN>”,直接扑ֈ?/SPAN>MusicView (remote jndi)”,Workshop会自动填好相应的接口Q点几Z?SPAN>create”即创徏成功?/SPAN>
当我们获得了?SPAN>EJBq回?/SPAN>Collection后,可以使用Repeater标签昄q个复杂的数据,在这里我们不打算q一步讨?/SPAN>Repeater标签的细节,关于如何使用Repeater标签Q可以参考下面的文章Q?/SPAN>
http://dev2dev.bea.com/products/wlworkshop81/articles/repeater.jsp
以下JSP面昄了如何?/SPAN>EJB Control调用EJB?/SPAN>getCategoriesq程ҎQƈ返回的Collectionl果集用Repeater标签以超链接的Ş式显C出来,q里q用C一?/SPAN>netui:anchor标签Q?/SPAN>
W一?SPAN>DeclareControl标签x了一?SPAN>ControlQ名?SPAN>ctrlMusicViewQ第二个CallControl标签调用控gctrlMusicView的一个方?SPAN>getCategoriesq带参?SPAN>MethodParameterQ参数值由URL?/SPAN>id定Q然后将l果攑֜变量categories中?/SPAN>Repeater标签?/SPAN>categories取得数据源,然后循环昄集合中的每个元素?/SPAN>
对应的源代码看v来是q样Q?/P>
q行l果?SPAN>IE中显C如下:
cM圎ͼ您可以自己“画”出列艺术家Q列专辑的页面,然后它们组合v来。这?SPAN>JSP面仅仅是通过单的鼠标拖放和属性设|完成的Q真是太了Q?/SPAN>
3Q?SPAN>Java Page Flow
Java Page Flow是完全基于开源的Struts模型Q但提供了更强的易用性和自动化的状态管理,支持数据l定Q拥有更强大的动作和异常处理。下一步,我们创Z个名?/SPAN>FileUploadController的页面流Q完成一个文件上传的功能?/P>
新徏面?SPAN>fileUpload
详细代码请下载源代码包?/P>
使用Java Page Flow的最大的优点是完全的MVC架构Q有清晰的导航模型,q且配合NetUI标签Q实现v来更为简单?/SPAN>
以上讨论了基本的览面Q下面我们要实现用户登陆的功能和针对理员的理功能?/P>
实现用户登陆
Z跟踪用户Q我们创Z?SPAN>AccountManageBD的业务代理类Qƈ它攑֜Session中,装所有与用户帐号相关的操作。几个关键的Ҏ如下Q?/P>
String getName() // q回用户登陆?/SPAN>
boolean isAdmin() // 是否是管理员
void login(String username, String password) // 登陆pȝ
我们?SPAN>MusicSessionListener中监?SPAN>Session事gQ以便创建和销毁与Session兌?SPAN>AccountManageBD对象。然后在/WEB-INF/web.xml中注?SPAN>MusicSessionListener?/P>
q样QQ何时候均可在JSP中调?/SPAN>
AccountManageBD account = (accountManageBD)
session.getAttribute(AccountManageBD.ACCOUNT);
来访问用户当前登陆信息?/P>
创徏一个登陆页面流LoginController.jpf实现登陆面Q?/P>
cM的,一?SPAN>registerUser面实现用h册功能:
实现理功能
Z能ɽ理员登陆后理站点Q我们把所有的理面攑֜目录{MusicWeb}/admin/下,Z防止未授权用戯问Q?SPAN>/admin/下的M面Q我们准备一个过滤器AdminFilterQ将所有未授权的操作重定向?SPAN>/login/LoginController.jpf中。核心代码如下:
修改/WEB-INF/web.xmlQ添加过滤器xQ?/P>
最后,你只需要用Dreamwaver之类的网制作Y件强化界面,然后把这?SPAN>JSP及页面流l织hQ即可发布您?/SPAN>JetMusic站点?/P>
正如你所看到的,我们的安全是通过Web层的登陆验证实现的,在逻辑层ƈ未用基于角色的验证逻辑Q对于我们现在的站点来说Q可能已l够了。不q,如果您打将来向用户提供付费下蝲Q付Ҏ听的业务Q那么就需要切实保证系l的安全性。通常Q不您自己开发安全逻辑Q因为开发防d的安全逻辑本n׃是一个简单的dQ此外,自定义的安全逻辑可能会导致较低的可重用性?/SPAN> ?SPAN>Java 1.4
1Q创Z?/SPAN>LoginContext实例?/P>
2Q指?/SPAN>LoginContext的配|文件?/P>
3Q加载指定的LoginModule?/P>
4Q客L调用LoginContext?SPAN>login 5Q如果登陆成功,׃一个通过w䆾验证?/SPAN>Principal?SPAN>Subject联系h?/P>
6Q?/SPAN>LoginContext通过w䆾验证?SPAN>Subjectq回客户机?/P>
?SPAN>WebLogic
要在WebLogic中实现自定义的安全验证,需要提供一?SPAN>Security ProviderQ包括自定义?SPAN>Authentication ProviderQ?/SPAN>Login ModuleQ?SPAN>Identity AssertionQ?SPAN>Principal Validation ProviderQ?/SPAN>Role Mapping ProviderQ看上去有点复杂Q不q,你可以从BEA站点下蝲一个示例代码:
http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp
然后修改相应的实现类Q配|部|Ԍ卛_实现自定义的JAAS验证?/SPAN>
使用JAAS验证的好处是Q验证逻辑从页面中分离Q对面的限制访问是通过/WEB-INF/web.xml中的配置指定的,无需自定义过滤器Q在调用EJBӞWeb容器能隐含的?/SPAN>Principal传递给EJB容器Q从而和EJB的安全限制联pv来?/SPAN>
在我们的JetMusic站点中,目前仅仅使用Session+qo器的机制Q也许可以考虑在下一个版本中加入JAAS验证Q以便更清晰地划分表C层逻辑和安全逻辑?/SPAN>
可以直接?SPAN>Workshop中启?SPAN>WebLogic ServerQ即可自动完成部|Ӏ如果部|成功,WebLogic不会输出提示信息Q如果部|失败,会提C异怿息?/P>
常见的异常有Q?/P>
JDBCq接出错Q没有启动数据库Q或者没有找到对?/SPAN>JNDI名称的数据库。你需要检?/SPAN>JNDI讄和数据库是否已经启动?/SPAN>
INSERT语句出错Q这是创建实?/SPAN>Bean时容器的INSERT语句出错。如果指定了automatic-key-generationQ第一ơ部|时WebLogic自动创徏的主键ؓINTcdQ但q不?/SPAN>AUTOINCREASEQ需要打开数据库,手动更改主键讄Q或者,您可以直接下载示例数据库Q然后导入到SQL Server2000卛_?/P>
我们已经利用J2EE技术在WebLogic Server上基本徏立v了一个健壮的Q可扩展?/SPAN>JetMusic音乐站点Q其中涉及到EJBQ?/SPAN>JSPQ?/SPAN>ServletsQ?/SPAN>NetUIQ?/SPAN>Java Page FlowQ?SPAN>JAAS以及几个常用?/SPAN>EJB设计模式{。相信您能从q个小?/SPAN>Web应用中看?/SPAN>J2EE体系强大的功能。由于时间仓促,水^有限Q文中不免会有一些错误,恌读者指正。最后希望本文能够给您带来一Ҏ莗最后申明:如果您?/SPAN>JetMusicpȝ在网上发布音乐,所引v的一切版权纠Uh文作者概不负责?/P>
JetMusic站点的全部源代码Q?/SPAN>Workshop 8.1工程Q和CZ数据库: http://javap2p.nease.net/src/jetmusic.zip Sun J2EE TutorialQ?/SPAN>http://java.sun.com/j2ee/tutorial/1_3-fcs/index.html EJB设计模式Q?/SPAN>[?/SPAN>]Floyd Marinescu EJB 2.0企业U应用程序开发,[?/SPAN>]Chuck CavanessQ?/SPAN>Brian Keeton BEA WebLogic Server宝典Q?/SPAN>[?/SPAN>]Joe Zuffoletto WebLogic Workshop HelpQ?/SPAN>http://e-docs.bea.com/workshop/docs81/doc/en/core/index.html 关于作?/SPAN> 廖雪峎ͼdev2dev论坛IDQ?/SPAN>xuefenglQ?/SPAN> 北京邮电大学在校本科生,信息工程专业Q对J2EE/J2ME开发有厚兴趣?/P> 什么是JNDIQ?/SPAN> The Java Naming and Directory Interface是访问不同名字和目录服务的统一API接口?/SPAN> 不同的服务用不同的名字格式?/SPAN> JavaE序需要以相同的格式访问数据库Q文Ӟ目录Q对象和|络?/SPAN> JNID有两部分接口Q应用程序接口和提供服务的接口。在应用E序中?/SPAN>API来访问名字或目录服务Q在一个新的服务中使用SPI来提供服务?/SPAN> JNDIl构 名字服务Q?/SPAN>Naming ServicesQ?/SPAN> 名字服务提供一U方法,映射标识W到实体或对象?/SPAN> 你需要知道的几条基本条款Q?/SPAN> l定Q?/SPAN>l定是将一个不可分割的名字Q?/SPAN>"原子"名字Q与一个对象联pv来。像DNSQ我们用名字www.yahoo.com?/SPAN>IP地址216.32.74.53联系hQ一个文件对象用文g?/SPAN>afile.txt联系h?/SPAN> 名字I间Q?/SPAN>名字I间包含一l名字,但名字空间内每个名字是唯一的。一个文件目录就是一个简单的名字I间Q如目录C:\tempQ在q个目录下,不能有两个相同名字的文gQ但是,不同目录下的两个文g可能有相同的名字?/SPAN> 复合名字Q?/SPAN>复合名字是用名字I间构成的唯一名字Q有一个或多个"原子"名字构成Q取决于所在的名字I间。文件\径就是一个复合名字,比如我们?/SPAN>C:\temp\myfile.txtQ我们可以看刎ͼq个名字由根目录名(C:\Q,临时目录名(tempQ和一个文件名Q?/SPAN>myfile.txtQ构成,q?/SPAN>3个名字复合v来表CZ个唯一的名字?/SPAN> l合名字Q?/SPAN>l合名字能跨多个名字空间。一?/SPAN>URL是一个组合名字,如果你看?/SPAN>http://www.npu.edu/index.htmQ你使用http服务q接到服务器Q然后用另一个名字空?/SPAN>/index.htm来访问一个文件?/SPAN> 目录服务 目录服务提供一l分成等U的目录对象Q具有可搜烦的能力?/SPAN> 在目录服务中存储的对象可以是M能用一l属性描q的对象Q每个对象都可通过一l属性来描述该对象的能力。例如,一?/SPAN>Person对象可能?/SPAN>heightQ?/SPAN>hair colorQ?/SPAN>ageQ?/SPAN>sex{属性。目录服务还可提供根据要求来搜烦的能力,如我们可以?/SPAN>Person?/SPAN>age属性,搜烦20-25岁间?/SPAN>Person对象Q目录服务将q回W合条g?/SPAN>Persion对象。这通常被称作基于内容的搜烦?/SPAN> 在客L使用JNDIQ?/SPAN> u 创徏一?/SPAN>java.util.Hashtable或?/SPAN>java.util.Properties的实例?/SPAN> u d变量?/SPAN>Hashtable?/SPAN>Properties对象Q?/SPAN> ?/SPAN>naming server提供?/SPAN>JNDI classcd?/SPAN> 包含aming server位置?/SPAN>URL?/SPAN> 安全信Q书?/SPAN> u 通过Hashtable?/SPAN>Properites?/SPAN>jndi属性文件创Z?/SPAN>InitialContext对象?/SPAN> CZQ?/SPAN> import java.util.*; import javax.naming.*; ?o:p> env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL,"t3://localhost:7001"); InitialContext ctx = new InitialContext(env); 环境变量 相应的常?/SPAN> 说明 java.naming.factory.initial Context.INITIAL_CONTEXT_FACTORY Context Factory cdQ由服务提供商给出?/SPAN> java.naming.provider.url Context.PROVIDE_URL 初始化地址?/SPAN> java.naming.security. principal Context.SECURITY_PRINCIPAL 服务使用者信息?/SPAN> java.naming.security. credentials Context.SECURITY_CREDENTIAL 口o?/SPAN> 更多的配|示例: Hashtable env = new Hashtable(); env.put (Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL, "t3://localhost:7001"); env.put(Context.SECURITY_PRINCIPAL, "system"); env.put(Context.SECURITY_CREDENTIALS, "password here"); Properties env = new Properties(); env.setProperties ("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory"); env.setProperties("java.naming.provider.url" , "t3://localhost:7001"); env.setProperties("java.naming.security.principal" , "tommy"); env.setProperties("java.naming.security.credentials" ,"password here"); 创徏InitialContextQ?/SPAN> Class Name: javax.naming.InitialContext Interfaces that it implements: javax.naming.Context Constructors: public InitialContext(); public InitialContext(Hashtable configuration); public InitialContext(Properties configuration); 以上所有方法都可能抛出NamingException?/SPAN> 一?/SPAN>BindingCZQ?/SPAN> public static InitialContext getInitialContext() throws NamingException { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL,"t3://localhost:7001"); InitialContext context = new InitialContext(env); return context; } //Obtain the initial context InitialContext initialContext = getInitialContext(); //Create a Bank object. Bank myBank = new Bank(); //Bind the object into the JNDI tree. initialContext.rebind("theBank",myBank); 一?/SPAN>LookupCZQ?/SPAN> public static InitialContext getInitialContext() throws NamingException { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL,"t3://localhost:7001"); InitialContext context = new InitialContext(env); return context; } //Obtain the initial context InitialContext initialContext = getInitialContext(); //Lookup an existing Bank object. Bank myBank = (Bank) initialContext.lookup("theBank"); 可能发生?/SPAN>NamingExceptionQ?/SPAN> AuthenticationException CommunicationException InvalidNameException NameNotFoundException NoInitialContextException 枚D所有名字对象: NamingEnumeration Declaration: public interface NamingEnumeration extends Enumeration { public boolean hashMore() throws NamingException; public Object next() throws NamingException; public void close() throws NamingException; //jndi 1.2 } try { ?o:p> NamingEnumeration enum = ctx.list(""); while (enum.hasMore()) { NameClassPair ncp = (NameClassPair) enum.next(); System.out.println("JNDI name is:" + ncp.getName()); } } catch (NamingException e) {…} 最后的CZQ?/SPAN> import java.util.*; import javax.naming.*; import javax.naming.directory.*; import java.io.*; public class ListAll { public static void main(java.lang.String[] args) { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL, "t3://localhost:7001"); try { InitialContext ctx = new InitialContext(env); NamingEnumeration enum = ctx.listBindings(""); while(enum.hasMore()) { Binding binding = (Binding) enum.next(); Object obj = (Object) binding.getObject(); System.out.println(obj); } } catch (NamingException e) { System.out.println(e); } } // end main } // end List common-fileuploadlg是apache的一个开源项目之一Q可以从http://jakarta.apache.org/commons/fileupload/下蝲。该lg单易用,可实Cơ上传一个或多个文gQƈ可限制文件大?/P>
下蝲后解压zip包,commons-fileupload-1.0.jar复制到tomcat的webapps\你的webapp\WEB-INF\lib\下,如果目录不存在请自徏目录?/P>
新徏一个servlet: Upload.java用于文g上传Q?/P>
import java.io.*; private String uploadPath = "C:\\upload\\"; // 用于存放上传文g的目?BR> private String tempPath = "C:\\upload\\tmp\\"; // 用于存放临时文g的目?/FONT> public void doPost(HttpServletRequest request, HttpServletResponse response) 当servlet收到览器发出的Posth后,在doPost()Ҏ中实现文件上传。以下是CZ代码Q?/P>
public void doPost(HttpServletRequest request, HttpServletResponse response) // 得到所有的文gQ?BR> List fileItems = fu.parseRequest(request); 如果要在配置文g中读取指定的上传文g夹,可以在init()Ҏ中执行: public void init() throws ServletException { ~译该servletQ注意要指定classpathQ确保包含commons-upload-1.0.jar和tomcat\common\lib\servlet-api.jar?/P>
配置servletQ用C本打开tomcat\webapps\你的webapp\WEB-INF\web.xmlQ没有的话新Z个。典型配|如下: <?xml version="1.0" encoding="ISO-8859-1"?> <web-app> <servlet-mapping> 配置好servlet后,启动tomcatQ写一个简单的html试Q?/P>
注意action="fileupload"其中fileupload是配|servlet时指定的url-pattern?/P>
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.fileupload.*;
public class Upload extends HttpServlet {
throws IOException, ServletException
{
}
}
throws IOException, ServletException
{
try {
DiskFileUpload fu = new DiskFileUpload();
// 讄最大文件尺寸,q里?MB
fu.setSizeMax(4194304);
// 讄~冲区大,q里?kb
fu.setSizeThreshold(4096);
// 讄临时目录Q?BR> fu.setRepositoryPath(tempPath);
Iterator i = fileItems.iterator();
// 依次处理每一个文Ӟ
while(i.hasNext()) {
FileItem fi = (FileItem)i.next();
// 获得文g名,q个文g名包括\径:
String fileName = fi.getName();
if(fileName!=null) {
// 在这里可以记录用户和文g信息
// ...
// 写入文ga.txtQ你也可以从fileName中提取文件名Q?BR> fi.write(new File(uploadPath + "a.txt"));
}
}
// 跌{C传成功提C页?BR> }
catch(Exception e) {
// 可以跌{出错面
}
}
uploadPath = ....
tempPath = ....
// 文g夹不存在p动创建:
if(!new File(uploadPath).isDirectory())
new File(uploadPath).mkdirs();
if(!new File(tempPath).isDirectory())
new File(tempPath).mkdirs();
}
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
<servlet>
<servlet-name>Upload</servlet-name>
<servlet-class>Upload</servlet-class>
</servlet>
<servlet-name>Upload</servlet-name>
<url-pattern>/fileupload</url-pattern>
</servlet-mapping>
</web-app>