Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();的解析

本文深入解析了Dubbo中SPI(Service Provider Interface)机制的工作原理,包括如何通过SPI注解获取默认实现,以及ExtensionLoader如何生成代理对象。文章详细介绍了getExtensionLoader、getAdaptiveExtension等方法的实现过程,展示了Dubbo如何利用SPI技术实现接口的动态扩展。
部署运行你感兴趣的模型镜像

这种Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();生成代理对象的方法在dubbo中运用的非常多。他主要运用了SPI技术。简单可以概括为(以Protocol为例):

@SPI("dubbo")
public interface Protocol {}

Protocol接口的实现类有很多:在这里插入图片描述
使用@SPI注解来得到默认实现,如上图Protocol的默认实现是dubbo,那么dubbo是怎么通过这个注解的值得到相应的实现的呢?
在这里插入图片描述
答案在这里,dubbo通过解析这个配置文件来得到对应的实现类。
当然,ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()这段代码的实现很复杂。接下来慢慢解析一下(只粘出了部分源码):
1.首先是getExtensionLoader方法:

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);
        }

EXTENSION_LOADERS是一个缓存的map,key为class,value为ExtensionLoader,意思是先从缓存中取ExtensionLoader,没有就执行new ExtensionLoader(type)。
2.getAdaptiveExtension()方法:

public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                            instance = createAdaptiveExtension();
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }

        return (T) instance;
    }

此方法主要功能是获得代理对象,cachedAdaptiveInstance是一个对象容器,先从容器里面拿,如果没有说明是首次生成代理对象,然后通过同步锁进行创建,如果此时有多个线程同时进来,只能由一个线程进行创建,待其创建完通过instance = cachedAdaptiveInstance.get();来取。
3.接下来是createAdaptiveExtension方法,此方法简单就不说了。
4.getAdaptiveExtensionClass方法:此方法简单就不说了。
5.getExtensionClasses方法,实现并发的思想跟上面差不多,此方法简单就不说了。
6.loadExtensionClasses方法:

private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if (value != null && (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<?>>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

此方法主要实现如下功能;
1.获得Protocol上的spi注解并得到value赋值给cachedDefaultName变量。
2.执行loadFile方法
7.loadFile方法:代码太长分析一部分

ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }

通过文件名字读取配置文件。

  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) {
      Class<?> clazz = Class.forName(line, true, classLoader);

如dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol那么name=dubbo,line =com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol,然后通过类全名得到class

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());
    }
 }

cachedNames是一个map,key为上面生成的class,value为name,extensionClasses也是一个map,key为上面生成的name,value为class.
8.createAdaptiveExtensionClass方法:主要生成各种代理信息。
在spring中如果有一个接口有多个实现类时,可以这样做:
@Resource 默认是按照 byName 的方式注入的, 如果通过 byName 的方式匹配不到,再按 byType 的方式去匹配。所以上面的引用可以替换为:
@Qualifier 注解也是 byName的方式,但是与@Resource 有区别,@Qualifier 使用的是 类名。

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

