Refresh your Java skills--聊聊Java9 中模組化設計是如何實現類似IOC依賴注入效果及與其區別

知秋z發表於2017-11-07

場景引入

如何實現IOC的效果,我們可以來想想,無非就是一個隱式實現,而想要做到,總不能什麼都沒有,來個巧婦難為無米之炊的境地吧,所以說,米必須要有滴,在Spring中就是一個bean,也就是說,容器裡得有米,再官話點就是上下文中得存在所需要的bean。同樣模組化中兩個互相隔離的模組想要達到這種效果,也要先往jvm裡扔個物件進去的,然後who use ,who get 就可以了。

請看例子(可以認為是我們平常寫的SpringMVC專案中的service->serviceImpl->controller):

service介面化模組

package com.example.api;
      public interface CodecFactory {
          Encoder getEncoder(String encodingName);
          Decoder getDecoder(String encodingName);

      }複製程式碼

上面這個介面所在的模組定義:

module migo.codec.api {
   exports com.example.api;
}複製程式碼

serviceImpl化模組

接著,我們定義一個實現模組:

module migo.codec.service {
   requires com.example.api;

   provides com.example.api.CodecFactory with com.example.service.codec.CodecFactoryImpl;
}複製程式碼

具體實現就省略了。

controller化模組

最後我們在最上層的模組內使用:

module migo.codec.controller {
   requires migo.codec.api;

   uses com.example.api.CodecFactory;
}複製程式碼

具體的controller模組內使用的程式碼如下:

ServiceLoader<CodecFactory> loader = ServiceLoader.load(CodecFactory.class);
      for (CodecFactory factory : loader) {
          Encoder enc = factory.getEncoder("PNG");
          if (enc != null)
              ... use enc to encode a PNG file
              break;
          }複製程式碼

或者:

public static void main(String... args) {
    CodecFactory cf =
      ServiceLoader.load(CodecFactory.class)
                   .findFirst()
                   .orElse(getFallBack());

      if(cf == null) {
        System.out.println("Using a fallback");
      } else {
        System.out.println("Found a service");
      }
  }

  private static CodecFactory getFallBack() {
    return null;
  }複製程式碼

亦或者假如有很多服務實現的提供者,而某個提供服務實現的provider(也就是serviceImpl)上面有新增註解@PNG,而我們想使用帶有這個註解的例項,可以使用以下程式碼:

ServiceLoader<CodecFactory> loader = ServiceLoader.load(CodecFactory.class);
      Set<CodecFactory> pngFactories = loader
             .stream()                                              
             .filter(p -> p.type().isAnnotationPresent(PNG.class))  
             .map(Provider::get)                                   
             .collect(Collectors.toSet());複製程式碼

內部工作機制原理

具體思路:

通過在模組定義裡面的provides aaa with aaaImpl 這個功能,可以很容易的想到key value組合
當我們碰到這對關鍵字的時候,我們就會解析並將aaa做為keyaaaImpl新增到一個list中並將這個list作為value,並新增到一個Map<String,list>
在我們碰到uses關鍵字(原始碼裡面acc會去確定這個許可權),並通過ServiceLoader.load(key)來找到這個key所對應的一個包含了實現類具體地址的list,可能有多個,那麼,擴充功能,我們使用一個裝飾模式,也就是繼承了Iterable這個介面,可以達到遍歷並生成具體例項來達到要求。

原始碼解析

確定米粒的路徑

那麼按照這個思路,我們反著來找下,這裡只列關鍵程式碼:

從上面的Demo中,我們可以看到,通過類的class位元組碼來載入:

之前有說,巧婦難為無米之炊,所以這個上下文很重要,我們的類載入器也是要講究上下文的

 /**
     * Creates a new service loader for the given service type, using the
     * current thread's {@linkplain java.lang.Thread#getContextClassLoader
     * context class loader}.
     *
     * <p> An invocation of this convenience method of the form
     * <pre>{@code
     *     ServiceLoader.load(service)
     * }</pre>
     *
     * is equivalent to
     *
     * <pre>{@code
     *     ServiceLoader.load(service, Thread.currentThread().getContextClassLoader())
     * }</pre>
     *
     * @apiNote Service loader objects obtained with this method should not be
     * cached VM-wide. For example, different applications in the same VM may
     * have different thread context class loaders. A lookup by one application
     * may locate a service provider that is only visible via its thread
     * context class loader and so is not suitable to be located by the other
     * application. Memory leaks can also arise. A thread local may be suited
     * to some applications.
     *
     * @param  <S> the class of the service type
     *
     * @param  service
     *         The interface or abstract class representing the service
     *
     * @return A new service loader
     *
     * @throws ServiceConfigurationError
     *         if the service type is not accessible to the caller or the
     *         caller is in an explicit module and its module descriptor does
     *         not declare that it uses {@code service}
     *
     * @revised 9
     * @spec JPMS
     */
    @CallerSensitive
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return new ServiceLoader<>(Reflection.getCallerClass(), service, cl);
    }複製程式碼

