Java技术回顾之JNDI--实例
JNDI 技術是Java EE規范中的一個重要“幕后”角色,它為Java EE容器、組件提供者和應用程序之間提供了橋梁作用:Java EE容器同時扮演JNDI提供者角色,組件提供者將某個服務的具體實現部署到容器上,應用程序通過標準的JNDI接口就可以從容器上發現并使用服務,而不用關心服務的具體實現是什么,它的具體位置在哪里。
下面以一個常見的J2EE應用場景來看四種角色(組件接口、容器、組件提供者、應用程序)是如何圍繞JNDI來發揮作用的:
組件接口
數據源DataSource是一種很常見的服務。我們通常將組件接口綁定到容器的Context上供客戶調用。
Java EE容器
Tomcat是一種常見的Java EE容器,其他的還有JBoss,WebLogic,它們同時也實現了JNDI提供者規范。容器通常提供一個JNDI注入場所供加入組件的具體實現,比如 Tomcat中的Server.xml配置文件。
組件提供者
眾多數據庫廠商提供了DataSource的實現,比如OracleDataSource,MySQLDataSource,XXXDataSource 等。我們將該實現的部署到容器中:將一系列jar加入classpath中,在Server.xml中配置DataSource實現,如:
<Resource name="jdbc/MyDB" auth="Container" type="javax.sql.DataSource" ..../>
應用程序
一個JSP/Servlet應用程序。通過JNDI接口使用 DataSource服務,如:
Context initContext = new InitialContext();
Context envContext? = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/MyDB");
關于在Tomcat中如何配置DataSource,可以參考文檔:http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html。
關于在Tomcat中如何配置其他JNDI服務,可以參考文檔:http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html。
二、 JNDI實例演練:在Java SE中使用JNDI
要在Java EE中環境中提供一個獨立的實例不太容易。下面以一個獨立的Java SE應用來演練JNDI的使用過程。在該應用中,我們使用JNDI來使用兩種假想的服務:數據庫服務DBService和日志服務LogService。
1、指定JNDI提供者
要使用JNDI,首先要配置JNDI提供者。在我們的 Java SE應用中,沒有Java EE容器充當JNDI提供者,因此我們要指定其他的JNDI提供者。在我們的例子里,我們使用SUN的文件系統服務提供者File System Service Provider。由于SUN的文件系統服務提供者并沒有包含在JDK中,我們需要從SUN網站上下載:http://java.sun.com/products/jndi/downloads/index.html。它包含兩個jar文件:fscontext.jar和providerutil.jar。我們將這兩個文件加入項目的類路徑中。
2、定義服務接口
首先,我們在服務接口package(xyz.service)中定義服務接口:DBService和LogService,分別表示數據庫服務和日志服務。
? 數據庫服務接口 DBService.java
?日志服務接口 LogService.java
package xyz.service;public interface LogService {void log(String message); //記錄日志 } 3、組件提供者實現服務接口
接下來,我們在組件提供者package(xyz.serviceprovider)中提供DBService和LogService的實現:SimpleDBService和SimpleLogService。為了讓服務能夠在JNDI環境中使用,根據JNDI規范,我們同時定義兩個對象工廠類SimpleDBServiceFactory和SimpleLogServiceFactory,分別用來創建服務實例。
? 數據庫服務接口實現? SimpleDBService.java
package xyz.serviceprovider;import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; import javax.naming.StringRefAddr;import xyz.service.DBService;// 為了將數據庫服務實例加入JNDI的Context中,我們需要實現Referenceable接口,并實現RetReference方法。 // 關于Reference和Referenceable,請參考上一篇:Java技術回顧之JNDI:JNDI API public class SimpleDBService implements Referenceable, DBService {private String location = "mydb//local:8421/defaultdb";// 數據庫服務屬性之一:數據庫位置private String state = "start"; // 數據庫服務屬性之二:數據庫狀態public Reference getReference() throws NamingException {// Reference是對象的引用,Context中存放的是Reference,為了從Reference中還原出對象實例,// 我們需要添加RefAddr,它們是創建對象實例的線索。在我們的// SimpleDBService中,location和state是這樣兩個線索。Reference ref = new Reference(getClass().getName(),SimpleDBServiceFactory.class.getName(), null);ref.add(new StringRefAddr("location", location));ref.add(new StringRefAddr("state", state));return ref;}public void accessDB() {if (state.equals("start"))System.out.println("we are accessing DB.");elseSystem.out.println("DB is not start.");}public String getLocation() {return location;}public String getState() {return state;}public void setProperty(int index, String property) {if (index == 0)location = property;elsestate = property;} } ?數據庫服務對象工廠類 SimpleDBServiceFactory.java
package xyz.serviceprovider;import java.util.Hashtable;import javax.naming.Context; import javax.naming.Name; import javax.naming.Reference; import javax.naming.spi.ObjectFactory;// 數據庫服務對象工廠類被JNDI提供者調用來創建數據庫服務實例,對使用JNDI的客戶不可見。 public class SimpleDBServiceFactory implements ObjectFactory {// 根據Reference中存儲的信息創建出SimpleDBService實例public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable<?, ?> env) throws Exception {if (obj instanceof Reference) {Reference ref = (Reference) obj;String location = (String) ref.get("location").getContent();String state = (String) ref.get("state").getContent();SimpleDBService db = new SimpleDBService();db.setProperty(0, location);db.setProperty(1, state);return db;}return null;} } ? 日志服務接口實現 SimpleLogService.java
package xyz.serviceprovider;import java.text.SimpleDateFormat; import java.util.Date;import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable;import xyz.service.LogService;public class SimpleLogService implements Referenceable, LogService {private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// SimpleLogService沒有任何屬性,通過 SimpleLogService類名創建出來的SimpleLogService實例都是一樣的,// 因此這里無需添加RefAddr了。public Reference getReference() throws NamingException {return new Reference(getClass().getName(),SimpleLogServiceFactory.class.getName(), null);}public void log(String message) {String date = sdf.format(new Date());System.out.println(date + ":" + message);} } ?日志服務對象工廠類 SimpleLogServiceFactory.java
package xyz.serviceprovider;import java.util.Hashtable;import javax.naming.Context; import javax.naming.Name; import javax.naming.Reference; import javax.naming.spi.ObjectFactory;public class SimpleLogServiceFactory implements ObjectFactory {public Object getObjectInstance(Object obj, Name name, Context ctx,Hashtable<?, ?> env) throws Exception {if (obj instanceof Reference) {return new SimpleLogService();}return null;} } ?4、 JNDI容器和JNDI客戶端
最后,我們在JNDI應用package(xyz.jndi)中實現一個JNDI容器 JNDIContainer和一個JNDI客戶端應用JNDIClient。
JNDIContainer在內部使用文件系統服務提供者 fscontext來提供命名和目錄服務,配置文件JNDIContainer.properties是服務注入場所,供配置DBService和 LogService實現。
?JNDI容器類 JNDIContainer.java
package xyz.jndi;import java.io.InputStream; import java.util.Hashtable; import java.util.Properties;import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException;import xyz.service.DBService; import xyz.service.LogService;public class JNDIContainer {private Context ctx = null;public void init() throws Exception {// 初始化JNDI提供者。Hashtable<Object,Object> env = new Hashtable<Object,Object>();env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");env.put(Context.PROVIDER_URL, "file:/d:/works"); // fscontext的初始目錄,我們需要在d:\下創建works目錄。ctx = new InitialContext(env);loadServices();}// 從配置文件 JNDIContainer.properties中讀取DBService和LogService實現,綁定到Context中。private void loadServices() throws Exception {InputStream in = getClass().getResourceAsStream("JNDIContainer.properties");Properties props = new Properties();props.load(in);// inject dbserviceString s = props.getProperty("DBServiceClass");Object obj = Class.forName(s).newInstance();if (obj instanceof DBService) {DBService db = (DBService) obj;String[] ss = props.getProperty("DBServiceProperty").split(";");for (int i = 0; i < ss.length; i++)db.setProperty(i, ss[i]);ctx.rebind(props.getProperty("DBServiceName"), db);}// inject logservices = props.getProperty("LogServiceClass");obj = Class.forName(s).newInstance();if (obj instanceof LogService) {LogService log = (LogService) obj;ctx.rebind(props.getProperty("LogServiceName"), log);}}public void close() throws NamingException {ctx.close();}public Context getContext() {return ctx;} } ?JNDI 容器配置文件 JNDIContainer.properties
//和 JNDIContainer.java文件位于同一目錄 DBServiceName=DBService DBServiceClass=xyz.serviceprovider.SimpleDBService DBServiceProperty=mydb//192.168.1.2:8421/testdb;startLogServiceName=LogService LogServiceClass=xyz.serviceprovider.SimpleLogService
?JNDI 客戶端 JNDIClient.java
package xyz.jndi;import javax.naming.Context;import xyz.service.DBService; import xyz.service.LogService;public class JNDIClient {public static void main(String[] args) {try {JNDIContainer container = new JNDIContainer();container.init();// JNDI客戶端使用標準JNDI接口訪問命名服務。Context ctx = container.getContext();DBService db = (DBService) ctx.lookup("DBService");System.out.println("db location is:" + db.getLocation()+ ",state is:" + db.getState());db.accessDB();LogService ls = (LogService) ctx.lookup("LogService");ls.log("this is a log message.");container.close();} catch (Exception e) {e.printStackTrace();}} }
總結
以上是生活随笔為你收集整理的Java技术回顾之JNDI--实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PostgreSQL 常见操作
- 下一篇: Effective Java~57. 将