欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习
1 文章概述
SPI(Service Provider Interface)是一种服务发现机制,本质是将接口实现类全限定名配置在文件,并由服务加载器读取配置文件加载实现类,这样可以在运行时动态为接口替换实现类,通过SPI机制可以为程序提供拓展功能。
之前在文章《JDK SPI机制》已经讨论了JDK SPI如何实现,在文章《SLF4J源码角度分析阿里开发手册日志规约》已经讨论了JDK SPI如何应用,本文我们分析DUBBO SPI机制,相较于JDK SPI至少进行了以下功能扩展:
指定具体扩展点指定默认扩展点类级别自适应扩展点方法级别自适应扩展点自实现IOC自实现AOP
2 指定具体扩展点
2.1 代码实例
第一步定义订单接口与订单模型
package com.java.front.dubbo.spi.api.order;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface OrderSpiService {
public boolean createOrder(OrderModel order);
}
public class OrderModel {
private String userId;
private String orderId;
}
第二步实现订单服务
package com.java.front.dubbo.spi.impl.order;
public class OrderSpiAServiceImpl implements OrderSpiService {
@Override
public boolean createOrder(OrderModel order) {
System.out.println("OrderSpiAService createOrder");
return Boolean.TRUE;
}
}
public class OrderSpiBServiceImpl implements OrderSpiService {
@Override
public boolean createOrder(OrderModel order) {
System.out.println("OrderSpiBService createOrder");
return Boolean.TRUE;
}
}
第三步新增配置文件
├─src │ ├─main │ │ └─resources │ │ └─meta-INF │ │ ├─services │ │ │ com.java.front.dubbo.spi.api.order.OrderSpiService
第四步新增配置文件内容
orderSpiAService=com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl orderSpiBService=com.java.front.dubbo.spi.impl.order.OrderSpiBServiceImpl
第五步运行测试代码
public class OrderSpiServiceTest {
public static void main(String[] args) {
test1();
}
public static void test1() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(OrderSpiService.class);
OrderSpiService orderSpiService = extensionLoader.getExtension("orderSpiAService");
orderSpiService.createOrder(new OrderModel());
}
}
第六步输出测试结果
OrderSpiAService createOrder
2.2 源码分析
loadExtensionClasses方法读取相关目录下配置文件并加载实现类
public class ExtensionLoader{ private static final String SERVICES_DIRECTORY = "meta-INF/services/"; private static final String DUBBO_DIRECTORY = "meta-INF/dubbo/"; private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/"; private Map > loadExtensionClasses() { // 省略代码 // KEY表示自定义名称、Value表示具体实现类 // orderSpiAService=class com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl // orderSpiBService=class com.java.front.dubbo.spi.impl.order.OrderSpiBServiceImpl Map > extensionClasses = new HashMap >(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; } private void loadDirectory(Map > extensionClasses, String dir, String type) { String fileName = dir + type; try { Enumeration urls; ClassLoader classLoader = findClassLoader(); 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 when load extension class(interface: " + type + ", description file: " + fileName + ").", t); } } 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); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); exceptions.put(line, e); } } } } finally { reader.close(); } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t); } } }
classes.get(name)根据输入具体名称获取扩展点
public class ExtensionLoader{ private T createExtension(String name) { // 根据name获取对应实现类 Class> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { // 获取实现类对象 T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // 省略代码 } catch (Throwable t) { throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t); } return instance; } }
3 指定默认扩展点 3.1 代码实例
第一步修改订单接口
package com.java.front.dubbo.spi.api.order;
import org.apache.dubbo.common.extension.SPI;
@SPI("orderSpiBService")
public interface OrderSpiService {
public boolean createOrder(OrderModel order);
}
第二步运行测试代码
public class OrderSpiServiceTest {
public static void main(String[] args) {
test2();
}
public static void test2() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(OrderSpiService.class);
OrderSpiService orderSpiService = extensionLoader.getDefaultExtension();
orderSpiService.createOrder(new OrderModel());
}
}
第三步输出测试结果
OrderSpiBService createOrder
3.2 源码分析
getDefaultExtension方法获取默认扩展点
public class ExtensionLoader{ public T getDefaultExtension() { // 加载实现类并设置cachedDefaultName getExtensionClasses(); if (null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName)) { return null; } // 根据默认名称获取扩展点 return getExtension(cachedDefaultName); } }
loadExtensionClasses方法设置默认扩展点名称
public class ExtensionLoader{ private Map > loadExtensionClasses() { // 一个接口只允许有一个默认扩展点 // @SPI("orderSpiBService")表示设置orderSpiBService作为默认扩展点 if (defaultAnnotation != null) { String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if (names.length > 1) { throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if (names.length == 1) { cachedDefaultName = names[0]; } } } // KEY表示自定义名称、Value表示具体实现类 // orderSpiAService=class com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl // orderSpiBService=class com.java.front.dubbo.spi.impl.order.OrderSpiBServiceImpl Map > extensionClasses = new HashMap >(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; } }
4 类级别自适应扩展点 4.1 代码实例
第一步新增订单自适应实现类
package com.java.front.dubbo.spi.impl.order;
import org.apache.dubbo.common.extension.Adaptive;
@Adaptive
public class OrderSpiAdaptiveServiceImpl implements OrderSpiService {
@Override
public boolean createOrder(OrderModel order) {
System.out.println("OrderSpiAdaptiveService createOrder");
return Boolean.TRUE;
}
}
第二步配置文件新增
orderAdaptiveService=com.java.front.dubbo.spi.impl.order.OrderSpiAdaptiveServiceImpl
第三步运行测试代码
public class OrderSpiServiceTest {
public static void main(String[] args) {
test3();
}
public static void test3() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(OrderSpiService.class);
OrderSpiService orderSpiService = extensionLoader.getAdaptiveExtension();
orderSpiService.createOrder(new OrderModel());
}
}
第四步输出测试结果
OrderSpiAdaptiveService createOrder
4.2 源码分析
getAdaptiveExtensionClass方法判断是否存在类级别自适应扩展点,如果存在则直接返回
public class ExtensionLoader{ private Class> getAdaptiveExtensionClass() { // 加载扩展点并设置cachedAdaptiveClass getExtensionClasses(); // 存在类级别自适应扩展点则直接返回 if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } // 不存在类级别自适应扩展点则动态创建 return cachedAdaptiveClass = createAdaptiveExtensionClass(); } }
loadClass方法发现存在类级别自适应扩展点则设置cachedAdaptiveClass
public class ExtensionLoader{ private void loadClass(Map > extensionClasses, java.net.URL resourceURL, Class> clazz, String name) throws NoSuchMethodException { // clazz是否为接口实现类 if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error when load extension class(interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface."); } // 存在类级别自适应扩展点则设置cachedAdaptiveClass if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } // 一个接口只允许有一个类级别自适应扩展点 else if (!cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName()); } } // 省略代码 } }
5 方法级别自适应扩展点 5.1 代码实例
第一步新建库存接口与库存实体,使用方法级别自适应扩展点需要满足以下任意一个条件:
方法必须包含URL输入参数方法必须包含get方法且返回值为URL
package com.java.front.dubbo.spi.api.stock;
import org.apache.dubbo.common.URL;
@SPI("stockA")
public interface StockSpiService {
@Adaptive("bizType")
public boolean reduceStock(String skuId, URL url);
@Adaptive("bizType")
public boolean reduceStock2(StockReduceModel stockReduceModel);
}
public class StockReduceModel {
private String skuId;
private URL url;
public StockReduceModel(String skuId, URL url) {
this.skuId = skuId;
this.url = url;
}
public String getSkuId() {
return skuId;
}
public void setSkuId(String skuId) {
this.skuId = skuId;
}
public URL getUrl() {
return url;
}
public void setUrl(URL url) {
this.url = url;
}
}
第二步实现库存服务
package com.java.front.dubbo.spi.impl.stock;
import org.apache.dubbo.common.URL;
public class StockSpiAServiceImpl implements StockSpiService {
@Override
public boolean reduceStock1(String skuId, URL url) {
System.out.println("StockSpiAService reduceStock1 skuId=" + skuId);
return Boolean.TRUE;
}
@Override
public boolean reduceStock2(StockReduceModel stockReduceModel) {
System.out.println("StockSpiAService reduceStock2 stockReduceModel=" + stockReduceModel);
return Boolean.TRUE;
}
}
public class StockSpiBServiceImpl implements StockSpiService {
@Override
public boolean reduceStock1(String skuId, URL url) {
System.out.println("StockSpiBService reduceStock1 skuId=" + skuId);
return Boolean.TRUE;
}
@Override
public boolean reduceStock2(StockReduceModel stockReduceModel) {
System.out.println("StockSpiBService reduceStock2 stockReduceModel=" + stockReduceModel);
return Boolean.TRUE;
}
}
第三步新增配置文件
├─src │ ├─main │ │ └─resources │ │ └─meta-INF │ │ ├─services │ │ │ com.java.front.dubbo.spi.api.stock.StockSpiService
第四步新增配置文件内容
stockA=com.java.front.dubbo.spi.impl.stock.StockSpiAServiceImpl stockB=com.java.front.dubbo.spi.impl.stock.StockSpiBServiceImpl
第五步运行测试代码
public class StockSpiServiceTest {
public static void main(String[] args) {
test1();
test2();
}
public static void test1() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(StockSpiService.class);
StockSpiService adaptiveService = extensionLoader.getAdaptiveExtension();
Map map = new HashMap<>();
map.put("bizType", "stockB");
URL url = new URL(StringUtils.EMPTY, StringUtils.EMPTY, 1, map);
adaptiveService.reduceStock1("skuId_111", url);
}
public static void test2() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(StockSpiService.class);
StockSpiService adaptiveService = extensionLoader.getAdaptiveExtension();
Map map = new HashMap<>();
map.put("bizType", "stockB");
URL url = new URL(StringUtils.EMPTY, StringUtils.EMPTY, 1, map);
StockReduceModel stockReduceModel = new StockReduceModel("skuId_111", url);
adaptiveService.reduceStock2(stockReduceModel);
}
}
第六步输出测试结果
StockSpiBService reduceStock1 skuId=skuId_111 StockSpiBService reduceStock2 stockReduceModel=StockReduceModel(skuId=skuId_111, url=?bizType=stockB)
5.2 源码分析
getAdaptiveExtensionClass方法判断是否存在类级别自适应扩展点,如果不存在则动态创建
public class ExtensionLoader{ private Class> getAdaptiveExtensionClass() { // 加载扩展点并设置cachedAdaptiveClass getExtensionClasses(); // 存在类级别自适应扩展点则直接返回 if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } // 不存在类级别自适应扩展点则动态创建 return cachedAdaptiveClass = createAdaptiveExtensionClass(); } }
createAdaptiveExtensionClass方法动态生成自适应扩展点代码
public class ExtensionLoader{ private Class> createAdaptiveExtensionClass() { // 动态生成自适应扩展点代码 // 此方法会校验是否满足以下任意一个条件 // 1.方法必须包含URL输入参数 // 2.方法必须包含get方法且返回值为URL String code = createAdaptiveExtensionClassCode(); // javassist编译生成class对象 ClassLoader classLoader = findClassLoader(); org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); } }
StockSpiService$Adaptive为动态生成自适应扩展点代码,我们可以看到URL这个参数作用,可以类比URL为路由器,自适应扩展点根据输入参数决定路由到哪个服务,如果没有值默认路由到stockA服务
package com.java.front.dubbo.spi.api.stock;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class StockSpiService$Adaptive implements com.java.front.dubbo.spi.api.stock.StockSpiService {
public boolean reduceStock1(java.lang.String arg0, org.apache.dubbo.common.URL arg1) {
if (arg1 == null)
throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg1;
String extName = url.getParameter("bizType", "stockA");
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.java.front.dubbo.spi.api.stock.StockSpiService) name from url(" + url.toString() + ") use keys([bizType])");
com.java.front.dubbo.spi.api.stock.StockSpiService extension = (com.java.front.dubbo.spi.api.stock.StockSpiService) ExtensionLoader.getExtensionLoader(com.java.front.dubbo.spi.api.stock.StockSpiService.class).getExtension(extName);
return extension.reduceStock1(arg0, arg1);
}
public boolean reduceStock2(com.java.front.dubbo.spi.model.StockReduceModel arg0) {
if (arg0 == null)
throw new IllegalArgumentException("com.java.front.dubbo.spi.model.StockReduceModel argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.java.front.dubbo.spi.model.StockReduceModel argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("bizType", "stockA");
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.java.front.dubbo.spi.api.stock.StockSpiService) name from url(" + url.toString() + ") use keys([bizType])");
com.java.front.dubbo.spi.api.stock.StockSpiService extension = (com.java.front.dubbo.spi.api.stock.StockSpiService) ExtensionLoader.getExtensionLoader(com.java.front.dubbo.spi.api.stock.StockSpiService.class).getExtension(extName);
return extension.reduceStock2(arg0);
}
}
6 自实现IOC 6.1 代码实例
第一步新增商品接口
package com.java.front.dubbo.spi.api.goods;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface GoodsSpiService {
public boolean buyGoods(String skuId, URL url);
}
第二步实现商品接口
package com.java.front.dubbo.spi.impl.goods;
import org.apache.dubbo.common.URL;
import com.java.front.dubbo.spi.api.goods.GoodsSpiService;
import com.java.front.dubbo.spi.api.stock.StockSpiService;
public class GoodsSpiAServiceImpl implements GoodsSpiService {
private StockSpiService stockSpiService;
public void setStockSpiService(StockSpiService stockSpiService) {
this.stockSpiService = stockSpiService;
}
@Override
public boolean buyGoods(String skuId, URL url) {
System.out.println("GoodsSpiAService buyGoods skuId=" + skuId);
stockSpiService.reduceStock1(skuId, url);
return false;
}
}
第三步新增配置文件
├─src │ ├─main │ │ └─resources │ │ └─meta-INF │ │ ├─services │ │ │ com.java.front.dubbo.spi.api.stock.GoodsSpiService
第四步新增配置文件内容
goodsA=com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl
第五步运行测试代码
public class GoodsServiceTest {
public static void main(String[] args) {
test1();
}
public static void test1() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(GoodsSpiService.class);
GoodsSpiService goodsService = extensionLoader.getExtension("goodsA");
Map map = new HashMap<>();
map.put("bizType", "stockA");
URL url = new URL(StringUtils.EMPTY, StringUtils.EMPTY, 1, map);
goodsService.buyGoods("skuId_111", url);
}
}
第六步输出测试结果
GoodsSpiAService buyGoods skuId=skuId_111 StockSpiAService reduceStock1 skuId=skuId_111
6.2 源码分析
injectExtension方法自实现IOC
public class ExtensionLoader{ private T createExtension(String name) { Class> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { // instance = com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // 自实现IOC injectExtension(instance); // 省略代码 return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t); } } private T injectExtension(T instance) { try { if (objectFactory != null) { // 遍历instance所有setxxx方法 for (Method method : instance.getClass().getMethods()) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) { if (method.getAnnotation(DisableInject.class) != null) { continue; } // method = public void com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl.setStockSpiService(com.java.front.dubbo.spi.api.stock.StockSpiService) // pt = com.java.front.dubbo.spi.api.stock.StockSpiService Class> pt = method.getParameterTypes()[0]; if (ReflectUtils.isPrimitives(pt)) { continue; } try { // property = stockSpiService String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : StringUtils.EMPTY; // objectFactory = AdaptiveExtensionFactory // AdaptiveExtensionFactory.getExtension依次执行SpiExtensionFactory、SpringExtensionFactory直到获取到StockSpiService对象赋值给object Object object = objectFactory.getExtension(pt, property); if (object != null) { // instance = com.java.front.dubbo.spi.impl.goods.GoodsSpiAServiceImpl // method = GoodsSpiAServiceImpl.setStockSpiService // object = StockSpiService$Adaptive method.invoke(instance, object); } } catch (Exception e) { logger.error("fail to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; } } public class SpiExtensionFactory implements ExtensionFactory { @Override public T getExtension(Class type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { ExtensionLoader loader = ExtensionLoader.getExtensionLoader(type); if (!loader.getSupportedExtensions().isEmpty()) { return loader.getAdaptiveExtension(); } } return null; } }
7 自实现AOP 7.1 装饰器模式
装饰器模式可以动态将责任附加到对象上,在不改变原始类接口情况下对原始类功能进行增强,并且支持多个装饰器的嵌套使用,实现装饰器模式需要以下组件:
(1) Component
抽象构件:核心业务抽象,可以使用接口或者抽象类
public abstract class Component {
public abstract void playFootBall();
}
(2) ConcreteComponent
具体构件:核心业务代码
public class ConcreteComponent extends Component {
@Override
public void playFootBall() {
System.out.println("踢足球");
}
}
(3) Decorator
抽象装饰器:实现Component并通过构造函数组合Component
public abstract class Decorator extends Component {
private Component component = null;
public Decorator(Component component) {
this.component = component;
}
@Override
public void playFootBall() {
this.component.playFootBall();
}
}
(4) ConcreteDecorator
具体装饰器:具体装饰代码
// 球袜装饰器
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
private void decorateMethod() {
System.out.println("换球袜");
}
@Override
public void playFootBall() {
this.decorateMethod();
super.playFootBall();
}
}
// 球鞋装饰器
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
private void decorateMethod() {
System.out.println("换球鞋");
}
@Override
public void playFootBall() {
this.decorateMethod();
super.playFootBall();
}
}
(5) 测试代码
public class TestDecoratorDemo {
public static void main(String[] args) {
Component component = new ConcreteComponent();
component = new ConcreteDecoratorA(component);
component = new ConcreteDecoratorB(component);
component.playFootBall();
}
}
(6) 输出结果
换球鞋 换球袜 踢足球
7.2 代码实例
本章节在第二章节订单服务基础上进行扩展,第一步新增两个切面,一个日志切面,一个事务切面。我们可以理解切面为装饰器:切面实现了订单服务,并通过构造函数组合了订单服务。
package com.java.front.dubbo.spi.impl.order;
// 日志切面
public class OrderSpiLogWrapper implements OrderSpiService {
private OrderSpiService orderSpiService;
public OrderSpiLogWrapper(OrderSpiService orderSpiService) {
this.orderSpiService = orderSpiService;
}
@Override
public boolean createOrder(OrderModel order) {
System.out.println("OrderSpiLogWrapper log start");
boolean result = orderSpiService.createOrder(order);
System.out.println("OrderSpiLogWrapper log end");
return result;
}
}
// 事务切面
public class OrderSpiTransactionWrapper implements OrderSpiService {
private OrderSpiService orderSpiService;
public OrderSpiTransactionWrapper(OrderSpiService orderSpiService) {
this.orderSpiService = orderSpiService;
}
@Override
public boolean createOrder(OrderModel order) {
System.out.println("OrderSpiTransactionWrapper begin");
boolean result = orderSpiService.createOrder(order);
System.out.println("OrderSpiTransactionWrapper commit");
return result;
}
}
第二步新增配置文件内容
orderSpiLogWrapper=com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper orderSpiTransactionWrapper=com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper
第三步运行测试代码
public class OrderSpiServiceTest {
public static void main(String[] args) {
test1();
}
public static void test1() {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(OrderSpiService.class);
OrderSpiService orderSpiService = extensionLoader.getExtension("orderSpiAService");
orderSpiService.createOrder(new OrderModel());
}
}
第四步输出测试结果
OrderSpiLogWrapper log start OrderSpiTransactionWrapper begin OrderSpiAService createOrder OrderSpiTransactionWrapper commit OrderSpiLogWrapper log end
7.3 源码分析
createExtension方法可以看到切面通过构造函数装饰订单服务
public class ExtensionLoader{ private T createExtension(String name) { Class> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { // instance = com.java.front.dubbo.spi.impl.order.OrderSpiAServiceImpl T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // 自实现IOC injectExtension(instance); // 所有切面 // wrapperClasses = [com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper,com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper] Set > wrapperClasses = cachedWrapperClasses; // 自实现AOP // instance=(OrderSpiLogWrapper(OrderSpiTransactionWrapper(orderSpiAService))) if (CollectionUtils.isNotEmpty(wrapperClasses)) { for (Class> wrapperClass : wrapperClasses) { // 1.通过切面构造函数进行装饰 // 2.切面可能需要通过set注入属性所以执行injectExtension instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t); } } }
loadClass方法设置cachedWrapperClasses
public class ExtensionLoader{ private void loadClass(Map > extensionClasses, java.net.URL resourceURL, Class> clazz, String name) throws NoSuchMethodException { // clazz是否实现type // type = com.java.front.dubbo.spi.api.order.OrderSpiService // clazz = com.java.front.dubbo.spi.impl.order.OrderSpiLogWrapper // clazz = com.java.front.dubbo.spi.impl.order.OrderSpiTransactionWrapper if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error when load extension class(interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface."); } // 省略代码 // clazz是不是wrapper else if (isWrapperClass(clazz)) { Set > wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet >(); wrappers = cachedWrapperClasses; } // 新增至集合cachedWrapperClasses wrappers.add(clazz); } // 省略代码 } // clazz是否通过构造函数组合type private boolean isWrapperClass(Class> clazz) { try { clazz.getConstructor(type); return true; } catch (NoSuchMethodException e) { return false; } } }
8 文章总结
本文通过代码实例与源码分析两种方式,详细分析了DUBBO SPI六个特性:指定具体扩展点、指定默认扩展点、类级别自适应扩展点、方法级别自适应扩展点、自实现IOC、自实现AOP,希望本文对大家有所帮助。
欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我个人微信「java_front」一起交流学习



