Dubbo过滤器 - ClassLoaderFilter

概述

ClassLoaderFilter 是服务提供端的一个 Filter 实现,用于切换当前工作线程的类加载器到接口的类加载器,以便和接口的类加载器的上下文一起工作。

ClassLoaderFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Activate(group = Constants.PROVIDER, order = -30000)
public class ClassLoaderFilter implements Filter {

@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// 获得当前线程的类加载器
ClassLoader ocl = Thread.currentThread().getContextClassLoader();
// 切换当前线程的类加载器为服务接口的类加载器
Thread.currentThread().setContextClassLoader(invoker.getInterface().getClassLoader());
try {
// 继续过滤器链的下一个节点
return invoker.invoke(invocation);
} finally {
// 切换当前线程的类加载器为原来的类加载器
Thread.currentThread().setContextClassLoader(ocl);
}
}
}

ClassLoaderFilter 的逻辑:首先获取当前线程关联的 ClassLoader,然后将其 ClassLoader 设置为 invoker.getInterface().getClassLoader(),也就是加载服务接口类的类加载器;之后执行 invoker.invoke() 方法,执行后续的 Filter 逻辑以及业务逻辑;最后,将当前线程关联的 ClassLoader 重置为原来的 ClassLoader ocl 。

双亲委派模型

如果 ClassA 和 ClassB 都是同一个类加载器加载的,则它们之间是可以相互访问的。如果 ClassA 和 ClassB 是不同的类加载器加载的,示例图如下:

根据双亲委派模型,如果 ClassA 要访问 ClassB,大致流程如下:

  1. ClassA 会从 ClassLoaderA 中查找 ClassB,看是否已经加载。
  2. 没有找到 ClassB 则会继续往上层查找,看父类加载器 ParentClassLoader 是否可以查找到 ClassB,如果找不到会继续往上层父类加载器查找。
  3. 最终没有找到,会抛出 ClassNotFoundException 异常。

如果要实现违反双亲委派模型来查找 Class,通常会使用上下文类加载器 ContextClassFilter

QA

Q: ClassLoaderFilter 的具体作用?

A: Dubbo 框架线程的类加载器可能和服务接口的类加载器不是同一个,而当前框架线程中又需要获取服务接口的类加载中的一些 Class,为了避免出现 ClassNotFoundException,此时只需要将框架线程的类加载器切换到加载了接口定义的类加载器,进而就能获得这个类加载中的 Class 。

Extra: Dubbo 中进行序列化优化时,会根据 Invoker 中配置的 optimizer 参数获取扩展的自定义序列化处理类,这些外部引入的序列化类在框架的类加载器中肯定没有,因此需要使用 Invoker 的类加载器获取对应的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
+--- DubboProtocol
/**
* 进行序列化优化,注册需要优化的类
*
* @param url
* @throws RpcException
*/
private void optimizeSerialization(URL url) throws RpcException {
// 获得 optimizer 序列化优化器 配置项
String className = url.getParameter(Constants.OPTIMIZER_KEY, "");

// 如果系统中没有序列化优化器就直接返回
if (StringUtils.isEmpty(className) || optimizers.contains(className)) {
return;
}

logger.info("Optimizing the serialization process for Kryo, FST, etc...");

try {

// 根据 序列化优化器名 加载 SerializationOptimizer 实现类。
// 这里的当前线程的 ClassLoader 是处理过的,使用的是 Invoker 的类加载器
Class clazz = Thread.currentThread().getContextClassLoader().loadClass(className);

// 是否是 SerializationOptimizer.class,或者 是SerializationOptimizer的子
if (!SerializationOptimizer.class.isAssignableFrom(clazz)) {
throw new RpcException("The serialization optimizer " + className + " isn't an instance of " + SerializationOptimizer.class.getName());
}

// 创建 SerializationOptimizer 对象
SerializationOptimizer optimizer = (SerializationOptimizer) clazz.newInstance();

// 没有要优化的类直接返回
if (optimizer.getSerializableClasses() == null) {
return;
}

// 将要优化的类注册到 SerializableClassRegistry 中 (todo 在使用 Kryo,FST 等序列化算法时,会读取该集合中的类,完成注册)
for (Class c : optimizer.getSerializableClasses()) {
SerializableClassRegistry.registerClass(c);
}

// 将 序列化优化器实现类名 加入到缓存中
optimizers.add(className);
} catch (ClassNotFoundException e) {
throw new RpcException("Cannot find the serialization optimizer class: " + className, e);
} catch (InstantiationException e) {
throw new RpcException("Cannot instantiate the serialization optimizer class: " + className, e);
} catch (IllegalAccessException e) {
throw new RpcException("Cannot instantiate the serialization optimizer class: " + className, e);
}
}

小结

本篇文件简单介绍了 Dubbo Filter 之 服务端的 ClassLoader 过滤器,它的作用就是用于切换当前工作线程的类加载器到接口的类加载器,以便和接口的类加载器的上下文一起工作。