序
本文主要研究一下dubbo的ExtensionLoader.getActivateExtension
ExtensionLoader.getActivateExtension
dubbo-2.7.3/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
public class ExtensionLoader<T> {
private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
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/";
//......
/**
* This is equivalent to {@code getActivateExtension(url, url.getParameter(key).split(","), null)}
*
* @param url url
* @param key url parameter key which used to get extension point names
* @param group group
* @return extension list which are activated.
* @see #getActivateExtension(org.apache.dubbo.common.URL, String[], String)
*/
public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}
/**
* Get activate extensions.
*
* @param url url
* @param values extension point names
* @param group group
* @return extension list which are activated
* @see org.apache.dubbo.common.extension.Activate
*/
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<>();
List<String> names = values == null ? new ArrayList<>(0) : Arrays.asList(values);
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
getExtensionClasses();
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Object activate = entry.getValue();
String[] activateGroup, activateValue;
if (activate instanceof Activate) {
activateGroup = ((Activate) activate).group();
activateValue = ((Activate) activate).value();
} else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
} else {
continue;
}
if (isMatchGroup(group, activateGroup)) {
T ext = getExtension(name);
if (!names.contains(name)
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(activateValue, url)) {
exts.add(ext);
}
}
}
exts.sort(ActivateComparator.COMPARATOR);
}
List<T> usrs = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
if (DEFAULT_KEY.equals(name)) {
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
// synchronized in getExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName();
Map<String, Class<?>> 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<String, Class<?>> extensionClasses, String dir, String type) {
String fileName = dir + type;
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) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
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);
}
}
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
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);
}
}
}
}
/**
* cache Activate class which is annotated with <code>Activate</code>
* <p>
* for compatibility, also cache class with old alibaba Activate annotation
*/
private void cacheActivateClass(Class<?> clazz, String name) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(name, activate);
} else {
// support com.alibaba.dubbo.common.extension.Activate
com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
if (oldActivate != null) {
cachedActivates.put(name, oldActivate);
}
}
}
/**
* Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
* will be thrown.
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
if ("true".equals(name)) {
return getDefaultExtension();
}
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;
}
//......
}
- getActivateExtension方法先执行getExtensionClasses加载extension类,这里主要通过loadDirectory方法加载META-INF/dubbo/、META-INF/dubbo/internal/、META-INF/services/三个目录下的extension
- loadDirectory方法会调用loadResource方法,loadResource方法会调用loadClass方法,loadClass方法会通过cacheActivateClass把Activate注解信息放入到cachedActivates中
- 之后遍历cachedActivates通过getExtension方法把extension实例放入exts,然后再根据ActivateComparator.COMPARATOR进行排序,最后加载usr的extension(
目前看代码names貌似一直为空
),然后合并返回
ActivateComparator
dubbo-common-2.7.3-sources.jar!/org/apache/dubbo/common/extension/support/ActivateComparator.java
public class ActivateComparator implements Comparator<Object> {
public static final Comparator<Object> COMPARATOR = new ActivateComparator();
@Override
public int compare(Object o1, Object o2) {
if (o1 == null && o2 == null) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
if (o1.equals(o2)) {
return 0;
}
Class<?> inf = findSpi(o1.getClass());
ActivateInfo a1 = parseActivate(o1.getClass());
ActivateInfo a2 = parseActivate(o2.getClass());
if ((a1.applicableToCompare() || a2.applicableToCompare()) && inf != null) {
ExtensionLoader<?> extensionLoader = ExtensionLoader.getExtensionLoader(inf);
if (a1.applicableToCompare()) {
String n2 = extensionLoader.getExtensionName(o2.getClass());
if (a1.isLess(n2)) {
return -1;
}
if (a1.isMore(n2)) {
return 1;
}
}
if (a2.applicableToCompare()) {
String n1 = extensionLoader.getExtensionName(o1.getClass());
if (a2.isLess(n1)) {
return 1;
}
if (a2.isMore(n1)) {
return -1;
}
}
}
int n1 = a1 == null ? 0 : a1.order;
int n2 = a2 == null ? 0 : a2.order;
// never return 0 even if n1 equals n2, otherwise, o1 and o2 will override each other in collection like HashSet
return n1 > n2 ? 1 : -1;
}
private Class<?> findSpi(Class clazz) {
if (clazz.getInterfaces().length <= 0) {
return null;
}
for (Class<?> intf : clazz.getInterfaces()) {
if (intf.isAnnotationPresent(SPI.class)) {
return intf;
} else {
Class result = findSpi(intf);
if (result != null) {
return result;
}
}
}
return null;
}
private ActivateInfo parseActivate(Class<?> clazz) {
ActivateInfo info = new ActivateInfo();
if (clazz.isAnnotationPresent(Activate.class)) {
Activate activate = clazz.getAnnotation(Activate.class);
info.before = activate.before();
info.after = activate.after();
info.order = activate.order();
} else {
com.alibaba.dubbo.common.extension.Activate activate = clazz.getAnnotation(
com.alibaba.dubbo.common.extension.Activate.class);
info.before = activate.before();
info.after = activate.after();
info.order = activate.order();
}
return info;
}
private static class ActivateInfo {
private String[] before;
private String[] after;
private int order;
private boolean applicableToCompare() {
return ArrayUtils.isNotEmpty(before) || ArrayUtils.isNotEmpty(after);
}
private boolean isLess(String name) {
return Arrays.asList(before).contains(name);
}
private boolean isMore(String name) {
return Arrays.asList(after).contains(name);
}
}
}
- ActivateComparator首先通过parseActivate解析注解信息到ActivateInfo,然后对于有配置before或after的则根据其值进行升序排序,否则则通过order(
没有指定默认为0
)来排序,大于返回1,否则返回-1
小结
- getActivateExtension方法先执行getExtensionClasses加载extension类,这里主要通过loadDirectory方法加载META-INF/dubbo/、META-INF/dubbo/internal/、META-INF/services/三个目录下的extension
- loadDirectory方法会调用loadResource方法,loadResource方法会调用loadClass方法,loadClass方法会通过cacheActivateClass把Activate注解信息放入到cachedActivates中
- 之后遍历cachedActivates通过getExtension方法把extension实例放入exts,然后再根据ActivateComparator.COMPARATOR进行排序,最后加载usr的extension(
目前看代码names貌似一直为空
),然后合并返回