一言不合直接上代码
首先引入dubbo的包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-http</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-api</artifactId>
<version>2.7.5</version>
</dependency>
</dependencies>
接下来就是使用Dubbo的SPI机制来实现动态加载。
与Java的API创建类似
文件中的内容就是对应的实现类
black=com.csf.dubbo.BlackCar
red=com.csf.dubbo.BlackCar
创建一个接口和不同的实现类
package com.csf.dubbo;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface Car {
String getName();
}
package com.csf.dubbo;
public class RedCar implements Car {
@Override
public String getName() {
return "red";
}
}
package com.csf.dubbo;
public class BlackCar implements Car{
@Override
public String getName() {
return "black";
}
}
接下来就是创建一个main方法进行调用。
public static void main(String[] args) {
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car black = extensionLoader.getExtension("black");
System.out.println(black.getName());
}
执行main方法的运行结果,执行了BlackCar的getName
以上Dubbo提供的基本实现。
如果有包装类Wrapper的类,那么在获取black的时候会先获取到包装类,执行包装类的方法。
package com.csf.dubbo;
public class WrapperCar implements Car {
private Car car;
//接口的扩展类还是包装类主要就是看有没有这个方法
public WrapperCar(Car car) {
this.car = car;
}
@Override
public String getName() {
return "wrapper";
}
}
Dubbo中SPI的代理机制
紧接着继续创建Person的接口和实现以及配置文件。
black=com.csf.dubbo.BlackPerson
package com.csf.dubbo;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface Person {
Car getCar();
}
package com.csf.dubbo;
public class BlackPerson implements Person {
private Car car;
public void setCar(Car car) {
this.car = car;
}
@Override
public Car getCar() {
return car;
}
}
package com.csf.dubbo;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.rpc.Protocol;
public class Test {
public static void main(String[] args) {
ExtensionLoader<Person> extensionLoader1 = ExtensionLoader.getExtensionLoader(Person.class);
Person black1 = extensionLoader1.getExtension("black");
System.out.println(black1.getCar().getName());
}
}
ok,很顺利的把写完了,这时候执行结果报错,空指针异常:
这是为什么呢,因为BlackPerson中引入了Car接口,这时候,在实例化BlackPerson对象的时候,需要进行依赖注入,但是此时我们并未指定具体的Car,所以这里Dubbo做了一个处理。这里会生成一个Adaptive代理对象,通过URL来指定具体要实例化的对象。
接下来就是重点了。通过源码解析来说说这个问题
为什么会报空指针异常,这就需要我们来分析一下这几行代码到底做了什么事情
先从第一行开始。
这个很清晰,根据传入的type类型做一些校验,非空,是否为接口,是否有SPI的注解,当然这些个东西都ok。
紧接着就是从 EXTENSION_LOADERS 这个属性中取值,这是一个map对象。里面放的是extensionLoader对象。
这里能看到的就是从EXTENSION_LOADERS这个map里面获取extensionLoader对象,没有获取到就创建一个,放进去。这里看完了,没有问题。
那么重点来了:
Person black1 = extensionLoader1.getExtension("black");
这里到底干了什么事情呢?
if ("true".equals(name)) { return getDefaultExtension(); }
这行代码是如果传的true,就取默认的实现类,
这里就是把对应目录下面接口文件中的实现类都加载了
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst) {
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls = null;
ClassLoader classLoader = findClassLoader();
// try to load from ExtensionLoader's ClassLoader first
if (extensionLoaderClassLoaderFirst) {
ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
urls = extensionLoaderClassLoader.getResources(fileName);
}
}
if(urls == null || !urls.hasMoreElements()) {
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}
这里主要就是解析文件,将文件中的内容解析了,这种key=value的形式就是在这里解析的。
这里有个重点就是将Adaptive,Wrapper,当前类等缓存起来。
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz);
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);
} else {
clazz.getConstructor();
if (StringUtils.isEmpty(name)) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
cacheActivateClass(clazz, names[0]);
for (String n : names) {
cacheName(clazz, n);
saveInExtensionClass(extensionClasses, clazz, n);
}
}
}
}
回到原来的地方。接着走
final Holder<Object> holder = getOrCreateHolder(name);
这里为什么要创建一个holder<Object> 呢。 这是为了处理并发问题。
当流程走到这里的时候还没有创建对应的对象,也就是说对象还没有进行实例化。而下面又是一个经典对象穿件双重非空判断。需要一把锁。这里就是将对应的对象的名字创建一个holder对象,来进行加锁。
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
后面把流程图加上。更加清晰一点。看源码需要方式。