文章目录
一、基本注解
1. @SPI
标注在接口层面上,在使用ExtensionLoader.getExtensionLoader(Class type)方法时,会在withExtensionAnnotation进行判断,如果type上没有标记@SPI注解,则会报错。
@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
// 在这里进行的SPI注解校验!!!!!
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
1.在loadExtensionClasses时会把SPI的value值放入缓存cachedDefaultName中
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
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];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
// 这里会拿到所有在dubbo classpath下配置的数据,与任何注解都无关
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
2.生成适配类代码的时候把默认值写入到代码里
private String createAdaptiveExtensionClassCode() {
......
// 赋值给默认的ExtName
String defaultExtName = cachedDefaultName;
String getNameCode = null;
for (int i = value.length - 1; i >= 0; --i) {
if (i == value.length - 1) {
if (null != defaultExtName) {
if (!"protocol".equals(value[i]))
// 生成扩展方法时,默认值为defaultExtName
// e.g. URL uRL = invoker.getUrl();
// String string = uRL.getParameter("proxy", "jdk");
String string = uRL.getParameter("proxy", "jdk");
if (hasInvocation)
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
else
getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
else
getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
} else {
if (!"protocol".equals(value[i]))
if (hasInvocation)
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
else
getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
else
getNameCode = "url.getProtocol()";
}
......
}
3.这就是生成好的适配类,这也是dubbo官网上说的,它先实例化适配类,只有调用它的相关方法后,才会最终调用getExtension(String name)实例化真正的Ext bean
public class ProxyFactory$Adaptive
implements ProxyFactory {
public Object getProxy(Invoker invoker) throws RpcException {
if (invoker == null) {
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
}
if (invoker.getUrl() == null) {
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
}
URL uRL = invoker.getUrl();
// 这里的“jdk”就是从spi的value()中拿到的缓存
String string = uRL.getParameter("proxy", "jdk");
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(").append(uRL.toString()).append(") use keys([proxy])").toString());
}
// very important!!!!
// 最终获取到的Ext Bean其实调用的是getExtension(String name)这个接口,目前来看它并不是一个代理类
ProxyFactory proxyFactory = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(string);
return proxyFactory.getProxy(invoker);
}
}
SPI注解作用总结
- getExtensionLoader()方法对接口的校验
- 取得被标注的接口的value值作为默认值,在getExtension()时获取对应的Extension
2. @Activate
2.1 作用:在调用getExtension时会缓存下来,用于后面寻找生效的实现类,并完成包装
标注在实现类上,当使用extensionLoader.getExtension(String name)方法时,调用链路为
getExtension->createExtension->getExtentionClasses->loadExtensionClasses->loadDirectory->loadResource->loadClass,最终loadClass会在this层面缓存cachedActivates(并不是缓存对应的实例)
// 缓存Activate的name与Activate注解
private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
.....
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
// clazz是从配置文件中查找到的,e.g. exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
// 从这里可以看出,如果对应的class中没有Activate注解,也是不会放入Cache中的,后续getActivateExtension也不会被get出来
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) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
ServiceConfig.doExportUrlsFor1Protocol()方法用到了getExtension
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.hasExtension(url.getProtocol())) {
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
2.2 项目中用到getExtension的地方
项目中处处用到
我理解主要是作为一个获取单例的方法,这也是Dubbo没用Spring也能获取单例的原因
其次还有配合2.3 getActivateExtension中提到的,当找到合适匹配的group的时候,从一堆可用的Activate类中生成对应的实例
2.3 getActivateExtension也会触发缓存
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<T>();
List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
// 在这里获取缓存的Class 如果之前用过2.1中的getExtension,cachedActivates会在之前就赋予初始值
// 如果没有用过2.1则会同2.1步骤一样,去为cachedActivates赋予初始值
getExtensionClasses();
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Activate activate = entry.getValue();
if (isMatchGroup(group, activate.group())) {
// 重要!!! 这里也调用了getExtension方法
T ext = getExtension(name);
if (!names.contains(name)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
&& isActive(activate, url)) {
exts.add(ext);
}
}
}
Collections.sort(exts, ActivateComparator.COMPARATOR);
...
}
2.4 getActivateExtension的作用
- Provider ServiceConfig.doExport() 之 buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER);
- Consumer MulticastRegistry.receive() 之 buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
// 这里 通过配置(可插拔的方式去包装filters),todo 确认一下 这里可能去逐级调用Filter的invoke方法
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (!filters.isEmpty()) {
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
@Override
public Class<T> getInterface() {
return invoker.getInterface();
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return invoker.isAvailable();
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return last;
}
3. @Adaptive
3.1 相关调用链路
1. 在实现类上处标注,则用这个类去生成代理
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 when load extension class(interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + "is not subtype of interface.");
}
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
// import!! 如果该实现类标注了Adaptive,则把改class赋值给cachedAdaptiveClass
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else if (isWrapperClass(clazz)) {
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
}
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
// 如果在上一步缓存过被@Adaptive注解的类,则不会再去动态生成类(为什么呢?)
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
2. 在接口类的方法上处标注,则该方法的内容和没有标记的内容不一样
private String createAdaptiveExtensionClassCode() {
StringBuilder codeBuilder = new StringBuilder();
Method[] methods = type.getMethods();
boolean hasAdaptiveAnnotation = false;
for (Method m : methods) {
if (m.isAnnotationPresent(Adaptive.class)) {
hasAdaptiveAnnotation = true;
break;
}
}
// no need to generate adaptive class since there's no adaptive method found.
if (!hasAdaptiveAnnotation)
throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
codeBuilder.append("package ").append(type.getPackage().getName()).append(";");
codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";");
codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {");
for (Method method : methods) {
Class<?> rt = method.getReturnType();
Class<?>[] pts = method.getParameterTypes();
Class<?>[] ets = method.getExceptionTypes();
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
// 标注了Adaptive注解的方法生成的代码是不同的
if (adaptiveAnnotation == null) {
code.append("throw new UnsupportedOperationException(\"method ")
.append(method.toString()).append(" of interface ")
.append(type.getName()).append(" is not adaptive method!\");");
} else {
int urlTypeIndex = -1;
for (int i = 0; i < pts.length; ++i) {
if (pts[i].equals(URL.class)) {
urlTypeIndex = i;
break;
}
}
// found parameter in URL type
if (urlTypeIndex != -1) {
// Null Point check
String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");",
urlTypeIndex);
code.append(s);
s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex);
code.append(s);
}
总结
- 大量运用Cache技术,只实例化用到的实现类
- 可插拔,易维护