🌟 Welcome to follow the personal technology public account: Jesse Nobu
preface
In the last section of the Apache Dubbo series annotating the @Adaptive implementation principle, I encountered a new problem, which is if we execute the following code, according to my previous article parsing
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Copy the code
At this time, a Protocol $Adaptive class will be dynamically generated and loaded. Then, when we call the method with @adaptive modification, we pass in the URL to obtain the corresponding extension according to the corresponding field.
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ((url.getProtocol() == null)?"dubbo"
: url.getProtocol());
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
.getExtension(extName);
Copy the code
However, in some cases, calls to methods that do not call the target extension directly may be preceded by classes such as Fitler(filter), so this mechanism is very important in Dubbo!
Are you ready? Let’s Go~
A mirror exactly
First let’s recall that when Dubbo loads a class that has not been loaded, it loads it through the getExtensionClasses method. The specific order is as follows:
- call
getExtensionClasses
Get extension methods - call
loadExtensionClasses
Load extension method - call
loadDirectory
Load by directory resource - call
loadResource
Load by resource path - call
loadClass
Method loads a single class resource
The loadClass method does several things:
- Classes are
@Adaptive
modified - through
isWrapper
Method to determine if it is a wrapper class - The cache
@Activate
Embellished information
We focus on wrappers in this section. Let’s look at the Wrapper code. The first is by determining whether it is an extended class
private boolean isWrapperClass(Class
clazz) {
try {
clazz.getConstructor(type);
return true;
} catch (NoSuchMethodException e) {
return false; }}Copy the code
The above check is whether the current clazz has a constructor that is its parent, and if so it is a wrapper class. It is then added to the ExtensionLoader member property cachedWrapperClasses. This step is mainly to detect which classes are wrapper classes.
For example, the most classic in Dubbo is Protocol. Let’s start by looking at what the Protocol extension classes are. What do you think? Remember that the Dubbo SPI extension definition file defines the file name based on the package path of the interface. So Protocol package name. The full path is com. Alibaba dubbo.. RPC Protocol. So if we look at this file, we’ll see that there are many related definitions:
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapper
Copy the code
There are many classes above, but only three are identified as Wrapper classes. They are
ProtocolListenerWrapper
The filter chain calls the chain wrapperQosProtocolWrapper
Protocol listener wrapperProtocolFilterWrapper
Online operation and maintenance service wrapper
The createExtension method is used to create a class that has not been instantiated, so we can create cachedWrapperClasses and wrap it as follows:
private T createExtension(String name) { Class<? > clazz = getExtensionClasses().get(name);if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
// Get the corresponding cachedWrapperClasses from the member attributeSet<Class<? >> wrapperClasses = cachedWrapperClasses;if(wrapperClasses ! =null && !wrapperClasses.isEmpty()) {
/ / loop
for(Class<? > wrapperClass : wrapperClasses) {// instantiate while injectinginstance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); }}return instance;
} catch (Throwable t) {
// throw Exception...}}Copy the code
The method focus code above has been commented out. We’re still going to use examples. Assuming we are ready to instantiate DubboProtocol, the current instance=DubboProtocol. The ProtocolListenerWrapper instances the DubboProtocol as a constructor parameter (injectExtension is mainly responsible for method injection of the set* prefix). The ProtocolListenerWrapper is then assigned to Instance; Next, instantiate QosProtocolWrapper, again instance=ProtocolListenerWrapper as a constructor argument, and assign QosProtocolWrapper to instance; Finally, there is the ProtocolFilterWrapper, and the next steps are the same as above.
When instantiated, the last instance is ProtocolFilterWrapper. The corresponding Dubbo Holder holds a ProtocolFilterWrapper. And what can we do with that? What we can actually see is that this is a nested filter chain, which you can interpret as a call that implements a chain of responsibility, or as some kind of implementation of AOP (because Spring’s IOC actually looks up beans through beanName, too, But you can implement real instances of AOP by substitution), as long as you understand it!
Now it’s time to call. Why is the criterion for determining whether or not a Wrapper is a constructor parameter? In order to make the call the same way as the class that is actually being called. Take Protocol as an example. When we call the Export method of DubboProtocol, we are actually calling the Export method of the ProtocolFilterWrapper, so we are ostensibly calling the Export method of the Protocol. Is this taking full advantage of the interface?
Let’s try to parse the above example. ProtocolFilterWrapper#export
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
// Use the url protocol to check whether it is Registry
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
// Set up the invoker call chain and pass it to the next one
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
Copy the code
According to the flow above, we know that the ProtocolFilterWrapper holds a QosProtocolWrapper. Then we move on to QosProtocolWrapper#export.
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
// Use the url protocol to check whether it is Registry
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
startQosServer(invoker.getUrl());
return protocol.export(invoker);
}
// Call the holding object directly
return protocol.export(invoker);
}
Copy the code
We can see that the QosProtocolWrapper has formal processing code only in the Registry protocol, which actually starts a QosServer service.
Similarly, QosProtocolWrapper holds the Protocol ProtocolListenerWrapper.
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//// check whether it is Registry by url protocol
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
// Two steps
return new ListenerExporterWrapper<T>(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
Copy the code
The above is done in two steps:
- through
ExtensionLoader
To obtainExporterListener
The plugin is then passed inurl
Call the specific extension and get the return result information ProtocolListenerWrapper
Student: Holding is actuallyDubboProtocol
, so willDubboProtocol
The result of the method call is wrapped with the result information obtained in the first stepListenerExporterWrapper
In the case of the Protocol, most of the access is actually to my friend, which I’ll continue in the next section!
conclusion
This episode, actually discovered while reading Dubbo service exposure, is also a foreshadows for the next section. In the next section, I will continue to parse the source code for Dubbo’s service exposure process!
See you next time!