Dubbo系列讲解之服务注册【3万字长文分享】,阿里面试100题

首先进入构造方法

public ServiceAnnotationBeanPostProcessor(Set packagesToScan) {

super(packagesToScan);

}

这里将传入的packagesToScan往父类进行传递,由于它继承了ServiceClassPostProcessor,现在进入ServiceClassPostProcessor类的构造方法:

public ServiceClassPostProcessor(Set packagesToScan) {

this.packagesToScan = packagesToScan;

}

ServiceClassPostProcessor只是将传入的扫包路径赋值给packagesToScan

根据BeanPostProcessor的特性,现在进入到postProcessBeanDefinitionRegistry方法

@Override

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

// @since 2.7.5

registerBeans(registry, DubboBootstrapApplicationListener.class);

Set resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {

registerServiceBeans(resolvedPackagesToScan, registry);

} else {

if (logger.isWarnEnabled()) {

logger.warn(“packagesToScan is empty , ServiceBean registry will be ignored!”);

}

}

}

该方法主要做了以下几件事:

  • 注册了一个DubboBootstrapApplicationListener监听,具体作用稍后再叙

  • 调用resolvePackagesToScan方法解析所有包名的路径。可能包名中存在一Placeholders的特殊定义

  • 调用registerServiceBeans方法进行注册

具体怎么解析包路径不在本次讨论范围,所有就先不深入了,现在直接进入到registerServiceBeans方法中

private void registerServiceBeans(Set packagesToScan, BeanDefinitionRegistry registry) {

DubboClassPathBeanDefinitionScanner scanner =

new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

scanner.setBeanNameGenerator(beanNameGenerator);

// refactor @since 2.7.7

serviceAnnotationTypes.forEach(annotationType -> {

scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));

});

for (String packageToScan : packagesToScan) {

// Registers @Service Bean first

scanner.scan(packageToScan);

// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.

Set beanDefinitionHolders =

findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {

registerServiceBean(beanDefinitionHolder, registry, scanner);

}

if (logger.isInfoEnabled()) {

logger.info(beanDefinitionHolders.size() + " annotated Dubbo’s @Service Components { " +

beanDefinitionHolders +

" } were scanned under package[" + packageToScan + “]”);

}

} else {

if (logger.isWarnEnabled()) {

logger.warn(“No Spring Bean annotating Dubbo’s @Service was found under package[”

  • packageToScan + “]”);

}

}

}

}

该方法有主要做了以下几件事

  • 构建了一个DubboClassPathBeanDefinitionScanner对象,该对象继承自Spring的ClassPathBeanDefinitionScanner。在Spring中,ClassPathBeanDefinitionScanner是一个扫描程序,主要用来扫描Classpath下符合条件的对象,然后将对象注入到给定的registry中

  • 定义一个为Bean生成名称的BeanNameGenerator,这里生成的是AnnotationBeanNameGenerator这个策略

  • 将bean名称策略set到scanner中

  • 添加过滤Filter,这里遍历serviceAnnotationTypes,获取到所有的过滤条件,这里是基于注解的拦截,到serviceAnnotationTypes赋值的地方,可以看到。初始化了以下三种注解作为拦截

private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(

// @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007

DubboService.class,

// @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service

Service.class,

// @since 2.7.3 Add the compatibility for legacy Dubbo’s @Service , the issue : https://github.com/apache/dubbo/issues/4330

com.alibaba.dubbo.config.annotation.Service.class

);

  • 遍历解析后的扫描的包,调用scanner.scan(packageToScan)注册所有标注了@Service的bean注入到ioc容器中

  • 调用findServiceBeanDefinitionHolders查找所有标注了@Service的Class封装成BeanDefinitionHolders,不管是否被@ComponentScan扫描

  • 如果beanDefinitionHolders存在元素,遍历beanDefinitionHolders,调用registerServiceBean注册

将标注了@Service注解的bean注入到ioc容器不属于本次讨论内容,这里也不做详细说明

下面进入到findServiceBeanDefinitionHolders方法,了解一下该方法都返回了那些类型的BeanDefinitionHolder

