background

Then the front analysis org.apache.dubbo.com mon. The extension. ExtensionLoader# injectExtension (instance) function. Let’s recall that we’ve already analyzed how SPI creates implementation classes. This is where SPI’s functionality is supposed to end, but dubbo is a popular framework for improving usability. InjectExtension mainly extends Spring’s dependency injection. And spI-enabled wrappers.

Source code analysis

1.1

//org.apache.dubbo.common.extension.ExtensionLoader#injectExtension private T injectExtension(T instance) { if (objectFactory ! = null) {// Iterate over the Method in the implementation class, if it is a set Method continue for (Method Method: instance.getClass().getMethods()) { if (isSetter(method)) { if (method.getAnnotation(DisableInject.class) ! = null) { continue; } Class<? > pt = method.getParameterTypes()[0]; if (ReflectUtils.isPrimitives(pt)) { continue; } return username String property = getSetterProperty(method); / / asynchronous 1.2 source code analysis Object Object. = the objectFactory getExtension (pt, the property); if (object ! Invoke (instance, object); // Invoke (instance, object); } } } } return instance; }Copy the code

1.2

//Object object = objectFactory.getExtension(pt, property);
//org.apache.dubbo.common.extension.ExtensionFactory#getExtension
@SPI
public interface ExtensionFactory {
    <T> T getExtension(Class<T> type, String name);

Copy the code

This is clearly an extension point, the execution of the objectFactory. GetExtension () when we need to find the interface implementation class can continue to debug, we scan the objectFactory attribute definition

private final ExtensionFactory objectFactory; private ExtensionLoader(Class<? > type) {/ / type was introduced into the Job of our interface So assignment for ExtensionLoader. GetExtensionLoader (ExtensionFactory. Class). GetAdaptiveExtension () objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }Copy the code

In ExtensionLoader initialization time will give the property assignment to ExtensionLoader getExtensionLoader (ExtensionFactory. Class). GetAdaptiveExtension (), As we examined in the previous article, the method getAdaptiveExtension returns an adaptive extension point implementation of that extension point. Let’s look at the interface implementation first

We see that one of the AdaptiveExtensionFactory classes with class annotations @Adaptive goes naturally to class extension point implementation, not method extension point, The getAdaptiveExtension() method returns the AdaptiveExtensionFactory object

public AdaptiveExtensionFactory() { ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); List<ExtensionFactory> list = new ArrayList<ExtensionFactory>(); for (String name : loader.getSupportedExtensions()) { list.add(loader.getExtension(name)); } // Factories load all the implementation classes of ExtensionFactory, including // spiextenfactory and SpringExtensionFactory Factories = Collections.unmodifiableList(list); } public <T> T getExtension(Class<T> type, String name) { for (ExtensionFactory factory : factories) { T extension = factory.getExtension(type, name); if (extension ! = null) { return extension; } } return null; }Copy the code

In line 12, we iterate over the two extension points in the Factories, calling the getExtension(type, name) method on the extension points. For example, we add a User object to the ApolloConfigCenter implementation class in the previous demo

HupuUser = User; hupuUser = User; SpringExtensionFactory = getExtension

1.2.1 SpringExtensionFactory
public <T> T getExtension(Class<T> type, String name) {// Attribute type skips SPI annotation if (type.isInterface() && type.isannotationPresent (spi.class)) {return null; } // Iterate through the ApplicationContext container to 1.2.1.1 for (ApplicationContext context: CONTEXTS) {if (context.containsBean(name)) {// If you have used Spring you should know the familiar method, Call getBean to getBean Object bean = context.getbean (name); if (type.isInstance(bean)) { return (T) bean; } } } if (Object.class == type) { return null; } // For (ApplicationContext context: CONTEXTS) { try { return context.getBean(type); } catch (NoUniqueBeanDefinitionException multiBeanExe) { logger.warn("Find more than 1 spring extensions (beans) of type  " + type.getName() + ", will stop auto injection. Please make sure you have specified the concrete parameter type and there's only one extension  of that type."); } catch (NoSuchBeanDefinitionException noBeanExe) { if (logger.isDebugEnabled()) { logger.debug("Error when get spring extension(bean) for type:" + type.getName(), noBeanExe); } } } return null; }Copy the code
1.2.1.1
private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>(); //org... bbo.config.spring.extension.SpringExtensionFactory#addApplicationContext public static void addApplicationContext(ApplicationContext context) { CONTEXTS.add(context); / / collect the context into the set if (context instanceof ConfigurableApplicationContext) {((ConfigurableApplicationContext) context).registerShutdownHook(); DubboShutdownHook.getDubboShutdownHook().unregister(); } BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER); } //org.apache.dubbo.config.spring.ServiceBean#setApplicationContext public void SetApplicationContext (ApplicationContext ApplicationContext) {ServiceBean is dubbo's class, ApplicationContextAware // so that dubbo initialization will be called to the CONTEXTS that assigned ApplicationContext to //SpringExtensionFactory, This. ApplicationContext = applicationContext; SpringExtensionFactory.addApplicationContext(applicationContext); supportedApplicationListener = addApplicationListener(applicationContext, this); }Copy the code
1.2.2 SpiExtensionFactory

After looking at the implementation of SpringExtensionFactory, let’s look at the implementation of SpiExtensionFactory, which is simpler

public class SpiExtensionFactory implements ExtensionFactory { @Override public <T> T getExtension(Class<T> type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type); if (! Loader. GetSupportedExtensions (). The isEmpty ()) {/ / returns the extension points adaptive extension point return loader. GetAdaptiveExtension (); } } return null; }}Copy the code

ApolloConfigCenter again, SpiExtensionFactory means that if the ApolloConfigCenter class has an extension point attribute, the adaptive extension point of the extension point will be assigned

As above, login is an extension point with weixin, phone, etc. If we define ApolloConfigCenter as such, spi will attach a value to the login variable.

Process deug

To verify the above analysis, we run debug, not just on its own, but in conjunction with the Spring container.

//dubbo-provider.xml <? The XML version = "1.0" encoding = "utf-8"? > <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <bean id="myUser" class="com.poizon.study.provider.User"/> </beans> //com.poizon.study.provider.spi.NacosConfigCenter public class NacosConfigCenter implements ConfigCenter { private User myUser; Public User getMyUser() {return myUser; } public void setMyUser(User myUser) { this.myUser = myUser; } private Protocol protocol; Public void setProtocol(Protocol Protocol) {this. Protocol = Protocol; } @Override public String get(String key) { System.out.println(protocol); return "nacos " + myUser.getName(); }} / demo/test public static void main (String [] args) throws IOException {ClassPathXmlApplicationContext CTX = new ClassPathXmlApplicationContext("dubbo-provider.xml"); ctx.start(); ExtensionLoader<ConfigCenter> extensionLoader = ExtensionLoader.getExtensionLoader(ConfigCenter.class); ConfigCenter weixin = extensionLoader.getExtension("nacos"); String key = weixin.get("key"); System.out.println(key); / / print / / org. Apache. Dubbo. RPC. The Protocol $5 a9d6f02 Adaptive @ / / nacos loyal dog eight male System. In the read (); }Copy the code

Clearly, the myUser attribute of NacosConfigCenter has been assigned to the User bean in Spring, and the Protocol attribute has been assigned to the adaptive extension point of the Protocol interface.

conclusion

If the implementation class and property inherit from the same interface, it can be interpreted as a wrapper. Dubbo is used in many ways, such as MockClusterWrapper in Cluster, ProtocolFilterWrapper in Protocol, etc. We’ll talk about that later, but that’s all we have to say about SPI.