<think> 我们需要通过编程方式(使用ExtensionLoader)来添加自定义的Codec扩展,而不是通过META-INF配置文件。 在Dubbo中,扩展点加载机制允许我们动态添加扩展实现。这里我们使用`ExtensionLoader.getExtensionLoader(Codec2.class).addExtension`方法来注册自定义的Codec。 步骤: 1. 创建自定义的Codec包装类(同上一个解决方案中的SafeKryoCodec)。 2. 在Dubbo启动之前,通过ExtensionLoader动态添加扩展,指定扩展名和实现类。 3. 在Dubbo协议配置中指定使用这个扩展名(例如codec="safeKryo")。 注意:这种方式需要在Dubbo启动之前执行,比如在Spring的初始化阶段。 下面给出示例代码: </think> ### 通过编程方式注入 Dubbo 自定义 Codec 在 Dubbo 中,可以使用 `ExtensionLoader` 的 `addExtension` 方法动态注册自定义 Codec,无需 META-INF 配置文件。以下是完整的实现方案: #### 动态注册代码: ```java import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.remoting.Codec2; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; public class CodecInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext context) { // 获取 Codec2 扩展加载器 ExtensionLoader<Codec2> loader = ExtensionLoader.getExtensionLoader(Codec2.class); // 动态注册自定义 Codec loader.addExtension("safeKryo", SafeKryoCodec.class); // 验证注册结果 if (loader.hasExtension("safeKryo")) { System.out.println("SafeKryoCodec registered successfully"); } } } ``` #### 自定义 Codec 实现 (SafeKryoCodec.java): ```java import org.apache.dubbo.remoting.Channel; import org.apache.dubbo.remoting.Codec2; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import org.apache.dubbo.remoting.exchange.Request; import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.exchange.support.DefaultFuture; import com.esotericsoftware.kryo.KryoException; import java.io.IOException; public class SafeKryoCodec implements Codec2 { private final Codec2 delegate = new DubboCodec(); @Override public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException { try { delegate.encode(channel, buffer, msg); } catch (KryoException e) { handleEncodingException(msg, channel, buffer, e); } } @Override public Object decode(Channel channel, ChannelBuffer buffer) throws IOException { try { return delegate.decode(channel, buffer); } catch (KryoException e) { return handleDecodingException(channel, buffer, e); } } private void handleEncodingException(Object msg, Channel channel, ChannelBuffer buffer, KryoException e) throws IOException { if (msg instanceof Response) { Response res = (Response) msg; Response errorResponse = createErrorResponse(res.getId(), "Encoding error: " + e.getMessage()); delegate.encode(channel, buffer, errorResponse); } else { throw e; } } private Response handleDecodingException(Channel channel, ChannelBuffer buffer, KryoException e) { long requestId = DefaultFuture.getRequestId(channel); return createErrorResponse(requestId, "Decoding error: " + e.getMessage()); } private Response createErrorResponse(long requestId, String message) { Response response = new Response(requestId); response.setStatus(Response.SERIALIZATION_EXCEPTION); response.setErrorMessage(message); return response; } } ``` #### Spring Boot 启动类集成: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DubboApp { public static void main(String[] args) { // 先初始化自定义Codec ExtensionLoader.getExtensionLoader(Codec2.class) .addExtension("safeKryo", SafeKryoCodec.class); // 启动Spring应用 SpringApplication.run(DubboApp.class, args); } } ``` #### Dubbo 协议配置 (application.yaml): ```yaml dubbo: protocol: name: dubbo codec: safeKryo # 使用编程注册的扩展名 ``` ### 关键技术解析: 1. **动态扩展注册**: - 使用 `ExtensionLoader.getExtensionLoader(Codec2.class).addExtension()` 方法 - 参数1:自定义扩展名称(如 "safeKryo") - 参数2:实现类(SafeKryoCodec.class) - **时机**:需在 Dubbo 初始化前完成注册(如 Spring 启动前) 2. **包装器实现要点**: - 直接实例化 `DubboCodec` 作为委托对象(无需构造函数注入) - 解码异常处理:通过 `DefaultFuture.getRequestId()` 获取请求ID - 错误响应状态:使用 `Response.SERIALIZATION_EXCEPTION` 明确异常类型 3. **集成方式**: - **Spring Boot**:在 `main()` 方法中优先注册扩展 - **传统 Spring**:使用 `ApplicationContextInitializer` 接口 - **非 Spring 项目**:在服务暴露前调用注册代码 4. **优势对比配置文件**: ```mermaid graph LR A[注册方式] --> B[配置文件] A --> C[编程注册] B --> D[需要资源文件] B --> E[需遵循SPI规范] C --> F[动态控制] C --> G[无文件依赖] C --> H[可运行时调整] ``` ### 注意事项: 1. **注册时机**: ```java // 错误示例:在Dubbo初始化后注册无效 @Bean public void registerCodec() { // 此时Dubbo已初始化,注册无效 loader.addExtension(...); } ``` 2. **线程安全**: - 扩展注册应在单线程初始化阶段完成 - 避免在运行时动态修改(Dubbo 扩展点非线程安全) 3. **委托对象选择**: - 示例中硬编码 `new DubboCodec()` 适用于 dubbo 协议 - 如需支持多协议,可通过 `ExtensionLoader.getExtension("dubbo")` 获取协议对应 Codec ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值