Java基础之《dubbo(3)—dubbo内核》

SPI、IOC、AOP、动态编译

一、dubbo自己的SPI实现

1、dubbo的SPI位置
举例:dubbo-3.x.x\META-INF\dubbo\internal

2、JDK的SPI位置
举例:spring-core-5.2.20\META-INF\services\

3、spi的设计目标
面向对象的设计里,模块之间是基于接口编程,模块之间不对实现类进行硬编码。
一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。
为了实现在模块装配的时候,不在模块里面写死代码,这就需要一种服务发现机制。
JDK SPI就是提供这样的一个机制。
为某个接口寻找服务实现类的机制。有点类似IOC的思想,就是将装配的控制权移到代码之外。(转移到了代码的配置文件里面)

4、SPI的具体约定
当服务的提供者(provider),提供了一个接口多种实现时,一般会在jar包的META-INF/services/目录下,创建该接口的同名文件。
该文件里面的内容就是该服务接口的具体实现类的名称。
而当外部加载这个模块的时候,就能通过jar包META-INF/services/里的配置文件得到具体的实现类名,并加载实例化,完成模块的装配。

5、JDK SPI例子
(1)接口Command.java

package jdkspi.api;

public interface Command {

	public void execute();
}

(2)接口实现类1,StartCommand.java

package jdkspi.api;

public class StartCommand implements Command {

	@Override
	public void execute() {
		System.out.println("start...");
	}

}

(3)接口实现类2,ShutdownCommand.java

package jdkspi.api;

public class ShutdownCommand implements Command {

	@Override
	public void execute() {
		System.out.println("shutdown...");
	}

}

(4)Main.java

package jdkspi.api;

import java.util.ServiceLoader;

public class Main {
	public static void main(String[] args) {
		ServiceLoader<Command> serviceLoader = ServiceLoader.load(Command.class);
		
		for (Command command : serviceLoader) {
			command.execute();
		}
	}
}

(5)建立META-INF\services\jdkspi.api.Command

jdkspi.api.StartCommand
jdkspi.api.ShutdownCommand

(6)执行结果

start...
shutdown...

二、为什么dubbo不采用JDK的SPI

1、JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源

2、增加了对扩展点IOC和AOP的支持,一个扩展点可以直接setter注入其他扩展点

三、dubbo SPI有哪些约定

1、SPI文件存储路径在,META-INF\dubbo\internal目录下,并且文件名为接口的全路径名,就是=接口的包名+接口名

2、每个SPI文件里面的格式定义为:扩展名=具体的类名,例如
org.apache.dubbo.rpc.Protocol文件内容

filter=org.apache.dubbo.rpc.cluster.filter.ProtocolFilterWrapper
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper
registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol
service-discovery-registry=org.apache.dubbo.registry.integration.RegistryProtocol
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
serializationwrapper=org.apache.dubbo.rpc.protocol.ProtocolSerializationWrapper

dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol
grpc=org.apache.dubbo.rpc.protocol.grpc.GrpcProtocol
tri=org.apache.dubbo.rpc.protocol.tri.TripleProtocol

它是一个key=value的格式,那就可以不用实例化所有实现类,通过key来加载就ok了

四、dubbo SPI的目的和途径

1、目的:获取一个实现类的对象

2、途径:ExtensionLoader.java,在org.apache.dubbo.common.extension包下
ExtensionLoader.getExtension(String name)

3、实现路径
getExtensionLoader(Class<T> type),为该接口new一个ExtensionLoader,然后缓冲起来
getAdaptiveExtension(),获取一个扩展装饰类的对象,这个类有一个规则,如果它没有一个@Adaptive注解,就动态创建一个装饰类,例如Protocol$Adaptive对象。PS:如果注解在类上就是一个装饰类;如果注解在方法上就是一个动态代理类
getExtension(String name),获取一个实现类的对象

五、SPI机制的adpative原理

1、哪些类被@Adaptive注解了

注解在类上面,比如:AdaptiveExtensionInjector.java

