All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sirius.kernel.di.Injector Maven / Gradle / Ivy

Go to download

Provides common core classes and the microkernel powering all Sirius applications

There is a newer version: 12.9.1
Show newest version
/*
 * Made with all the love in the world
 * by scireum in Remshalden, Germany
 *
 * Copyright by scireum GmbH
 * http://www.scireum.de - [email protected]
 */

package sirius.kernel.di;

import com.google.common.collect.Lists;
import sirius.kernel.Classpath;
import sirius.kernel.Sirius;
import sirius.kernel.health.Exceptions;
import sirius.kernel.health.Log;

import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Central class for collecting and injecting dependencies (which are called parts).
 * 

* Contains the micro kernel which keeps track of all parts and injects them where required. We consider this * a micro kernel, as it doesn't directly know any annotations. It rather delegates the work to classes which implement * defined interfaces. Therefore the implementation is rather short but easily extensible. *

* Parts are commonly added via subclasses of {@link ClassLoadAction}. These scan each class in the classpath * and instantiate and insert them into the {@link MutableGlobalContext} if required. Most of these * ClassLoadAction implementations trigger on annotations. A prominent example is the * {@link sirius.kernel.di.std.AutoRegisterAction} which loads all classes wearing the {@link sirius.kernel.di.std * .Register} * annotation. Subclasses of ClassLoadAction are discovered automatically. *

* Accessing parts can be done in two ways. First, one can access the current {@link GlobalContext} via * {@link #context()}. This can be used to retrieve parts by class or by class and name. The second way * to access parts is to use marker annotations like {@link sirius.kernel.di.std.Part}, * {@link sirius.kernel.di.std.Parts}. Again, these annotations are not processed by the micro kernel itself, but by * subclasses of {@link FieldAnnotationProcessor}. * To process all annotations of a given Java object, {@link GlobalContext#wire(Object)} can be used. This will * be automatically called for each part which is auto-instantiated by a ClassLoadAction. *

* Also all annotations on static fields are processed on system startup. This is a simple trick to pass a * part to objects which are frequently created and destroyed. */ public class Injector { /** * Logger used by the injection framework. This is public so that annotation processors in sub packages can * log through this logger. */ public static final Log LOG = Log.get("di"); private static PartRegistry ctx = new PartRegistry(); private static List> loadedClasses; private static List packageFilter; private static Classpath cp; private static List actions; private Injector() { } /** * Initializes the framework. Must be only called once on system startup. *

* This is automatically invoked by {@link sirius.kernel.Sirius#start(sirius.kernel.Setup)}. * * @param classpath the classpath used to enumerate all classes to be scanned */ public static void init(@Nonnull final Classpath classpath) { ctx = new PartRegistry(); // Make the context itself visible for GlobalContext... ctx.registerPart(ctx, GlobalContext.class); cp = classpath; loadedClasses = Lists.newArrayList(); actions = Lists.newArrayList(); packageFilter = Sirius.getSettings().getStringList("di.packageFilter"); LOG.INFO("Initializing the MicroKernel...."); LOG.INFO("~ Scanning .class files..."); classpath.find(Pattern.compile(".*?\\.class")).forEach(Injector::loadClass); LOG.INFO("~ Applying %d class load actions on %d classes...", actions.size(), loadedClasses.size()); loadedClasses.parallelStream().forEach(Injector::applyClassLoadActions); LOG.INFO("~ Initializing static parts-references..."); loadedClasses.parallelStream().forEach(ctx::wireClass); LOG.INFO("~ Initializing parts..."); ctx.processAnnotations(); } private static void loadClass(Matcher matcher) { String relativePath = matcher.group(); String className = relativePath.substring(0, relativePath.length() - 6).replace("/", "."); if (!shoudLoadClass(className)) { return; } try { if (LOG.isFINE()) { LOG.FINE("Found class: " + className); } Class clazz = Class.forName(className, true, cp.getLoader()); if (ClassLoadAction.class.isAssignableFrom(clazz) && !clazz.isInterface()) { createAndCollectClassLoadAction(className, clazz); } loadedClasses.add(clazz); } catch (NoClassDefFoundError e) { Exceptions.handle() .error(e) .to(LOG) .withSystemErrorMessage("Failed to load dependent class: %s", className) .handle(); } catch (Exception e) { Exceptions.handle() .error(e) .to(LOG) .withSystemErrorMessage("Failed to load class %s: %s (%s)", className) .handle(); } } private static void createAndCollectClassLoadAction(String className, Class clazz) { try { actions.add((ClassLoadAction) clazz.getDeclaredConstructor().newInstance()); } catch (Exception e) { Exceptions.handle() .error(e) .to(LOG) .withSystemErrorMessage("Failed to instantiate ClassLoadAction: %s - %s (%s)", className) .handle(); } } private static void applyClassLoadActions(Class clazz) { for (ClassLoadAction action : actions) { if (action.getTrigger() == null || clazz.isAnnotationPresent(action.getTrigger())) { if (LOG.isFINE()) { LOG.FINE("Auto-installing class: %s based on %s", clazz.getName(), action.getClass().getName()); } try { action.handle(ctx, clazz); } catch (Exception e) { Exceptions.handle() .error(e) .to(LOG) .withSystemErrorMessage("Failed to auto-load: %s with ClassLoadAction: %s: %s (%s)", clazz.getName(), action.getClass().getSimpleName()) .handle(); } } } } private static boolean shoudLoadClass(String className) { if (packageFilter.isEmpty()) { return true; } for (String filter : packageFilter) { if (className.startsWith(filter)) { return true; } } return false; } /** * Provides access to the global context, containing all parts *

* This can also be loaded into a class field using the {@link sirius.kernel.di.std.Part} annotation and * GlobalContext as field type. * * @return the global context containing all parts known to the system */ public static GlobalContext context() { return ctx; } /** * Returns a list of all loaded classes. * * @return a list of all classes detected at system startup */ public static List> getAllLoadedClasses() { return Collections.unmodifiableList(loadedClasses); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy