一、JNDI在Java EE中的應(yīng)用
JNDI技術(shù)是Java EE規(guī)范中的一個(gè)重要“幕后”角色,它為Java EE容器、組件提供者和應(yīng)用程序之間提供了橋梁作用:JavaEE容器同時(shí)扮演JNDI提供者角色,組件提供者將某個(gè)服務(wù)的具體實(shí)現(xiàn)部署到容器上,應(yīng)用程序通過(guò)標(biāo)準(zhǔn)的JNDI接口就可以從容器上發(fā)現(xiàn)并使用服務(wù),而不用關(guān)心服務(wù)的具體實(shí)現(xiàn)是什么,它的具體位置在哪里。
下面以一個(gè)常見(jiàn)的J2EE應(yīng)用場(chǎng)景來(lái)看四種角色(組件接口、容器、組件提供者、應(yīng)用程序)是如何圍繞JNDI來(lái)發(fā)揮作用的:
組件接口
數(shù)據(jù)源DataSource是一種很常見(jiàn)的服務(wù)。我們通常將組件接口綁定到容器的Context上供客戶(hù)調(diào)用。
Java EE容器
Tomcat是一種常見(jiàn)的JavaEE容器,其他的還有JBoss,WebLogic,它們同時(shí)也實(shí)現(xiàn)了JNDI提供者規(guī)范。容器通常提供一個(gè)JNDI注入場(chǎng)所供加入組件的具體實(shí)現(xiàn),比如Tomcat中的Server.xml配置文件。
組件提供者
眾多數(shù)據(jù)庫(kù)廠商提供了DataSource的實(shí)現(xiàn),比如OracleDataSource,MySQLDataSource,XXXDataSource等。我們將該實(shí)現(xiàn)的部署到容器中:將一系列jar加入classpath中,在Server.xml中配置DataSource實(shí)現(xiàn),如:
<Resourcename="jdbc/MyDB" auth="Container" type="javax.sql.DataSource" ..../>
應(yīng)用程序
一個(gè)JSP/Servlet應(yīng)用程序。通過(guò)JNDI接口使用DataSource服務(wù),如:
Context initContext = new InitialContext();
ContextenvContext = (Context)initContext.lookup("java:/comp/env");
DataSourceds = (DataSource)envContext.lookup("jdbc/MyDB");
關(guān)于在Tomcat中如何配置DataSource,可以參考文檔:http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html。
關(guān)于在Tomcat中如何配置其他JNDI服務(wù),可以參考文檔:http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html。
二、JNDI實(shí)例演練:在Java SE中使用JNDI
要在JavaEE中環(huán)境中提供一個(gè)獨(dú)立的實(shí)例不太容易。下面以一個(gè)獨(dú)立的JavaSE應(yīng)用來(lái)演練JNDI的使用過(guò)程。在該應(yīng)用中,我們使用JNDI來(lái)使用兩種假想的服務(wù):數(shù)據(jù)庫(kù)服務(wù)DBService和日志服務(wù)LogService。
1、指定JNDI提供者
要使用JNDI,首先要配置JNDI提供者。在我們的Java SE應(yīng)用中,沒(méi)有JavaEE容器充當(dāng)JNDI提供者,因此我們要指定其他的JNDI提供者。在我們的例子里,我們使用SUN的文件系統(tǒng)服務(wù)提供者File SystemService Provider。由于SUN的文件系統(tǒng)服務(wù)提供者并沒(méi)有包含在JDK中,我們需要從SUN網(wǎng)站上下載:http://java.sun.com/products/jndi/downloads/index.html。它包含兩個(gè)jar文件:fscontext.jar和providerutil.jar。我們將這兩個(gè)文件加入項(xiàng)目的類(lèi)路徑中。
2、定義服務(wù)接口
首先,我們?cè)诜?wù)接口package(xyz.service)中定義服務(wù)接口:DBService和LogService,分別表示數(shù)據(jù)庫(kù)服務(wù)和日志服務(wù)。
♦數(shù)據(jù)庫(kù)服務(wù)接口 DBService.java
package xyz.service;
public interface DBService {
StringgetLocation(); //獲取數(shù)據(jù)庫(kù)位置
String getState(); //獲取數(shù)據(jù)庫(kù)狀態(tài)
voidaccessDB(); //訪問(wèn)數(shù)據(jù)庫(kù)
void setProperty(int index,Stringproperty); //設(shè)置數(shù)據(jù)庫(kù)信息
}
♦日志服務(wù)接口 LogService.java
package xyz.service;
public interface LogService {
voidlog(String message); //記錄日志
}
3、組件提供者實(shí)現(xiàn)服務(wù)接口
接下來(lái),我們?cè)诮M件提供者package(xyz.serviceprovider)中提供DBService和LogService的實(shí)現(xiàn):SimpleDBService和SimpleLogService。為了讓服務(wù)能夠在JNDI環(huán)境中使用,根據(jù)JNDI規(guī)范,我們同時(shí)定義兩個(gè)對(duì)象工廠類(lèi)SimpleDBServiceFactory和SimpleLogServiceFactory,分別用來(lái)創(chuàng)建服務(wù)實(shí)例。
♦數(shù)據(jù)庫(kù)服務(wù)接口實(shí)現(xiàn) SimpleDBService.java
package xyz.serviceprovider;
importjavax.naming.NamingException;
import javax.naming.Reference;
importjavax.naming.Referenceable;
import javax.naming.StringRefAddr;
import xyz.service.DBService;
//為了將數(shù)據(jù)庫(kù)服務(wù)實(shí)例加入JNDI的Context中,我們需要實(shí)現(xiàn)Referenceable接口,并實(shí)現(xiàn)RetReference方法。
//關(guān)于Reference和Referenceable,請(qǐng)參考上一篇:Java技術(shù)回顧之JNDI:JNDI API
public class SimpleDBService implementsReferenceable, DBService {
private Stringlocation="mydb//local:8421/defaultdb";//數(shù)據(jù)庫(kù)服務(wù)屬性之一:數(shù)據(jù)庫(kù)位置
privateString state="start"; //數(shù)據(jù)庫(kù)服務(wù)屬性之二:數(shù)據(jù)庫(kù)狀態(tài)
public Reference getReference()throws NamingException {
//Reference是對(duì)象的引用,Context中存放的是Reference,為了從Reference中還原出對(duì)象實(shí)例,
//我們需要添加RefAddr,它們是創(chuàng)建對(duì)象實(shí)例的線索。在我們的SimpleDBService中,location和state是這樣兩個(gè)線索。
Reference ref=newReference(getClass().getName(),SimpleDBServiceFactory.class.getName(),null);
ref.add(newStringRefAddr("location",location));
ref.add(newStringRefAddr("state",state));
return ref;
}
public void accessDB() {
if(state.equals("start"))
System.out.println("weare accessing DB.");
else
System.out.println("DB is notstart.");
}
public String getLocation() {
returnlocation;
}
public String getState() {
return state;
}
public void setProperty(intindex,String property){
if(index==0)
location=property;
else
state=property;
}
}
♦數(shù)據(jù)庫(kù)服務(wù)對(duì)象工廠類(lèi) SimpleDBServiceFactory.java
package xyz.serviceprovider;
import java.util.Hashtable;
import javax.naming.Context;
importjavax.naming.Name;
import javax.naming.Reference;
importjavax.naming.spi.ObjectFactory;
//數(shù)據(jù)庫(kù)服務(wù)對(duì)象工廠類(lèi)被JNDI提供者調(diào)用來(lái)創(chuàng)建數(shù)據(jù)庫(kù)服務(wù)實(shí)例,對(duì)使用JNDI的客戶(hù)不可見(jiàn)。
public class SimpleDBServiceFactory implementsObjectFactory {
//根據(jù)Reference中存儲(chǔ)的信息創(chuàng)建出SimpleDBService實(shí)例
publicObject getObjectInstance(Object obj, Name name, Context ctx,
Hashtable<?,?> env) throws Exception {
if(obj instanceof Reference){
Referenceref=(Reference)obj;
Stringlocation=(String)ref.get("location").getContent();
Stringstate=(String)ref.get("state").getContent();
SimpleDBService db=new SimpleDBService();
db.setProperty(0, location);
db.setProperty(1,state);
return db;
}
return null;
}
}
♦日志服務(wù)接口實(shí)現(xiàn) SimpleLogService.java
packagexyz.serviceprovider;
import java.text.SimpleDateFormat;
importjava.util.Date;
import javax.naming.NamingException;
importjavax.naming.Reference;
import javax.naming.Referenceable;
import xyz.service.LogService;
public class SimpleLogServiceimplements Referenceable, LogService {
private SimpleDateFormatsdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//SimpleLogService沒(méi)有任何屬性,通過(guò)SimpleLogService類(lèi)名創(chuàng)建出來(lái)的SimpleLogService實(shí)例都是一樣的,
//因此這里無(wú)需添加RefAddr了。
publicReference getReference() throws NamingException {
return newReference(getClass().getName(),SimpleLogServiceFactory.class.getName(),null);
}
聯(lián)系客服