@Adaptive
public class AdaptiveExtensionInjector implements ExtensionInjector, Lifecycle {...}

注解在方法上面,比如:Protocol.java

@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

2、adaptive注解在类和方法上的区别

(1)注解在类上,代表人工实现编码,即实现了一个装饰类(设计模式中的装饰模式),例如:AdaptiveExtensionInjector
(2)注解在方法上,代表自动生成和编译一个动态的adaptive类,例如:Protocol

3、getAdaptiveExtension()分析
getAdaptiveExtension()//方法的目的是为cachedAdaptiveInstance赋值
  -->createAdaptiveExtension()
    -->getAdaptiveExtensionClass()
      -->getExtensionClasses()//为cachedClasses赋值
        -->loadExtensionClasses()
          -->loadDirectory(extensionClasses, strategy, type.getName())
            -->loadDirectoryInternal(extensionClasses, strategy, type)
      -->createAdaptiveExtensionClass()//自动生成和编译一个动态的adaptive类,这个类是一个代理类
        -->String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate()//动态生成adaptive类的代码
        -->extensionDirector.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()
        -->compiler.compile(type, code, classLoader)
    -->postProcessBeforeInitialization(instance, null)
    -->injectExtension(instance)//作用:进入IOC的反转控制模式,实现了动态注入
    -->postProcessAfterInitialization(instance, null)
    -->initExtension(instance)

dubbo的所有对象都是通过ExtensionLoader来获取的

六、dubbo自己的IOC和AOP原理

1、getExtension(String name)分析
getExtension(String name)//get出来的对象是wrapper包装对象
  -->getExtension(String name, boolean wrap)
    -->getOrCreateHolder(cacheKey)//从缓存cachedInstances里提取对象,没有则创建
    -->createExtension(name, wrap)
      -->getExtensionClasses().get(name)//从SPI的配置文件里面,把所有的文件读出来,然后缓存起来
      -->postProcessBeforeInitialization(instance, name)
      -->injectExtension(instance)//dubbo的IOC反转控制,就是从spi和spring里面提取对象赋值
        -->把这个instance对象里的所有方法提取出来
        -->injector.getInstance(pt, property)
          -->extensionAccessor.getExtensionLoader(type)
            -->loader.getAdaptiveExtension()
      -->postProcessAfterInitialization(instance, name)
      -->injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))//获取wrapperClass类的构造函数,把instance赋值进去。先AOP在IOC
      -->postProcessAfterInitialization(instance, name)

七、dubbo的动态编译

1、代码
org.apache.dubbo.common.compiler.Compiler compiler = extensionDirector.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
compiler.compile(type, code, classLoader)
获取一个ExtensionLoader,然后获取一个装饰类

2、什么是Javassist
Javassist是一款字节码编辑工具,同时也是一个动态类库,它可以直接检查、修改以及创建Java类

八、dubbo如何和spring融合

1、以provider为例
ProviderApplication.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.dubbo.springboot.demo.provider;


import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubbo
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}

DemoServiceImpl.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.dubbo.springboot.demo.provider;


import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.springboot.demo.DemoService;

@DubboService
public class DemoServiceImpl implements DemoService {

    @Override
    public String sayHello(String name) {
        return "Hello " + name;
    }
}

application.yml

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

dubbo:
  application:
    name: dubbo-springboot-demo-provider
  protocol:
    name: dubbo
    port: -1
  registry:
    address: zookeeper://${zookeeper.address:127.0.0.1}:2181

2、dubbo如何采用spring的schema
完成一个spring的自定义配置一般需要以下5个步骤:
(1)设计配置属性和JavaBean
(2)编写XSD文件,全称就是XML Schema,它就是校验XML,定义了一系列的语法来规范XML
(3)编写NamespaceHandler和BeanDefinitionParser完成解析工作
(4)编写spring.handlers和spring.schemas串联起所有部件
(5)在Bean文件中应用

参考资料:
Dubbo的spi机制分析和实战案例_田哥coder的博客-CSDN博客
06-Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析 | Apache Dubbo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值