加載META-INF/services/,META-INF/dubbo/和META-INF/dubbo/internal/ 下與當前類型全類名相同的文件app
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) { String fileName = dir + type.getName(); try { Enumeration<java.net.URL> 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) { ... } }
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { if (!type.isAssignableFrom(clazz)) { throw ... } //當前接口對應的文件下的被@Adaptive標識的類 if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { throw ... } } else if (isWrapperClass(clazz)) { //若是當前掃描的類有個構造方法,而且該構造方法的參數與當前類型相同(type),保存到cachedWrapperClasses,這裏是實現AOP的一個關鍵點 Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } else { clazz.getConstructor(); //校驗擴展名, 這裏能夠發現擴展名能夠爲空 即配置文件不使用key=value,學JDK直接使用value // 那麼1.該擴展類上有@Extension註解 或者2.該類的名字的末尾包含當前類的名字 // 即: 若是當前類叫 Qiao 那麼該類的名字爲 AbcQiao 那麼這個類的擴展名就是abc 詳見:注1 if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name.length() == 0) { throw ... } } 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) { //加載不被@Adaptive標識、不是aop的其餘擴展類 extensionClasses.put(n, clazz); } else if (c != clazz) { throw ... } } } } }
注1: 配置文件中不使用key=value的形式,使用JDK的 value形式ide
ExtensionLoader.getExtensionLoader(Qiao.class).getExtension("abc")
配置文件 META-INF/services/per.qiao.AbcQiao 以下this
per.qiao.AbcQiao
注意,這個類的simpleName以Qiao結尾.url
注意到上面,已經將當前類型(@SPI標識)的aop類型放到了cachedWrapperClasses、wrappers中..net
在當咱們調用getExtension方法時會調用createExtension方法,code
createExtension方法以下對象
private T createExtension(String 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); } // 對獲取的擴展類進行IOC處理 injectExtension(instance); Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && !wrapperClasses.isEmpty()) { //這裏將aop擴展類進行鏈式處理,實例化並進行IOC處理 for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { ... } }
上面方法會返回一個鏈式條用的第一個入口對象, 好比有兩個AOP擴展類, A和B,目標擴展類是Target,接口
那麼調用方法就是 A.method->B.method->Target.method (A,和B的執行順序取決於你的文件中的配置順序)。get
還有要注意的是AOP類型要和當前類型,放在同一個文件裏面(也能夠說是目標Target)
瞭解到那麼多以後,咱們來自定義一個AOP實現。
// 設置默認擴展類型 @SPI(value = MyExtProtocol.NAME) public interface MyProtocol { String getName(); Integer getSize(String name); }
public class MyExtProtocol implements MyProtocol { public static final String NAME = "myExt"; @Override public String getName() { System.out.println("MyExtProtocol.getName" + "目標方法被調用"); return "MyExtProtocol.getName"; } @Override public Integer getSize(String name) { return null; } }
/** * MyProtocol類型的AOP擴展類 * 與目標擴展類實現同一個接口 */ public class MyProtocolWrapper implements MyProtocol { /** * AOP鏈式調用中的一節,這裏咱們只有一個AOP擴展類,故這個就是目標擴展類 */ private MyProtocol myProtocol; public MyProtocolWrapper(MyProtocol myProtocol) { this.myProtocol = myProtocol; } @Override public String getName() { System.out.println("MyProtocolWrapper.getName === " + "前置方法被調用"); String name = myProtocol.getName(); System.out.println("MyProtocolWrapper.getName === " + "後置方法被調用"); return name; } @Override public Integer getSize(String name) { System.out.println("MyProtocolWrapper.getSize === " + "前置方法被調用"); Integer size = myProtocol.getSize(name); System.out.println("MyProtocolWrapper.getSize === " + "後置方法被調用"); return size; } }
# 目標擴展類 myExt=per.qiao.myprotocol.impl.MyExtProtocol # AOP類型 myWrapper=per.qiao.myprotocol.wrapper.MyProtocolWrapper
MyProtocol myExt = ExtensionLoader.getExtensionLoader(MyProtocol.class).getExtension("myExt"); myExt.getName();
一個簡單的AOP就完成了.