private Set findServiceBeanDefinitionHolders(

ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry,

BeanNameGenerator beanNameGenerator) {

Set beanDefinitions = scanner.findCandidateComponents(packageToScan);

Set beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());

for (BeanDefinition beanDefinition : beanDefinitions) {

String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);

BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);

beanDefinitionHolders.add(beanDefinitionHolder);

}

return beanDefinitionHolders;

}

  • 首先扫描传入的packageToScan包下的所有的符合在scanner中定义的过滤注解的.class文件,封装成BeanDefinition

  • 遍历扫描到的beanDefinitions,通过名称策略,为Bean生成名称,同时用Bean和名称构建成BeanDefinitionHolder,加入到beanDefinitionHolders中,返回该集合

beanDefinitionHolders的获取到这里就已经完成了,接下来进入到 registerServiceBean方法中,看看具体的注册流程

private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,

DubboClassPathBeanDefinitionScanner scanner) {

// 通过beanDefinitionHolder中的BeanDefinition中保存的全类名通过Class.forName加载成class对象

Class<?> beanClass = resolveClass(beanDefinitionHolder);

Annotation service = findServiceAnnotation(beanClass);

/**

  • The {@link AnnotationAttributes} of @Service annotation

*/

AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);

Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);

String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

AbstractBeanDefinition serviceBeanDefinition =

buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

// ServiceBean Bean name

String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);

if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean

registry.registerBeanDefinition(beanName, serviceBeanDefinition);

if (logger.isInfoEnabled()) {

logger.info(“The BeanDefinition[” + serviceBeanDefinition +

"] of ServiceBean has been registered with name : " + beanName);

}

} else {

if (logger.isWarnEnabled()) {

logger.warn(“The Duplicated BeanDefinition[” + serviceBeanDefinition +

"] of ServiceBean[ bean name : " + beanName +

“] was be found , Did @DubboComponentScan scan to same package in many times?”);

}

}

}

  • 获取扫描到的类的字节码的class对象

  • 获取 beanClass上的注解,会跟初始化时的serviceAnnotationTypes属性中的注解进行匹配,返回匹配到的注解

  • 获取匹配到的注解service上的所有属性及其属性值serviceAnnotationAttributes

  • 获取扫描到的.class对象实现的接口的class对象interfaceClass

  • 获取组装BeanDefinitionHolder是为Bean生成的名称

  • 将所有参数传入到buildServiceBeanDefinition方法中,构建一个AbstractBeanDefinition的对象,这里我们暂时是不知道AbstractBeanDefinition保存的是哪个Bean的定义

  • 通过generateServiceBeanName方法构建一个ServiceBean的名称。

  • 检查是否存在重复名称的bean,如果不存在,则直接注入AbstractBeanDefinition的定义到ioc容器中

现在我们先来探索一下在buildServiceBeanDefinition中构建的是一个什么Bean的定义,由于方法比较长,这里就不贴代码了,该方法的大概的流程就是创建了一个ServiceBean的BeanDefinition。然后组装前面解析到的注解的参数和获取到的实现类的接口等为ServiceBean的属性进行赋值,然后最后返回一个ServiceBean的BeanDefinition。

然后再来看看ServiceBean的命名规则是怎么样的

private String generateServiceBeanName(AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass) {

ServiceBeanNameBuilder builder = create(interfaceClass, environment)

.group(serviceAnnotationAttributes.getString(“group”))

.version(serviceAnnotationAttributes.getString(“version”));

return builder.build();

}

可以看到,ServiceBean的命名规则是通过接口的全类名以及group,version等一起来保证唯一名称的,或许是长这样的ServiceBean:com.bobo.dubbo.api.HelloService:2.0.1这一系列操作下来,就是为了构建一个ServiceBean。而我们在DubboNamespaceHandler的方式中,也可以看到,最终也注入了一个ServiceBean。那么ServiceBean到底有何神奇之处呢?马上揭晓!进入到ServiceBean,看到其继承了ServiceConfig,同时实现了InitializingBean,DisposableBean,ApplicationContextAware,BeanNameAware,ApplicationEventPublisherAware等接口。而ServiceConfig又继承了AbstractConfig类,它是,比如Service,Refrence,application,Monitor等配置类的父类,我们进入AbstractConfig类,发现它存在一个@PostConstruct注解标注的方法,它会在spring的bean初始化完成之后执行,我们进入该方法