我們進去這個ServiceLoader,其實無非就是一個構造器而已了,關鍵程式碼我截下:

  this.service = svc;
        this.serviceName = svc.getName();
        this.layer = null;
        this.loader = cl;
        this.acc = (System.getSecurityManager() != null)
                ? AccessController.getContext()
                : null;複製程式碼

有了這個載入器之後,其實我們就拿到了上下文和訪問許可權的一些東西,我們再來看看這個類的欄位:

public final class ServiceLoader<S>
    implements Iterable<S>
{
    // The class or interface representing the service being loaded
    private final Class<S> service;

    // The class of the service type
    private final String serviceName;

    // The module layer used to locate providers; null when locating
    // providers using a class loader
    private final ModuleLayer layer;

    // The class loader used to locate, load, and instantiate providers;
    // null when locating provider using a module layer
    private final ClassLoader loader;

    // The access control context taken when the ServiceLoader is created
    private final AccessControlContext acc;

    // The lazy-lookup iterator for iterator operations
    private Iterator<Provider<S>> lookupIterator1;
    private final List<S> instantiatedProviders = new ArrayList<>();

    // The lazy-lookup iterator for stream operations
    private Iterator<Provider<S>> lookupIterator2;
    private final List<Provider<S>> loadedProviders = new ArrayList<>();
    private boolean loadedAllProviders; // true when all providers loaded

    // Incremented when reload is called
    private int reloadCount;

    private static JavaLangAccess LANG_ACCESS;複製程式碼

可以看到,它實現了按照我們分析的Iterable介面,這樣我們就可以多了很多操作,而且我們也看到了下面這幾個東西,這樣我們就可以做事情了:

 private Iterator<Provider<S>> lookupIterator2;
 private final List<Provider<S>> loadedProviders = new ArrayList<>();
 private boolean loadedAllProviders; // true when all providers loaded複製程式碼

我們走進findFirst這個方法來看看:

public Optional<S> findFirst() {
        Iterator<S> iterator = iterator();
        if (iterator.hasNext()) {
            return Optional.of(iterator.next());
        } else {
            return Optional.empty();
        }
    }複製程式碼

我們看到了iterator()這個方法:

 public Iterator<S> iterator() {

        // create lookup iterator if needed
        if (lookupIterator1 == null) {
            lookupIterator1 = newLookupIterator();
        }

        return new Iterator<S>() {

            // record reload count
            final int expectedReloadCount = ServiceLoader.this.reloadCount;
          ...
        }複製程式碼

現在newLookupIterator()進入到我們的視野中,沒有條件建立條件,剛開始我們可沒有拿到米,現在去找米去:

    /**
     * Returns a new lookup iterator.
     */
    private Iterator<Provider<S>> newLookupIterator() {
        assert layer == null || loader == null;
        if (layer != null) {
            return new LayerLookupIterator<>();
        } else {
            Iterator<Provider<S>> first = new ModuleServicesLookupIterator<>();
            Iterator<Provider<S>> second = new LazyClassPathLookupIterator<>();
            return new Iterator<Provider<S>>() {
                @Override
                public boolean hasNext() {
                    return (first.hasNext() || second.hasNext());
                }
                @Override
                public Provider<S> next() {
                    if (first.hasNext()) {
                        return first.next();
                    } else if (second.hasNext()) {
                        return second.next();
                    } else {
                        throw new NoSuchElementException();
                    }
                }
            };
        }
    }複製程式碼

這裡拋開其他我們來看ModuleServicesLookupIterator()這個建構函式 :

ModuleServicesLookupIterator() {
            this.currentLoader = loader;
            this.iterator = iteratorFor(loader);
        }複製程式碼

映入眼簾的是iteratorFor(ClassLoader loader)這個方法:

/**
         * Returns an iterator to iterate over the implementations of {@code
         * service} in modules defined to the given class loader or in custom
         * layers with a module defined to this class loader.
         */
        private Iterator<ServiceProvider> iteratorFor(ClassLoader loader) {
            // modules defined to the class loader
            ServicesCatalog catalog;
            if (loader == null) {
                catalog = BootLoader.getServicesCatalog();
            } else {
                catalog = ServicesCatalog.getServicesCatalogOrNull(loader);
            }
              //此處往下到我中文標記結束就是我們的正主了
            List<ServiceProvider> providers;
            if (catalog == null) {
                providers = List.of();
            } else {
                providers = catalog.findServices(serviceName);
            }
          //結束

            // modules in layers that define modules to the class loader
            ClassLoader platformClassLoader = ClassLoaders.platformClassLoader();
            if (loader == null || loader == platformClassLoader) {
                return providers.iterator();
            } else {
                List<ServiceProvider> allProviders = new ArrayList<>(providers);
                Iterator<ModuleLayer> iterator = LANG_ACCESS.layers(loader).iterator();
                while (iterator.hasNext()) {
                    ModuleLayer layer = iterator.next();
                    for (ServiceProvider sp : providers(layer)) {
                        ClassLoader l = loaderFor(sp.module());
                        if (l != null && l != platformClassLoader) {
                            allProviders.add(sp);
                        }
                    }
                }
                return allProviders.iterator();
            }
        }複製程式碼

這裡終於找到了findServices(String service)這個方法:

    /**
     * Returns the (possibly empty) list of service providers that implement
     * the given service type.
     */
    public List<ServiceProvider> findServices(String service) {
        return map.getOrDefault(service, Collections.emptyList());
    }複製程式碼

結合getOrDefault的原始碼可知:

 default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }複製程式碼

是不是和我們的具體思路接上軌了

拿到我們想要的大米

而我們的provider例項從何而來,請容我娓娓道來咯:

我們從jdk.internal.module.Modules這個模組定義類中可以找到addProvides這個方法,也就是說在我們載入這個模組的時候,這個動作就已經要幹活了:

     /**
     * Updates module m to provide a service
     */
    public static void addProvides(Module m, Class<?> service, Class<?> impl) {
        ModuleLayer layer = m.getLayer();

        PrivilegedAction<ClassLoader> pa = m::getClassLoader;
        ClassLoader loader = AccessController.doPrivileged(pa);

        ClassLoader platformClassLoader = ClassLoaders.platformClassLoader();
        if (layer == null || loader == null || loader == platformClassLoader) {
            // update ClassLoader catalog
            ServicesCatalog catalog;
            if (loader == null) {
                catalog = BootLoader.getServicesCatalog();
            } else {
                catalog = ServicesCatalog.getServicesCatalog(loader);
            }
            catalog.addProvider(m, service, impl);
        }

        if (layer != null) {
            // update Layer catalog
            JLA.getServicesCatalog(layer).addProvider(m, service, impl);
        }
    }複製程式碼

然後我們可以從sun.instrument.InstrumentationImpl這個類來看到其工作方式(通過其註釋就可以看到這個類和JVM相關):

在載入模組的時候就執行了下面的程式碼,看下面update provides這個註釋的程式碼可以知道其呼叫了上面的addProvides這個方法,而最後也是呼叫了addProvider(m, service, impl)

/**
 * The Java side of the JPLIS implementation. Works in concert with a native JVMTI agent
 * to implement the JPLIS API set. Provides both the Java API implementation of
 * the Instrumentation interface and utility Java routines to support the native code.
 * Keeps a pointer to the native data structure in a scalar field to allow native
 * processing behind native methods.
 */
public class InstrumentationImpl implements Instrumentation {
  ...
  @Override
    public void redefineModule(Module module,
                               Set<Module> extraReads,
                               Map<String, Set<Module>> extraExports,
                               Map<String, Set<Module>> extraOpens,
                               Set<Class<?>> extraUses,
                               Map<Class<?>, List<Class<?>>> extraProvides)
    {
        if (!module.isNamed())
            return;

        if (!isModifiableModule(module))
            throw new UnmodifiableModuleException(module.getName());

        // copy and check reads
        extraReads = new HashSet<>(extraReads);
        if (extraReads.contains(null))
            throw new NullPointerException("'extraReads' contains null");

        // copy and check exports and opens
        extraExports = cloneAndCheckMap(module, extraExports);
        extraOpens = cloneAndCheckMap(module, extraOpens);

        // copy and check uses
        extraUses = new HashSet<>(extraUses);
        if (extraUses.contains(null))
            throw new NullPointerException("'extraUses' contains null");

        // copy and check provides
        Map<Class<?>, List<Class<?>>> tmpProvides = new HashMap<>();
        for (Map.Entry<Class<?>, List<Class<?>>> e : extraProvides.entrySet()) {
            Class<?> service = e.getKey();
            if (service == null)
                throw new NullPointerException("'extraProvides' contains null");
            List<Class<?>> providers = new ArrayList<>(e.getValue());
            if (providers.isEmpty())
                throw new IllegalArgumentException("list of providers is empty");
            providers.forEach(p -> {
                if (p.getModule() != module)
                    throw new IllegalArgumentException(p + " not in " + module);
                if (!service.isAssignableFrom(p))
                    throw new IllegalArgumentException(p + " is not a " + service);
            });
            tmpProvides.put(service, providers);
        }
        extraProvides = tmpProvides;


        // update reads
        extraReads.forEach(m -> Modules.addReads(module, m));

        // update exports
        for (Map.Entry<String, Set<Module>> e : extraExports.entrySet()) {
            String pkg = e.getKey();
            Set<Module> targets = e.getValue();
            targets.forEach(m -> Modules.addExports(module, pkg, m));
        }

        // update opens
        for (Map.Entry<String, Set<Module>> e : extraOpens.entrySet()) {
            String pkg = e.getKey();
            Set<Module> targets = e.getValue();
            targets.forEach(m -> Modules.addOpens(module, pkg, m));
        }

        // update uses
        extraUses.forEach(service -> Modules.addUses(module, service));

        // update provides
        for (Map.Entry<Class<?>, List<Class<?>>> e : extraProvides.entrySet()) {
            Class<?> service = e.getKey();
            List<Class<?>> providers = e.getValue();
            providers.forEach(p -> Modules.addProvides(module, service, p));
        }
    }
  ...
}複製程式碼

Instrumentation介面有一段很重要的註釋,大家自己看吧,就不多說了:

/**
 * This class provides services needed to instrument Java
 * programming language code.
 * Instrumentation is the addition of byte-codes to methods for the
 * purpose of gathering data to be utilized by tools.
 * Since the changes are purely additive, these tools do not modify
 * application state or behavior.
 * Examples of such benign tools include monitoring agents, profilers,
 * coverage analyzers, and event loggers.
 *
 * <P>
 * There are two ways to obtain an instance of the
 * <code>Instrumentation</code> interface:
 *
 * <ol>
 *   <li><p> When a JVM is launched in a way that indicates an agent
 *     class. In that case an <code>Instrumentation</code> instance
 *     is passed to the <code>premain</code> method of the agent class.
 *     </p></li>
 *   <li><p> When a JVM provides a mechanism to start agents sometime
 *     after the JVM is launched. In that case an <code>Instrumentation</code>
 *     instance is passed to the <code>agentmain</code> method of the
 *     agent code. </p> </li>
 * </ol>
 * <p>
 * These mechanisms are described in the
 * {@linkplain java.lang.instrument package specification}.
 * <p>
 * Once an agent acquires an <code>Instrumentation</code> instance,
 * the agent may call methods on the instance at any time.
 *
 * @since   1.5
 */
public interface Instrumentation {
}複製程式碼

那麼,我們最後,走入addProvider(m, service, impl)這個方法中:

    /**
     * Add a provider in the given module to this services catalog
     *
     * @apiNote This method is for use by java.lang.instrument
     */
    public void addProvider(Module module, Class<?> service, Class<?> impl) {
        List<ServiceProvider> list = providers(service.getName());
        list.add(new ServiceProvider(module, impl.getName()));
    }
...
 public final class ServiceProvider {
        private final Module module;
        private final String providerName;

        public ServiceProvider(Module module, String providerName) {
            this.module = module;
            this.providerName = providerName;
        }
   ...
 }複製程式碼

再經過了這麼曲曲折折的過程,終於拿到了ServiceProvider,裡面包括了我們所要呼叫實現類的地址資訊

於是,看下ServiceLoader這個類定義的Provider靜態內部介面:


     /**
     * Represents a service provider located by {@code ServiceLoader}.
     *
     * <p> When using a loader's {@link ServiceLoader#stream() stream()} method
     * then the elements are of type {@code Provider}. This allows processing
     * to select or filter on the provider class without instantiating the
     * provider. </p>
     *
     * @param  <S> The service type
     * @since 9
     * @spec JPMS
     */
    public static interface Provider<S> extends Supplier<S> {
        /**
         * Returns the provider type. There is no guarantee that this type is
         * accessible or that it has a public no-args constructor. The {@link
         * #get() get()} method should be used to obtain the provider instance.
         *
         * <p> When a module declares that the provider class is created by a
         * provider factory then this method returns the return type of its
         * public static "{@code provider()}" method.
         *
         * @return The provider type
         */
        Class<? extends S> type();

        /**
         * Returns an instance of the provider.
         *
         * @return An instance of the provider.
         *
         * @throws ServiceConfigurationError
         *         If the service provider cannot be instantiated, or in the
         *         case of a provider factory, the public static
         *         "{@code provider()}" method returns {@code null} or throws
         *         an error or exception. The {@code ServiceConfigurationError}
         *         will carry an appropriate cause where possible.
         */
        @Override S get();
    }複製程式碼

然後我們回到之前追到的iteratorFor方法,知道其返回的是 Iterator<ServiceProvider>型別

    /**
         * Returns an iterator to iterate over the implementations of {@code
         * service} in modules defined to the given class loader or in custom
         * layers with a module defined to this class loader.
         */
        private Iterator<ServiceProvider> iteratorFor(ClassLoader loader) {
            // modules defined to the class loader
            ServicesCatalog catalog;
            if (loader == null) {
                catalog = BootLoader.getServicesCatalog();
            } else {
                catalog = ServicesCatalog.getServicesCatalogOrNull(loader);
            }
            List<ServiceProvider> providers;
            if (catalog == null) {
                providers = List.of();
            } else {
                providers = catalog.findServices(serviceName);
            }
          ...
        }複製程式碼

然後回到ModuleServicesLookupIterator()這個建構函式,直接看這個內部類,也就是呼叫這個

 /**
     * Implements lazy service provider lookup of service providers that
     * are provided by modules defined to a class loader or to modules in
     * layers with a module defined to the class loader.
     */
    private final class ModuleServicesLookupIterator<T>
        implements Iterator<Provider<T>>
    {
        ClassLoader currentLoader;
        Iterator<ServiceProvider> iterator;

        Provider<T> nextProvider;
        ServiceConfigurationError nextError;

        ModuleServicesLookupIterator() {
            this.currentLoader = loader;
            this.iterator = iteratorFor(loader);
        }

        ...
    }複製程式碼

newLookupIterator這個方法中得到ModuleServicesLookupIterator的例項first,並呼叫其hasNext方法

 /**
     * Returns a new lookup iterator.
     */
    private Iterator<Provider<S>> newLookupIterator() {
        assert layer == null || loader == null;
        if (layer != null) {
            return new LayerLookupIterator<>();
        } else {
            Iterator<Provider<S>> first = new ModuleServicesLookupIterator<>();
            Iterator<Provider<S>> second = new LazyClassPathLookupIterator<>();
            return new Iterator<Provider<S>>() {
                @Override
                public boolean hasNext() {
                    return (first.hasNext() || second.hasNext());
                }
                @Override
                public Provider<S> next() {
                    if (first.hasNext()) {
                        return first.next();
                    } else if (second.hasNext()) {
                        return second.next();
                    } else {
                        throw new NoSuchElementException();
                    }
                }
            };
        }複製程式碼

我們來進入這個hasNext方法,也就是在這裡,呼叫了loadProvider生成了一個bean


        @Override
        public boolean hasNext() {
            while (nextProvider == null && nextError == null) {
                // get next provider to load
                while (!iterator.hasNext()) {
                    if (currentLoader == null) {
                        return false;
                    } else {
                        currentLoader = currentLoader.getParent();
                        iterator = iteratorFor(currentLoader);
                    }
                }

                // attempt to load provider
                ServiceProvider provider = iterator.next();
                try {
                    @SuppressWarnings("unchecked")
                    Provider<T> next = (Provider<T>) loadProvider(provider);
                    nextProvider = next;
                } catch (ServiceConfigurationError e) {
                    nextError = e;
                }
            }
            return true;
        }

        @Override
        public Provider<T> next() {
            if (!hasNext())
                throw new NoSuchElementException();

            Provider<T> provider = nextProvider;
            if (provider != null) {
                nextProvider = null;
                return provider;
            } else {
                ServiceConfigurationError e = nextError;
                assert e != null;
                nextError = null;
                throw e;
            }
        }
    }複製程式碼

走進這個loadProvider方法,拋開前面所有,我們只看最後返回為:new ProviderImpl<S>(service, type, ctor, acc)

/**
     * Loads a service provider in a module.
     *
     * Returns {@code null} if the service provider's module doesn't read
     * the module with the service type.
     *
     * @throws ServiceConfigurationError if the class cannot be loaded or
     *         isn't the expected sub-type (or doesn't define a provider
     *         factory method that returns the expected type)
     */
    private Provider<S> loadProvider(ServiceProvider provider) {
        Module module = provider.module();
        if (!module.canRead(service.getModule())) {
            // module does not read the module with the service type
            return null;
        }

        String cn = provider.providerName();
        Class<?> clazz = null;
        if (acc == null) {
            try {
                clazz = Class.forName(module, cn);
            } catch (LinkageError e) {
                fail(service, "Unable to load " + cn, e);
            }
        } else {
            PrivilegedExceptionAction<Class<?>> pa = () -> Class.forName(module, cn);
            try {
                clazz = AccessController.doPrivileged(pa);
            } catch (PrivilegedActionException pae) {
                Throwable x = pae.getCause();
                fail(service, "Unable to load " + cn, x);
                return null;
            }
        }
        if (clazz == null) {
            fail(service, "Provider " + cn + " not found");
        }

        int mods = clazz.getModifiers();
        if (!Modifier.isPublic(mods)) {
            fail(service, clazz + " is not public");
        }

        // if provider in explicit module then check for static factory method
        if (inExplicitModule(clazz)) {
            Method factoryMethod = findStaticProviderMethod(clazz);
            if (factoryMethod != null) {
                Class<?> returnType = factoryMethod.getReturnType();
                if (!service.isAssignableFrom(returnType)) {
                    fail(service, factoryMethod + " return type not a subtype");
                }

                @SuppressWarnings("unchecked")
                Class<? extends S> type = (Class<? extends S>) returnType;
                return new ProviderImpl<S>(service, type, factoryMethod, acc);
            }
        }

        // no factory method so must be a subtype
        if (!service.isAssignableFrom(clazz)) {
            fail(service, clazz.getName() + " not a subtype");
        }

        @SuppressWarnings("unchecked")
        Class<? extends S> type = (Class<? extends S>) clazz;
        @SuppressWarnings("unchecked")
        Constructor<? extends S> ctor = (Constructor<? extends S> ) getConstructor(clazz);
        return new ProviderImpl<S>(service, type, ctor, acc);
    }複製程式碼

最後,我們通過檢視這個ProviderImpl類終於得到了我們想要得到的結果。

 /**
     * A Provider implementation that supports invoking, with reduced
     * permissions, the static factory to obtain the provider or the
     * provider's no-arg constructor.
     */
    private static class ProviderImpl<S> implements Provider<S> {
        final Class<S> service;
        final Class<? extends S> type;
        final Method factoryMethod;  // factory method or null
        final Constructor<? extends S> ctor; // public no-args constructor or null
        final AccessControlContext acc;

        ProviderImpl(Class<S> service,
                     Class<? extends S> type,
                     Method factoryMethod,
                     AccessControlContext acc) {
            this.service = service;
            this.type = type;
            this.factoryMethod = factoryMethod;
            this.ctor = null;
            this.acc = acc;
        }

        ProviderImpl(Class<S> service,
                     Class<? extends S> type,
                     Constructor<? extends S> ctor,
                     AccessControlContext acc) {
            this.service = service;
            this.type = type;
            this.factoryMethod = null;
            this.ctor = ctor;
            this.acc = acc;
        }

        @Override
        public Class<? extends S> type() {
            return type;
        }

        @Override
        public S get() {
            if (factoryMethod != null) {
                return invokeFactoryMethod();
            } else {
                return newInstance();
            }
        }複製程式碼

IOC和模組化所提供的類似效果的最大的區別就是,前者是提供了例項化的bean(即便是通過AOP實現的,這點很重要,Java9模組化在使用Spring的時候會有特別的設定),而且是基於Spring容器的單例的存在(多例注入的問題請參考我這方面的Spring原始碼解析),後者是提供了class位元組碼所在的路徑,用的時候內部會自行生成例項,所以是多例的。

其實整個過程,Java的模組化檔案系統起了很大的作用(這塊看情況假如篇幅比較長久不放在我的書裡了),然後自己追原始碼的思路也在這裡給大家展現了一番,希望可以對大家有所幫助,看原始碼不要上來就瞎找的。另外,最重要的一點就是,不要因為原始碼很多,很複雜就輕言放棄,看的多了,看的久了,自然就有一套屬於自己的方法論了。

相關文章