2、DubboのSPIメカニズム分析1-SPIロードclass
1、DubboのSPI例
2、DubboのSPIソース分析
ここでのobjectFactory作成は参考になる:DubboのSPIメカニズム解析3-DubboのIOC依存注入
ここで依存注入は参考になる:DubboのSPIメカニズム解析3-DubboのIOC依存注入
ここではDubboのSPIメカニズム解析2-Adaptive詳細2を参照してください.ここではDubboのSPIメカニズム解析4-DubboがWrapperによってAOPを実現することを参照してください.
@SPI
public interface Robot {
void sayHello();
}
public class OptimusPrime implements Robot{
@Override
public void sayHello() {
System.out.println("Hello, I am Optimus Prime.");
}
}
public class Bumblebee implements Robot{
@Override
public void sayHello() {
System.out.println("Hello, I am Bumblebee.");
}
}
public class DubboSPITest {
@Test
public void sayHelloDubbo() throws Exception {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
optimusPrime.sayHello();
Robot bumblebee = extensionLoader.getExtension("bumblebee");
bumblebee.sayHello();
}
}
:
Hello, I am Optimus Prime.
Hello, I am Bumblebee.
2、DubboのSPIソース分析
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
public static ExtensionLoader getExtensionLoader(Class type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
// ExtensionLoader
ExtensionLoader loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);
if (loader == null) {
// , , new ExtensionLoader(type)
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);
}
return loader;
}
ここでのobjectFactory作成は参考になる:DubboのSPIメカニズム解析3-DubboのIOC依存注入
private ExtensionLoader(Class> type) {
this.type = type;
// type Robot.class, , SpiExtensionFactory SpringExtensionFactory,
objectFactory = (type == ExtensionFactory.class ? null :
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
public T getExtension(String name) {
if ("true".equals(name)) {
return getDefaultExtension();
}
// Holder ,
Holder
ここで依存注入は参考になる:DubboのSPIメカニズム解析3-DubboのIOC依存注入
private T createExtension(String name) {
// , “ ” “ ”
// name , com.alibaba.dubbo.demo.provider.spi.OptimusPrime
// getExtensionClasses(),
Class> clazz = getExtensionClasses().get(name);
try {
// ,
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// cachedWrapperClasses,
injectExtension(instance);
Set> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
}
}
private Map> getExtensionClasses() {
//
Map> classes = cachedClasses.get();
//
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// ,
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
private Map> loadExtensionClasses() {
// SPI , type getExtensionLoader
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
//
}
// @SPI , , ,
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map> extensionClasses = new HashMap>();
// ,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
//
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
private void loadDirectory(Map> extensionClasses, String dir) {
// fileName META-INF/dubbo/com.alibaba.dubbo.demo.provider.spi.Robot
String fileName = dir + type.getName();
try {
Enumeration urls;
ClassLoader classLoader = findClassLoader();
//
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
// resourceURL
java.net.URL resourceURL = urls.nextElement();
loadResource(extensionClasses, classLoader, resourceURL);
}
}
}
}
private void loadResource(Map> extensionClasses, ClassLoader classLoader,
java.net.URL resourceURL) {
try {
BufferedReader reader =
new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
String line;
//
while ((line = reader.readLine()) != null) {
final int ci = line.indexOf('#');
// # ,# ,
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
//
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
//
loadClass(extensionClasses, resourceURL,
Class.forName(line, true, classLoader), name);
}
}
}
}
}
}
}
ここではDubboのSPIメカニズム解析2-Adaptive詳細2を参照してください.ここではDubboのSPIメカニズム解析4-DubboがWrapperによってAOPを実現することを参照してください.
private void loadClass(Map> extensionClasses, java.net.URL resourceURL,
Class> clazz, String name) throws NoSuchMethodException {
// clazz type ,
if (!type.isAssignableFrom(clazz)) {
}
// clazz @Adaptive , 1
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
// , @Adaptive
}
}
// Wrapper , 2
else if (isWrapperClass(clazz)) {
Set> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
}
// , clazz ,Robot
else {
// clazz , ,
clazz.getConstructor();
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
//
}
}
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class> c = extensionClasses.get(n);
if (c == null) {
// class ,
extensionClasses.put(n, clazz);
} else if (c != clazz) {
//
}
}
}
}
}