@PostConstruct

public void addIntoConfigManager() {

ApplicationModel.getConfigManager().addConfig(this);

}

进入到ApplicationModel.getConfigManager()方法

public static ConfigManager getConfigManager() {

return (ConfigManager) LOADER.getExtension(ConfigManager.NAME);

}

看到这里,上一篇的SPI知识就排上了用场了,这是一个用来获取FrameworkExt接口的扩展实现的扩展点,同时ConfigManager.NAME指定了需要获取的扩展点的名称为config。所以就到FrameworkExt的实现类中查找一个名为config的扩展点实现即可得到。其实这里得到的就是ConfigManager本身。

所以在addIntoConfigManager方法中,实际上是将当前的bean保存到了ConfigManager的对象中,最终保存到了ConfigManager的configsCache中。ConfigManager主要是用来管理Dubbo中的所有继承了AbstractConfig的配置

而在ServiceBean中,我们并没有看到有任何任何有价值的东西,到这里看起来似乎前路已断,不知道怎么样入手了。

此时突然想起来我们在进行扫包的一系列操作之前,貌似注册了一个监听器,是不是可以从监听器入手呢?

进入到前面注册的DubboBootstrapApplicationListener监听器中

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener

implements Ordered {

/**

  • The bean name of {@link DubboBootstrapApplicationListener}

  • @since 2.7.6

*/

public static final String BEAN_NAME = “dubboBootstrapApplicationListener”;

private final DubboBootstrap dubboBootstrap;

public DubboBootstrapApplicationListener() {

this.dubboBootstrap = DubboBootstrap.getInstance();

}

@Override

public void onApplicationContextEvent(ApplicationContextEvent event) {

if (event instanceof ContextRefreshedEvent) {

onContextRefreshedEvent((ContextRefreshedEvent) event);

} else if (event instanceof ContextClosedEvent) {

onContextClosedEvent((ContextClosedEvent) event);

}

}

private void onContextRefreshedEvent(ContextRefreshedEvent event) {

dubboBootstrap.start();

}

private void onContextClosedEvent(ContextClosedEvent event) {

dubboBootstrap.stop();

}

@Override

public int getOrder() {

return LOWEST_PRECEDENCE;

}

}

可以看到,该监听器监听了容器的容器的刷新和关闭,我们前面的操作已经将ServiceBean注入到了ioc容器中,根据ioc的容器初始化的几个周期,可以知道在Refreshd容器时,我们所有的服务提供者对应的ServiceBean已经全部装载到了容器中。

继续往下,当产生ContextRefreshedEvent事件时,调用了onContextRefreshedEvent方法,该方法中调用dubboBootstrap.start();

