轉載:http://terrencexu.javaeye.com/blog/715982
Discovery組件被用以查找可插拔接口的實現實例,它提供了一種通用的實例化這些實現的方式,而且可以管理單例(工廠)的生命周期。本質上來講,就是定位那些實現了給定Java接口的類,并實例化。除此之外,Discovery還可以用以在給定的classpath中查找并加載資源文件。
Discovery組件在查找所有的實現類的時候需要預先將允許被查找的實現類配置到默認的配置文件中,默認的配置文件為:
/META-INF/services/<YOUR Interface whole name including pkg name>, Discovery將依次加載該文件中配置的允許加載的實現類。
下面將舉例說明:
首先定義一個Interface:Action
- package com.javaeye.terrencexu.discovery;
-
- public interface Action {
-
- public String getName();
-
- }
package com.javaeye.terrencexu.discovery;
public interface Action {
public String getName();
}
然后在不同的包里分別實現Action接口,如下(請注意包名)
- package com.javaeye.terrencexu.discovery.impl;
-
- import com.javaeye.terrencexu.discovery.Action;
-
- public class CreateAction implements Action {
-
- @Override
- public String getName() {
- return "Create Action";
- }
-
- }
package com.javaeye.terrencexu.discovery.impl;
import com.javaeye.terrencexu.discovery.Action;
public class CreateAction implements Action {
@Override
public String getName() {
return "Create Action";
}
}
- package com.javaeye.terrencexu.discovery.impl;
-
- import com.javaeye.terrencexu.discovery.Action;
-
- public class DeleteAction implements Action {
-
- @Override
- public String getName() {
- return "Delete Action";
- }
-
- }
package com.javaeye.terrencexu.discovery.impl;
import com.javaeye.terrencexu.discovery.Action;
public class DeleteAction implements Action {
@Override
public String getName() {
return "Delete Action";
}
}
- package com.javaeye.terrencexu.discovery.impl2;
-
- import com.javaeye.terrencexu.discovery.Action;
-
- public class AddAction implements Action {
-
- @Override
- public String getName() {
- return "Add Action";
- }
-
- }
package com.javaeye.terrencexu.discovery.impl2;
import com.javaeye.terrencexu.discovery.Action;
public class AddAction implements Action {
@Override
public String getName() {
return "Add Action";
}
}
- package com.javaeye.terrencexu.discovery.impl2;
-
- import com.javaeye.terrencexu.discovery.Action;
-
- public class RemoveAction implements Action {
-
- @Override
- public String getName() {
- return "Remove Action";
- }
-
- }
package com.javaeye.terrencexu.discovery.impl2;
import com.javaeye.terrencexu.discovery.Action;
public class RemoveAction implements Action {
@Override
public String getName() {
return "Remove Action";
}
}
接下來將定義配置文件,按序定義允許被加載的實現類,該文件默認存在位置為/META-INF/services/,文件名為com.javaeye.terrencexu.discovery.Action,文件內容如下:
- # Display in order
-
- com.javaeye.terrencexu.discovery.impl.CreateAction
- com.javaeye.terrencexu.discovery.impl.DeleteAction
- com.javaeye.terrencexu.discovery.impl2.AddAction
# Display in order
com.javaeye.terrencexu.discovery.impl.CreateAction
com.javaeye.terrencexu.discovery.impl.DeleteAction
com.javaeye.terrencexu.discovery.impl2.AddAction
這樣所有的準備材料就都已經齊全了,接下來可以驗證一把了,如下:
-
-
-
-
-
- @SuppressWarnings("unchecked")
- public void testGetAllProviders() {
- String[] expectedResults = new String[] {"Create Action", "Delete Action", "Add Action"};
-
- Enumeration<Action> enu = Service.providers(Action.class);
-
- int count = 0;
- while (enu.hasMoreElements()) {
- Action action = enu.nextElement();
- assertTrue("The action name should be \"" + expectedResults[count] + "\", but actually is \"" + action.getName() + "\"",
- action.getName().equals(expectedResults[count]));
- count ++;
- }
-
- assertEquals(count, expectedResults.length);
- }
/**
* CreateAction/DeleteAction/AddAction have been defined in /META-INF/services/com.javaeye.terrencexu.discovery.Action
*
* And the order is CreateAction > DeleteAction > AddAction
*/
@SuppressWarnings("unchecked")
public void testGetAllProviders() {
String[] expectedResults = new String[] {"Create Action", "Delete Action", "Add Action"};
Enumeration<Action> enu = Service.providers(Action.class);
int count = 0;
while (enu.hasMoreElements()) {
Action action = enu.nextElement();
assertTrue("The action name should be \"" + expectedResults[count] + "\", but actually is \"" + action.getName() + "\"",
action.getName().equals(expectedResults[count]));
count ++;
}
assertEquals(count, expectedResults.length);
}
可以發現,因為RemoveAction沒有被配置到service文件中,所以將不會被加載,另外一點兒就是,配置文件中的定義順序即加載順序。
除此之外,Discovery提供了singleton模式加載唯一實現,并且該實現將被緩存在cache中,除非通過顯示的調用release()方法釋放緩存,否則所有之后的調用,都將返回初次調用加載的Action。
- public void testFindCreateAction() {
- try {
-
- Action createAction = (Action) DiscoverSingleton.find(Action.class, CreateAction.class.getName());
- assertEquals("Create Action", createAction.getName());
- } finally {
- DiscoverSingleton.release();
- }
- }
public void testFindCreateAction() {
try {
// Load provider com.javaeye.terrencexu.discovery.impl.CreateAction
Action createAction = (Action) DiscoverSingleton.find(Action.class, CreateAction.class.getName());
assertEquals("Create Action", createAction.getName());
} finally {
DiscoverSingleton.release();
}
}
還有一點需要說明的是,如果定義了默認的service文件,無論通過singleton模式加載的實現類有沒有被配置在service文件中,都將默認加載配置中文中的第一個Action,比如上文中的CreateAction。
- public void testFindDeleteActionInConfig() {
- try {
-
- Action deleteAction = (Action) DiscoverSingleton.find(Action.class, DeleteAction.class.getName());
-
-
- assertEquals("Create Action", deleteAction.getName());
- } finally {
- DiscoverSingleton.release();
- }
- }
public void testFindDeleteActionInConfig() {
try {
// Load provider com.javaeye.terrencexu.discovery.impl.CreateAction
Action deleteAction = (Action) DiscoverSingleton.find(Action.class, DeleteAction.class.getName());
// As the default configuration file defines the CreateAction as the first element, so you will always get the CreateAction as singleton.
assertEquals("Create Action", deleteAction.getName());
} finally {
DiscoverSingleton.release();
}
}
那么如果必須使用service文件,又想通過singleton模式加載某特定的實現類該怎么辦呢?可以通過傳遞Properties到DiscoverSingleton中去改變它的行為,如下:
- public void testFindDeleteActionWithProperty() {
- try {
- Properties props = new Properties();
- props.setProperty(Action.class.getName(), DeleteAction.class.getName());
-
-
- Action deleteAction = (Action) DiscoverSingleton.find(Action.class, props);
- assertEquals("Delete Action", deleteAction.getName());
- } finally {
- DiscoverSingleton.release();
- }
- }
public void testFindDeleteActionWithProperty() {
try {
Properties props = new Properties();
props.setProperty(Action.class.getName(), DeleteAction.class.getName());
// Load provider com.javaeye.terrencexu.discovery.impl.CreateAction
Action deleteAction = (Action) DiscoverSingleton.find(Action.class, props);
assertEquals("Delete Action", deleteAction.getName());
} finally {
DiscoverSingleton.release();
}
}
除了加載類之外,很多情況下我們還想加載資源文件,比如在你的classpath下有一個配置文件為/conf/testResource,下面我們通過Discovery去加載該資源文件:
- public void testFindResources() {
- ClassLoaders loaders = new ClassLoaders();
- ClassLoader cl = getClass().getClassLoader();
- if(cl != null) {
- loaders.put(getClass().getClassLoader(), true);
- } else {
- loaders.put(JDKHooks.getJDKHooks().getSystemClassLoader(), true);
- }
-
- String name = "conf/testResource";
- DiscoverResources discovery = new DiscoverResources(loaders);
- ResourceIterator iter = discovery.findResources(name);
-
- while(iter.hasNext()) {
- Resource resource = iter.nextResource();
- URL url = resource.getResource();
- System.out.println(url);
- }
- }
public void testFindResources() {
ClassLoaders loaders = new ClassLoaders();
ClassLoader cl = getClass().getClassLoader();
if(cl != null) {
loaders.put(getClass().getClassLoader(), true);
} else {
loaders.put(JDKHooks.getJDKHooks().getSystemClassLoader(), true);
}
String name = "conf/testResource";
DiscoverResources discovery = new DiscoverResources(loaders);
ResourceIterator iter = discovery.findResources(name);
while(iter.hasNext()) {
Resource resource = iter.nextResource();
URL url = resource.getResource();
System.out.println(url);
}
}
Discovery還有其他一些功能這里就不在詳細的一一贅述了,可以參考http://commons.apache.org/discovery/index.html進一步詳細了解。
下圖是我的例子的目錄結構,僅供參考:

然后附件中有source code,僅供參考。