AOP

AOP

ExtensionLoader#loadDirectory

加載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) {
            ...
        }
    }

ExtensionLoader#loadClass

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實現。

AOP的Demo

  1. 自定義一個協議
// 設置默認擴展類型
@SPI(value = MyExtProtocol.NAME)
public interface MyProtocol {

    String getName();

    Integer getSize(String name);
}
  1. 默認擴展類(目標執行的擴展類)
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;
    }
}
  1. AOP類型
/**
 * 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;
    }
}
  1. 擴展類配置文件
#  目標擴展類
myExt=per.qiao.myprotocol.impl.MyExtProtocol
# AOP類型
myWrapper=per.qiao.myprotocol.wrapper.MyProtocolWrapper
  1. 調用
MyProtocol myExt = ExtensionLoader.getExtensionLoader(MyProtocol.class).getExtension("myExt");
myExt.getName();

一個簡單的AOP就完成了.

相關文章
相關標籤/搜索