到这里,跟Spring相关的东西已经走完了,下面做一个总结

  • 通过Spring Boot的自动装配或@EnableDubbo注解自动注入一个ServiceAnnotationBeanPostProcessor传入需要扫描的包的路径

  • 注册了一个``DubboBootstrapApplicationListener`监听

  • 根据BeanPostProcessor的特性,调用postProcessBeanDefinitionRegistry方法,根据传入的扫包路径进行扫描,然后将所有的标注了@Service注解的bean注入到ioc容器中

  • 继续扫描包,获得标注了Service/DubboService等注解的所有BeanDefinitionHolders

  • 遍历BeanDefinitionHolders,解析出每个BeanDefinition中的接口,标注的注解,及注解上定义的参数等。

  • 通过解析出来的一系列信息生成一个ServiceBean.然后将ServiceBean注入到ioc容器中。

  • 同时在ServiceBean的父类AbstractConfig中,会存在一个标注了@PostConstruct注解的方法,它会在bean初始化完成之后,将当前bean保存到一个ConfigManager对象中,它dubbo环境中是一个单例的存在。

  • 在Spring进行Refresh容器时,会触发一个事件,调用dubboBootstrap.start();方法,启动

接下来就真正的进入到Dubbo的服务发布,注册的世界,一探究竟吧

Dubbo的服务注册与发布


在进入start()方法之前,首先需要看看dubboBootstrap的初始化过程,它是一个单例的对象,直接进入DubboBootstrap的构造方法

private DubboBootstrap() {

configManager = ApplicationModel.getConfigManager();

environment = ApplicationModel.getEnvironment();

DubboShutdownHook.getDubboShutdownHook().register();

ShutdownHookCallbacks.INSTANCE.addCallback(new ShutdownHookCallback() {

@Override

public void callback() throws Throwable {

DubboBootstrap.this.destroy();

}

});

}

可以看到,初始化时构建了configManager和environment,其中configManager主要用于管理Dubbo中的所有配置。Environment展示先不关注

根据以上的分析,我们现在进入到start()方法

public DubboBootstrap start() {

// 已经启动过后,就不用再次启动了

if (started.compareAndSet(false, true)) {

ready.set(false);

// 初始化方法,就是检查一些配置,启动配置中心等等

initialize();

if (logger.isInfoEnabled()) {

logger.info(NAME + " is starting…");

}

// 1. export Dubbo Services

// 真正执行发布服务的方法

exportServices();

// Not only provider register

if (!isOnlyRegisterProvider() || hasExportedServices()) {

// 2. export MetadataService

exportMetadataService();

//3. Register the local ServiceInstance if required

registerServiceInstance();

}

referServices();

if (asyncExportingFutures.size() > 0) {

new Thread(() -> {

try {

this.awaitFinish();

} catch (Exception e) {

logger.warn(NAME + " exportAsync occurred an exception.");

}

ready.set(true);

if (logger.isInfoEnabled()) {

logger.info(NAME + " is ready.");

}

}).start();

} else {

ready.set(true);

if (logger.isInfoEnabled()) {

logger.info(NAME + " is ready.");

}

}

if (logger.isInfoEnabled()) {

logger.info(NAME + " has started.");

}

}

return this;

}

调用了exportServices方法进行了服务的发布和注册,调用referServices方法进行服务的发现,服务发现将留到下一篇去,今天只对服务的注册进行探索。

进入到exportServices方法

private void exportServices() {

configManager.getServices().forEach(sc -> {

// TODO, compatible with ServiceConfig.export()

ServiceConfig serviceConfig = (ServiceConfig) sc;

serviceConfig.setBootstrap(this);

if (exportAsync) {

ExecutorService executor = executorRepository.getServiceExporterExecutor();

Future<?> future = executor.submit(() -> {

sc.export();

exportedServices.add(sc);

});

asyncExportingFutures.add(future);

} else {

sc.export();

exportedServices.add(sc);

}

});

}

遍历我们在Spring Boot环节时添加到configManager的所有ServiceConfig,将当前的对象传入到ServiceConfig中,同步或异步调用ServiceConfig的export方法。

进入到export方法

public synchronized void export() {

if (!shouldExport()) {

return;

}

if (bootstrap == null) {

bootstrap = DubboBootstrap.getInstance();

bootstrap.init();

}

checkAndUpdateSubConfigs();

//init serviceMetadata

serviceMetadata.setVersion(version);

serviceMetadata.setGroup(group);

serviceMetadata.setDefaultGroup(group);

serviceMetadata.setServiceType(getInterfaceClass());

serviceMetadata.setServiceInterfaceName(getInterface());

serviceMetadata.setTarget(getRef());

if (shouldDelay()) {

DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);

} else {

doExport();

}

exported();

}

该方法算是Dubbo服务发布的入口流程方法了。

  • 判断是否应该发布本服务

  • 如果DubboBootstrap对象为null,初始化一个DubboBootstrap对象

  • 检查是否更新存根配置

  • 初始化ServiceMetadata,将注入Bean时初始化的一些参数保存到serviceMetadata中

  • 延时或同步调用doExport

进入doExport

protected synchronized void doExport() {

if (unexported) {

throw new IllegalStateException(“The service " + interfaceClass.getName() + " has already unexported!”);

}

if (exported) {

return;

}

exported = true;

if (StringUtils.isEmpty(path)) {

path = interfaceName;

}

doExportUrls();

}

做了一系列判断,标识等初始化之后,再调用doExportUrls方法

private void doExportUrls() {

ServiceRepository repository = ApplicationModel.getServiceRepository();

ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());

repository.registerProvider(

getUniqueServiceName(),

ref,

serviceDescriptor,

this,

serviceMetadata

);

List registryURLs = ConfigValidationUtils.loadRegistries(this, true);

for (ProtocolConfig protocolConfig : protocols) {

String pathKey = URL.buildKey(getContextPath(protocolConfig)

.map(p -> p + “/” + path)

.orElse(path), group, version);

// In case user specified path, register service one more time to map it to path.

repository.registerService(pathKey, interfaceClass);

// TODO, uncomment this line once service key is unified

serviceMetadata.setServiceKey(pathKey);

doExportUrlsFor1Protocol(protocolConfig, registryURLs);

}

}

  • 获取到一个ServiceRepository,根据前面分析的经验,可以看出这里得到的就是一个ServiceRepository对象。

  • 根据服务的接口名称和字节码封装一个ServiceDescriptor保存到repository的services中。并返回ServiceDescriptor

  • 调用registerProvider方法,将唯一服务名,服务的Provider,serviceDescriptor,当前对象,ServiceMetadata等传入方法。

解析注册中心的URLregistryURLs,这里返回的是如下的URL

registry://192.168.100.127:2181/org.apache.dubbo.registry.RegistryService?application=spring-cloud-alibaba-boot-dubbo-provider&default=true&dubbo=2.0.2&pid=48053&preferred=true&qos.enable=false&registry=zookeeper&release=2.7.7&timeout=10000&timestamp=1598411022992

  • 遍历protocols,根据遍历到的协议拼接成不同的pathKey,调用registerService进行注册,保存服务源信息

  • 根据不同的协议,调用doExportUrlsFor1Protocol方法进行注册

进入registerProvider方法,该方法会将传入的对象构建成一个ProviderModel对象。保存到相应的集合中,同时在ProviderModel对象初始化时,会调用将该接口的所有方法遍历,构建成一个ProviderMethodModel保存到methods中。

然后进入到doExportUrlsFor1Protocol,方法过长,这里就不贴代码了。其主要完成了以下功能

  • 根据初始化ServiceBean时传入的各个参数,封装成一个map

  • 获取当前服务器的host

  • 获取当前服务需要监听的port

  • 根据封装的参数,协议,host,port构建一个URL

  • 发布一个本地服务-injvm

  • 获取到配置的注册中心的URL,可以存在多个注册中心,这就是Dubbo对多注册中心的支持

  • 添加注册中心的URL的参数

  • 生成monitor的URL

  • 再次封装发布服务的URL的参数

  • 通过Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));将当前服务的url作为参数添加到注册中心的url上,然后使用registryURL和当前服务接口字节码,服务实现构建一个invoker,这是一个属于注册中心的invoker;

  • 使用Invoker和当前的ServiceConfig构建一个 DelegateProviderMetaDataInvoker对象

  • 调用Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);进行服务的发布

  • 将返回的exporter添加到exporters中

接下来看看是怎么获取到Invoker的,根据我们的SPI的知识,在没有参数中没有指定扩展点时,会使用默认@SPI注解上默认指定的扩展点,由于在ProxyFactory类上的注解为@SPI(“javassist”),所以可以知道这里获取到的扩展点为JavassistProxyFactory的对象,在进入JavassistProxyFactory的getInvoker()方法之前,根据我们学习SPI的知识,或许该扩展点存在一些包装,这里就不详细说明了,主要讲服务发布的主要流程。进入该类的getInvoker()方法。

public Invoker getInvoker(T proxy, Class type, URL url) {

// TODO Wrapper cannot handle this scenario correctly: the classname contains ‘$’

final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf(‘$’) < 0 ? proxy.getClass() : type);

return new AbstractProxyInvoker(proxy, type, url) {

@Override

protected Object doInvoke(T proxy, String methodName,

Class<?>[] parameterTypes,

Object[] arguments) throws Throwable {

return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);

}

};

}

根据传入的服务接口的class对象,动态生成的一个包装器,该包装器继承了Wrapper了,重写了invokeMethod()方法。重写方法如下

public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {

com.wangx.spring.cloud.alibaba.provider.HelloServiceImpl w;

try {

w = ((com.wangx.spring.cloud.alibaba.provider.HelloServiceImpl) $1);

} catch (Throwable e) {

throw new IllegalArgumentException(e);

}

try {

if (“hello”.equals($2) && $3.length == 1) {

return ($w) w.hello((java.lang.String) $4[0]);

}

} catch (Throwable e) {

throw new java.lang.reflect.InvocationTargetException(e);

}

throw new org.apache.dubbo.common.bytecode.NoSuchMethodException(“Not found method “” + $2 + “” in class com.wangx.spring.cloud.alibaba.provider.HelloServiceImpl.”);

}

所以,当AbstractProxyInvoker的doInvoke方法被调用的时候,会直接执行被传入服务提供者的具体方法。这样做的好处就是在服务启动时就将方法调用准备好,在被远程调用时,直接通过引用调用,而不需要通过反射调用。提高性能。回到doExportUrlsFor1Protocol方法,根据返回的invoker和当前对象包装一个DelegateProviderMetaDataInvoker对象,接下来调用Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);方法。

现在先来确定PROCTOL的具体实现是什么,PROCTOL是一个自适应的扩展点,它会生成一个Proctol&Adaptie的类,该类实现了Protocol接口,重写了Protocol的export和refer()方法。这里只讨论生成的export方法,如下:

public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {

if (arg0 == null) throw new IllegalArgumentException(“org.apache.dubbo.rpc.Invoker argument == null”);

if (arg0.getUrl() == null)

throw new IllegalArgumentException(“org.apache.dubbo.rpc.Invoker argument getUrl() == null”);

org.apache.dubbo.common.URL url = arg0.getUrl();

String extName = (url.getProtocol() == null ? “dubbo” : url.getProtocol());

if (extName == null)

throw new IllegalStateException(“Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (” + url.toString() + “) use keys([protocol])”);

org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);

return extension.export(arg0);

}

该方法会根据传入的invoker对象中的 protocol作为扩展名,获取Protocol的扩展实现,因为我上一步我们传入的是registryURl,根据注解的协议可以知道,通过自适应扩展对象获取到的扩展实现为RegistryProtocol的对象,进入该类的export方法。

public Exporter export(final Invoker originInvoker) throws RpcException {

URL registryUrl = getRegistryUrl(originInvoker);

// url to export locally

URL providerUrl = getProviderUrl(originInvoker);

// Subscribe the override data

// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call

// the same service. Because the subscribed is cached key with the name of the service, it causes the

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

总结

无论是哪家公司,都很重视高并发高可用的技术,重视基础,重视JVM。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。

最后我整理了一些面试真题资料,技术知识点剖析教程,还有和广大同仁一起交流学习共同进步,还有一些职业经验的分享。

面试了阿里,滴滴,网易,蚂蚁,最终有幸去了网易【面试题分享】

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

// the same service. Because the subscribed is cached key with the name of the service, it causes the

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-p5HX9EuA-1712784948473)]
[外链图片转存中…(img-xs3QLk4B-1712784948474)]
[外链图片转存中…(img-KmWFFEPn-1712784948475)]
[外链图片转存中…(img-AOauOL3V-1712784948475)]
[外链图片转存中…(img-M9Pm2zq5-1712784948475)]
[外链图片转存中…(img-YpSz4cdX-1712784948475)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-RNVMO1O2-1712784948476)]

总结

无论是哪家公司,都很重视高并发高可用的技术,重视基础,重视JVM。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。

最后我整理了一些面试真题资料,技术知识点剖析教程,还有和广大同仁一起交流学习共同进步,还有一些职业经验的分享。

[外链图片转存中…(img-McvQmbVb-1712784948476)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-R3ZU4FWk-1712784948476)]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值