You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2021/07/26 01:12:51 UTC

[logging-log4j2] 05/05: Add richer plugin bean metadata generation

This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch mean-bean-machine
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 4bf8c3ea0f78c9e99474dabc0e32cd20b7403390
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Sun Jul 25 20:12:06 2021 -0500

    Add richer plugin bean metadata generation
---
 .../log4j/plugins/di/model/DisposesMethod.java     |  53 +++
 .../log4j/plugins/di/model/GenericPlugin.java      |  48 +++
 .../log4j/plugins/di/model/InjectionTarget.java    |  49 +++
 .../log4j/plugins/di/model/PluginModule.java       |  32 ++
 .../log4j/plugins/di/model/PluginSource.java       |  29 ++
 .../log4j/plugins/di/model/ProducerField.java      |  55 +++
 .../log4j/plugins/di/model/ProducerMethod.java     |  62 +++
 .../logging/log4j/plugins/di/spi/PluginModule.java |  54 ---
 .../log4j/plugins/processor/BeanProcessor.java     | 417 ++++++++++++++-------
 log4j-plugins/src/main/java9/module-info.java      |   6 +-
 log4j-plugins/src/test/java-test9/module-info.java |   2 +-
 .../log4j/plugins/processor/BeanProcessorTest.java |  28 +-
 log4j-plugins/src/test/java9/module-info.java      |   6 +-
 13 files changed, 635 insertions(+), 206 deletions(-)

diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/DisposesMethod.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/DisposesMethod.java
new file mode 100644
index 0000000..02175cb
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/DisposesMethod.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.di.model;
+
+import org.apache.logging.log4j.plugins.di.Disposes;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class DisposesMethod implements PluginSource {
+    private final String declaringClassName;
+    private final String disposesTypeName;
+
+    public DisposesMethod(final String declaringClassName, final String disposesTypeName) {
+        this.declaringClassName = declaringClassName;
+        this.disposesTypeName = disposesTypeName;
+    }
+
+    @Override
+    public String getDeclaringClassName() {
+        return declaringClassName;
+    }
+
+    public String getDisposesTypeName() {
+        return disposesTypeName;
+    }
+
+    @Override
+    public Set<Class<?>> getImplementedInterfaces() {
+        return Set.of();
+    }
+
+    @Override
+    public Class<? extends Annotation> getScopeType() {
+        // not a real scope
+        return Disposes.class;
+    }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/GenericPlugin.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/GenericPlugin.java
new file mode 100644
index 0000000..3bff77c
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/GenericPlugin.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.di.model;
+
+import org.apache.logging.log4j.plugins.Plugin;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class GenericPlugin implements PluginSource {
+    private final String declaringClassName;
+    private final Set<Class<?>> implementedInterfaces;
+
+    public GenericPlugin(final String declaringClassName, final Set<Class<?>> implementedInterfaces) {
+        this.declaringClassName = declaringClassName;
+        this.implementedInterfaces = implementedInterfaces;
+    }
+
+    @Override
+    public String getDeclaringClassName() {
+        return declaringClassName;
+    }
+
+    @Override
+    public Set<Class<?>> getImplementedInterfaces() {
+        return implementedInterfaces;
+    }
+
+    @Override
+    public Class<? extends Annotation> getScopeType() {
+        return Plugin.class;
+    }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/InjectionTarget.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/InjectionTarget.java
new file mode 100644
index 0000000..a6676e5
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/InjectionTarget.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.di.model;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class InjectionTarget implements PluginSource {
+    private final String className;
+    private final Set<Class<?>> implementedInterfaces;
+    private final Class<? extends Annotation> scopeType;
+
+    public InjectionTarget(final String className, final Set<Class<?>> implementedInterfaces,
+                           final Class<? extends Annotation> scopeType) {
+        this.className = className;
+        this.implementedInterfaces = implementedInterfaces;
+        this.scopeType = scopeType;
+    }
+
+    @Override
+    public String getDeclaringClassName() {
+        return className;
+    }
+
+    @Override
+    public Set<Class<?>> getImplementedInterfaces() {
+        return implementedInterfaces;
+    }
+
+    @Override
+    public Class<? extends Annotation> getScopeType() {
+        return scopeType;
+    }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginModule.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginModule.java
new file mode 100644
index 0000000..70b7093
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginModule.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.di.model;
+
+import java.util.List;
+
+public abstract class PluginModule {
+    private final List<PluginSource> pluginSources;
+
+    protected PluginModule(final List<PluginSource> pluginSources) {
+        this.pluginSources = pluginSources;
+    }
+
+    public List<PluginSource> getPluginSources() {
+        return pluginSources;
+    }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginSource.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginSource.java
new file mode 100644
index 0000000..c144f63
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/PluginSource.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.di.model;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public interface PluginSource {
+    String getDeclaringClassName();
+
+    Set<Class<?>> getImplementedInterfaces();
+
+    Class<? extends Annotation> getScopeType();
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerField.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerField.java
new file mode 100644
index 0000000..acfc542
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerField.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.di.model;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class ProducerField implements PluginSource {
+    private final String declaringClassName;
+    private final String fieldName;
+    private final Set<Class<?>> implementedInterfaces;
+    private final Class<? extends Annotation> scopeType;
+
+    public ProducerField(final String declaringClassName, final String fieldName,
+                         final Set<Class<?>> implementedInterfaces, final Class<? extends Annotation> scopeType) {
+        this.declaringClassName = declaringClassName;
+        this.fieldName = fieldName;
+        this.implementedInterfaces = implementedInterfaces;
+        this.scopeType = scopeType;
+    }
+
+    @Override
+    public String getDeclaringClassName() {
+        return declaringClassName;
+    }
+
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    @Override
+    public Set<Class<?>> getImplementedInterfaces() {
+        return implementedInterfaces;
+    }
+
+    @Override
+    public Class<? extends Annotation> getScopeType() {
+        return scopeType;
+    }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java
new file mode 100644
index 0000000..5c836b4
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/model/ProducerMethod.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.di.model;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class ProducerMethod implements PluginSource {
+    private final String declaringClassName;
+    private final String returnTypeClassName;
+    private final String methodName;
+    private final Set<Class<?>> implementedInterfaces;
+    private final Class<? extends Annotation> scopeType;
+
+    public ProducerMethod(final String declaringClassName, final String returnTypeClassName,
+                          final String methodName, final Set<Class<?>> implementedInterfaces,
+                          final Class<? extends Annotation> scopeType) {
+        this.declaringClassName = declaringClassName;
+        this.returnTypeClassName = returnTypeClassName;
+        this.methodName = methodName;
+        this.implementedInterfaces = implementedInterfaces;
+        this.scopeType = scopeType;
+    }
+
+    @Override
+    public String getDeclaringClassName() {
+        return declaringClassName;
+    }
+
+    public String getReturnTypeClassName() {
+        return returnTypeClassName;
+    }
+
+    public String getMethodName() {
+        return methodName;
+    }
+
+    @Override
+    public Set<Class<?>> getImplementedInterfaces() {
+        return implementedInterfaces;
+    }
+
+    @Override
+    public Class<? extends Annotation> getScopeType() {
+        return scopeType;
+    }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/PluginModule.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/PluginModule.java
deleted file mode 100644
index 279be74..0000000
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/spi/PluginModule.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.plugins.di.spi;
-
-import java.util.Map;
-import java.util.Set;
-
-public abstract class PluginModule {
-    private final Map<String, Set<String>> injectionBeans;
-    private final Map<String, Set<String>> producerBeans;
-    private final Set<String> destructorBeans;
-    private final Map<String, Set<String>> pluginBeans;
-
-    protected PluginModule(final Map<String, Set<String>> injectionBeans,
-                           final Map<String, Set<String>> producerBeans,
-                           final Set<String> destructorBeans,
-                           final Map<String, Set<String>> pluginBeans) {
-        this.injectionBeans = injectionBeans;
-        this.producerBeans = producerBeans;
-        this.destructorBeans = destructorBeans;
-        this.pluginBeans = pluginBeans;
-    }
-
-    public Map<String, Set<String>> getInjectionBeans() {
-        return injectionBeans;
-    }
-
-    public Map<String, Set<String>> getProducerBeans() {
-        return producerBeans;
-    }
-
-    public Set<String> getDestructorBeans() {
-        return destructorBeans;
-    }
-
-    public Map<String, Set<String>> getPluginBeans() {
-        return pluginBeans;
-    }
-}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java
index d2f1386..de599a2 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/BeanProcessor.java
@@ -17,10 +17,12 @@
 
 package org.apache.logging.log4j.plugins.processor;
 
+import org.apache.logging.log4j.plugins.di.DependentScoped;
 import org.apache.logging.log4j.plugins.di.Disposes;
 import org.apache.logging.log4j.plugins.di.Inject;
 import org.apache.logging.log4j.plugins.di.Producer;
 import org.apache.logging.log4j.plugins.di.Qualifier;
+import org.apache.logging.log4j.plugins.di.ScopeType;
 
 import javax.annotation.processing.AbstractProcessor;
 import javax.annotation.processing.RoundEnvironment;
@@ -31,31 +33,36 @@ import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ElementKind;
 import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
 import javax.lang.model.element.PackageElement;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.ElementKindVisitor9;
 import javax.lang.model.util.Elements;
-import javax.lang.model.util.SimpleAnnotationValueVisitor9;
+import javax.lang.model.util.SimpleTypeVisitor9;
 import javax.lang.model.util.Types;
 import javax.tools.StandardLocation;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.UncheckedIOException;
-import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashSet;
-import java.util.Map;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
-import java.util.TreeSet;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 // TODO: migrate to separate maven module between log4j-plugins and log4j-core
 @SupportedAnnotationTypes({"org.apache.logging.log4j.plugins.*", "org.apache.logging.log4j.core.config.plugins.*"})
-@SupportedOptions("pluginPackage")
+@SupportedOptions({"pluginPackage", "pluginClassName"})
 public class BeanProcessor extends AbstractProcessor {
-    public static final String PLUGIN_MODULE_SERVICE_FILE = "META-INF/services/org.apache.logging.log4j.plugins.di.spi.PluginModule";
+    public static final String PLUGIN_MODULE_SERVICE_FILE = "META-INF/services/org.apache.logging.log4j.plugins.di.model.PluginModule";
 
     public BeanProcessor() {
     }
@@ -66,57 +73,57 @@ public class BeanProcessor extends AbstractProcessor {
     }
 
     private static class ProducerAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
-        private final Set<ExecutableElement> producerMethods = new HashSet<>();
-        private final Set<VariableElement> producerFields = new HashSet<>();
+        private final List<ProducerMethodMirror> producerMethods = new ArrayList<>();
+        private final List<ProducerFieldMirror> producerFields = new ArrayList<>();
 
         @Override
         public Void visitVariableAsField(final VariableElement e, final Void unused) {
-            producerFields.add(e);
+            producerFields.add(new ProducerFieldMirror(e));
             return null;
         }
 
         @Override
         public Void visitExecutableAsMethod(final ExecutableElement e, final Void unused) {
-            producerMethods.add(e);
+            producerMethods.add(new ProducerMethodMirror(e));
             return null;
         }
     }
 
     private static class DisposesAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
-        private final Set<ExecutableElement> disposesMethods = new HashSet<>();
+        private final List<DisposesMirror> disposesParameters = new ArrayList<>();
 
         @Override
         public Void visitVariableAsParameter(final VariableElement e, final Void unused) {
-            disposesMethods.add((ExecutableElement) e.getEnclosingElement());
+            disposesParameters.add(new DisposesMirror(e));
             return null;
         }
     }
 
     private static class InjectAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
-        private final Set<TypeElement> injectableClasses = new HashSet<>();
+        private final List<InjectionTargetMirror> injectableClasses = new ArrayList<>();
 
         @Override
         public Void visitVariableAsField(final VariableElement e, final Void unused) {
-            injectableClasses.add((TypeElement) e.getEnclosingElement());
+            injectableClasses.add(new InjectionTargetMirror(((TypeElement) e.getEnclosingElement())));
             return null;
         }
 
         @Override
         public Void visitExecutableAsConstructor(final ExecutableElement e, final Void unused) {
-            injectableClasses.add((TypeElement) e.getEnclosingElement());
+            injectableClasses.add(new InjectionTargetMirror((TypeElement) e.getEnclosingElement()));
             return null;
         }
 
         @Override
         public Void visitExecutableAsMethod(final ExecutableElement e, final Void unused) {
-            injectableClasses.add((TypeElement) e.getEnclosingElement());
+            injectableClasses.add(new InjectionTargetMirror((TypeElement) e.getEnclosingElement()));
             return null;
         }
     }
 
     private static class QualifiedAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
         private final Predicate<AnnotationMirror> isProducerAnnotation;
-        private final Set<TypeElement> injectableClasses = new HashSet<>();
+        private final List<InjectionTargetMirror> injectableClasses = new ArrayList<>();
 
         private QualifiedAnnotationVisitor(final Predicate<AnnotationMirror> isProducerAnnotation) {
             this.isProducerAnnotation = isProducerAnnotation;
@@ -125,7 +132,7 @@ public class BeanProcessor extends AbstractProcessor {
         @Override
         public Void visitVariableAsField(final VariableElement e, final Void unused) {
             if (e.getAnnotationMirrors().stream().noneMatch(isProducerAnnotation)) {
-                injectableClasses.add((TypeElement) e.getEnclosingElement());
+                injectableClasses.add(new InjectionTargetMirror((TypeElement) e.getEnclosingElement()));
             }
             return null;
         }
@@ -136,43 +143,182 @@ public class BeanProcessor extends AbstractProcessor {
             final TypeElement typeElement = (TypeElement) enclosingExecutable.getEnclosingElement();
             if (enclosingExecutable.getKind() == ElementKind.CONSTRUCTOR ||
                     enclosingExecutable.getAnnotationMirrors().stream().noneMatch(isProducerAnnotation)) {
-                injectableClasses.add(typeElement);
+                injectableClasses.add(new InjectionTargetMirror(typeElement));
             }
             return null;
         }
     }
 
     private static class PluginAnnotationVisitor extends ElementKindVisitor9<Void, Void> {
-        private final Map<String, Set<TypeElement>> pluginCategories = new HashMap<>();
+        private final List<GenericPluginMirror> plugins = new ArrayList<>();
 
         @Override
         public Void visitTypeAsClass(final TypeElement e, final Void unused) {
-            final AnnotationMirror pluginAnnotation = e.getAnnotationMirrors()
-                    .stream()
-                    .filter(ann -> ann.getAnnotationType().asElement().getSimpleName().contentEquals("Plugin"))
-                    .findAny()
-                    .orElseThrow();
-            final ExecutableElement categoryKey = pluginAnnotation.getElementValues()
-                    .keySet()
-                    .stream()
-                    .filter(element -> element.getSimpleName().contentEquals("category"))
-                    .findAny()
-                    .orElseThrow();
-
-            final String category = pluginAnnotation.getElementValues()
-                    .get(categoryKey)
-                    .accept(new SimpleAnnotationValueVisitor9<String, Void>() {
-                        @Override
-                        public String visitString(final String s, final Void unused1) {
-                            return s;
-                        }
-                    }, null);
-            pluginCategories.computeIfAbsent(category, ignored -> new HashSet<>()).add(e);
-
+            plugins.add(new GenericPluginMirror(e));
             return null;
         }
     }
 
+    private static class ScopeTypeVisitor extends ElementKindVisitor9<TypeElement, Types> {
+        protected ScopeTypeVisitor(final TypeElement defaultValue) {
+            super(defaultValue);
+        }
+
+        @Override
+        public TypeElement visitType(final TypeElement e, final Types types) {
+            for (final AnnotationMirror annotationMirror : e.getAnnotationMirrors()) {
+                final DeclaredType annotationType = annotationMirror.getAnnotationType();
+                if (annotationType.getAnnotation(ScopeType.class) != null) {
+                    return (TypeElement) annotationType.asElement();
+                }
+            }
+            return super.visitType(e, types);
+        }
+
+        @Override
+        public TypeElement visitVariableAsField(final VariableElement e, final Types types) {
+            return Stream.concat(e.getAnnotationMirrors().stream(), e.asType().getAnnotationMirrors().stream())
+                    .map(AnnotationMirror::getAnnotationType)
+                    .filter(type -> type.getAnnotation(ScopeType.class) != null)
+                    .findFirst()
+                    .map(type -> (TypeElement) type.asElement())
+                    .orElse(super.DEFAULT_VALUE);
+        }
+
+        @Override
+        public TypeElement visitExecutableAsMethod(final ExecutableElement e, final Types types) {
+            return Stream.concat(e.getAnnotationMirrors().stream(), e.getReturnType().getAnnotationMirrors().stream())
+                    .map(AnnotationMirror::getAnnotationType)
+                    .filter(type -> type.getAnnotation(ScopeType.class) != null)
+                    .findFirst()
+                    .map(type -> (TypeElement) type.asElement())
+                    .orElse(super.DEFAULT_VALUE);
+        }
+    }
+
+    interface PluginSourceMirror<E extends Element> {
+        E getElement();
+
+        TypeElement getDeclaringElement();
+
+        TypeMirror getType();
+    }
+
+    static class ProducerMethodMirror implements PluginSourceMirror<ExecutableElement> {
+        private final ExecutableElement element;
+
+        ProducerMethodMirror(final ExecutableElement element) {
+            this.element = element;
+        }
+
+        @Override
+        public ExecutableElement getElement() {
+            return element;
+        }
+
+        @Override
+        public TypeElement getDeclaringElement() {
+            return (TypeElement) element.getEnclosingElement();
+        }
+
+        @Override
+        public TypeMirror getType() {
+            return element.getReturnType();
+        }
+    }
+
+    static class ProducerFieldMirror implements PluginSourceMirror<VariableElement> {
+        private final VariableElement element;
+
+        ProducerFieldMirror(final VariableElement element) {
+            this.element = element;
+        }
+
+        @Override
+        public VariableElement getElement() {
+            return element;
+        }
+
+        @Override
+        public TypeElement getDeclaringElement() {
+            return (TypeElement) element.getEnclosingElement();
+        }
+
+        @Override
+        public TypeMirror getType() {
+            return element.asType();
+        }
+    }
+
+    static class InjectionTargetMirror implements PluginSourceMirror<TypeElement> {
+        private final TypeElement element;
+
+        InjectionTargetMirror(final TypeElement element) {
+            this.element = element;
+        }
+
+        @Override
+        public TypeElement getElement() {
+            return element;
+        }
+
+        @Override
+        public TypeElement getDeclaringElement() {
+            return element;
+        }
+
+        @Override
+        public TypeMirror getType() {
+            return element.asType();
+        }
+    }
+
+    static class DisposesMirror implements PluginSourceMirror<VariableElement> {
+        private final VariableElement element;
+
+        DisposesMirror(final VariableElement element) {
+            this.element = element;
+        }
+
+        @Override
+        public VariableElement getElement() {
+            return element;
+        }
+
+        @Override
+        public TypeElement getDeclaringElement() {
+            return (TypeElement) element.getEnclosingElement().getEnclosingElement();
+        }
+
+        @Override
+        public TypeMirror getType() {
+            return element.asType();
+        }
+    }
+
+    static class GenericPluginMirror implements PluginSourceMirror<TypeElement> {
+        private final TypeElement element;
+
+        GenericPluginMirror(final TypeElement element) {
+            this.element = element;
+        }
+
+        @Override
+        public TypeElement getElement() {
+            return element;
+        }
+
+        @Override
+        public TypeElement getDeclaringElement() {
+            return element;
+        }
+
+        @Override
+        public TypeMirror getType() {
+            return element.asType();
+        }
+    }
+
     @Override
     public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
         if (annotations.isEmpty()) {
@@ -212,49 +358,33 @@ public class BeanProcessor extends AbstractProcessor {
         roundEnv.getElementsAnnotatedWithAny(pluginAnnotations).forEach(pluginAnnotationVisitor::visit);
 
         final Set<PackageElement> packageElements = new HashSet<>();
+        final Set<TypeElement> declaringTypes = new HashSet<>();
 
         final Elements elements = processingEnv.getElementUtils();
-        final Map<String, Set<String>> producerBeans = new HashMap<>();
-        for (final ExecutableElement producerMethod : producesAnnotationVisitor.producerMethods) {
-            final TypeElement declaringType = (TypeElement) producerMethod.getEnclosingElement();
-            packageElements.add(elements.getPackageOf(declaringType));
-            final String declaringClassName = elements.getBinaryName(declaringType).toString();
-            final Set<String> classNameHierarchy = getClassNameHierarchy(producerMethod.getReturnType());
-            producerBeans.put(declaringClassName, classNameHierarchy);
-        }
-        for (final VariableElement producerField : producesAnnotationVisitor.producerFields) {
-            final TypeElement declaringType = (TypeElement) producerField.getEnclosingElement();
-            packageElements.add(elements.getPackageOf(declaringType));
-            final String declaringClassName = elements.getBinaryName(declaringType).toString();
-            final Set<String> classNameHierarchy = getClassNameHierarchy(producerField.asType());
-            producerBeans.put(declaringClassName, classNameHierarchy);
-        }
-
-        final Set<CharSequence> destructibleClassNames = disposesAnnotationVisitor.disposesMethods.stream()
-                .map(e -> (TypeElement) e.getEnclosingElement())
-                .peek(e -> packageElements.add(elements.getPackageOf(e)))
-                .map(elements::getBinaryName)
-                .collect(Collectors.toSet());
-
-        final Map<String, Set<String>> injectionBeans = new HashMap<>();
-        final Set<TypeElement> injectionClasses = new HashSet<>(injectAnnotationVisitor.injectableClasses);
-        injectionClasses.addAll(qualifiedAnnotationVisitor.injectableClasses);
-        for (final TypeElement injectableClass : injectionClasses) {
-            packageElements.add(elements.getPackageOf(injectableClass));
-            final String declaringClassName = elements.getBinaryName(injectableClass).toString();
-            final Set<String> classNameHierarchy = getClassNameHierarchy(injectableClass.asType());
-            injectionBeans.put(declaringClassName, classNameHierarchy);
-        }
-
-        final Map<String, Set<String>> pluginBeans = pluginAnnotationVisitor.pluginCategories
-                .values()
-                .stream()
-                .flatMap(Set::stream)
-                .peek(typeElement -> packageElements.add(elements.getPackageOf(typeElement)))
-                .collect(Collectors.toMap(
-                        typeElement -> elements.getBinaryName(typeElement).toString(),
-                        typeElement -> getClassNameHierarchy(typeElement.asType())
-                ));
+        final List<PluginSourceMirror<?>> mirrors = new ArrayList<>(producesAnnotationVisitor.producerMethods);
+        mirrors.addAll(producesAnnotationVisitor.producerFields);
+        mirrors.addAll(injectAnnotationVisitor.injectableClasses);
+        mirrors.addAll(disposesAnnotationVisitor.disposesParameters);
+        mirrors.forEach(mirror -> {
+            declaringTypes.add(mirror.getDeclaringElement());
+            packageElements.add(elements.getPackageOf(mirror.getDeclaringElement()));
+        });
+
+        qualifiedAnnotationVisitor.injectableClasses.stream()
+                .filter(mirror -> !declaringTypes.contains(mirror.getDeclaringElement()))
+                .forEach(mirror -> {
+                    mirrors.add(mirror);
+                    declaringTypes.add(mirror.getDeclaringElement());
+                    packageElements.add(elements.getPackageOf(mirror.getDeclaringElement()));
+                });
+
+        pluginAnnotationVisitor.plugins.stream()
+                .filter(mirror -> !declaringTypes.contains(mirror.getDeclaringElement()))
+                .forEach(mirror -> {
+                    mirrors.add(mirror);
+                    declaringTypes.add(mirror.getDeclaringElement());
+                    packageElements.add(elements.getPackageOf(mirror.getDeclaringElement()));
+                });
 
         String packageName = processingEnv.getOptions().get("pluginPackage");
         if (packageName == null) {
@@ -267,7 +397,7 @@ public class BeanProcessor extends AbstractProcessor {
         }
         String className = processingEnv.getOptions().getOrDefault("pluginClassName", "Log4jModule");
         try {
-            writePluginModule(packageName, className, injectionBeans, producerBeans, destructibleClassNames, pluginBeans);
+            writePluginModule(packageName, className, mirrors);
             return false;
         } catch (IOException e) {
             throw new UncheckedIOException(e);
@@ -275,10 +405,7 @@ public class BeanProcessor extends AbstractProcessor {
     }
 
     private void writePluginModule(final CharSequence packageName, final CharSequence className,
-                                   final Map<String, Set<String>> injectionBeans,
-                                   final Map<String, Set<String>> producerBeans,
-                                   final Set<CharSequence> destructorBeans,
-                                   final Map<String, Set<String>> pluginBeans) throws IOException {
+                                   final List<PluginSourceMirror<?>> mirrors) throws IOException {
         try (final PrintWriter out = new PrintWriter(processingEnv.getFiler().createResource(
                 StandardLocation.CLASS_OUTPUT, "", PLUGIN_MODULE_SERVICE_FILE).openWriter())) {
             out.println(packageName + ".plugins." + className);
@@ -287,75 +414,91 @@ public class BeanProcessor extends AbstractProcessor {
                 packageName + ".plugins." + className).openWriter())) {
             out.println("package " + packageName + ".plugins;");
             out.println();
-            out.println("import java.util.*;");
+            out.println("import org.apache.logging.log4j.plugins.di.model.*;");
             out.println();
-            out.println("import org.apache.logging.log4j.plugins.di.spi.PluginModule;");
+            out.println("import java.util.List;");
+            out.println("import java.util.Set;");
             out.println();
             out.println("@javax.annotation.processing.Generated(\"" + getClass().getName() + "\")");
             out.println("public class " + className + " extends PluginModule {");
             out.println();
-            out.println("  private static final Map<String, Set<String>> INJECT =");
-            out.println("    new TreeMap<>(Map.ofEntries(" + getEntries(injectionBeans) + "));");
-            out.println();
-            out.println("  private static final Map<String, Set<String>> PRODUCE =");
-            out.println("    new TreeMap<>(Map.ofEntries(" + getEntries(producerBeans) + "));");
-            out.println();
-            out.println("  private static final Set<String> DESTROY = new TreeSet<>(Set.of(" + destructorBeans.stream().sorted(CharSequence::compare).collect(Collectors.joining("\",\n\"", "\n\"", "\"\n")) + "));");
-            out.println();
-            out.println("  private static final Map<String, Set<String>> PLUGINS =");
-            out.println("    new TreeMap<>(Map.ofEntries(" + getEntries(pluginBeans) + "));");
+            out.println("  private static final List<PluginSource> PLUGINS = List.of(" + javaListOfPlugins(mirrors) + ");");
             out.println();
             out.println("  public " + className + "() {");
-            out.println("    super(INJECT, PRODUCE, DESTROY, PLUGINS);");
+            out.println("    super(PLUGINS);");
             out.println("  }");
             out.println();
             out.println("}");
         }
     }
 
-    private static String getEntries(final Map<String, Set<String>> beans) {
-        return beans.entrySet().stream()
-                .sorted(Map.Entry.comparingByKey())
-                .map(e -> "Map.entry(\"" + e.getKey() + "\", new TreeSet<>(Set.of(" + e.getValue().stream().sorted().collect(Collectors.joining("\", \"", "\"", "\"")) + ")))")
+    private String javaListOfPlugins(final List<PluginSourceMirror<?>> mirrors) {
+        final Elements elements = processingEnv.getElementUtils();
+        final Types types = processingEnv.getTypeUtils();
+        final var scopeTypeVisitor = new ScopeTypeVisitor(elements.getTypeElement(DependentScoped.class.getCanonicalName()));
+        return mirrors.stream()
+                .sorted(Comparator.<PluginSourceMirror<?>, String>comparing(m -> m.getClass().getName())
+                        .thenComparing(m -> elements.getBinaryName(m.getDeclaringElement()), CharSequence::compare))
+                .map(mirror -> {
+                    final String declaringClassName = '"' + elements.getBinaryName(mirror.getDeclaringElement()).toString() + '"';
+                    final String setOfImplementedInterfaces = javaSetOfImplementedInterfaces(mirror.getType());
+                    final String scopeTypeClassReference = mirror.getElement().accept(scopeTypeVisitor, types).getQualifiedName() + ".class";
+                    if (mirror instanceof ProducerMethodMirror) {
+                        return "new ProducerMethod(" + declaringClassName + ", \"" +
+                                mirror.getType().toString() + "\", \"" +
+                                mirror.getElement().getSimpleName() + "\", " +
+                                setOfImplementedInterfaces + ", " +
+                                scopeTypeClassReference + ")";
+                    } else if (mirror instanceof ProducerFieldMirror) {
+                        return "new ProducerField(" + declaringClassName + ", \"" +
+                                mirror.getElement().getSimpleName() + "\", " +
+                                setOfImplementedInterfaces + ", " +
+                                scopeTypeClassReference + ")";
+                    } else if (mirror instanceof InjectionTargetMirror) {
+                        return "new InjectionTarget(" + declaringClassName + ", " +
+                                setOfImplementedInterfaces + ", " +
+                                scopeTypeClassReference + ")";
+                    } else if (mirror instanceof DisposesMirror) {
+                        return "new DisposesMethod(" + declaringClassName + ", \"" +
+                                elements.getBinaryName((TypeElement) types.asElement(mirror.getElement().asType())) + "\")";
+                    } else if (mirror instanceof GenericPluginMirror) {
+                        return "new GenericPlugin(" + declaringClassName + ", " + setOfImplementedInterfaces + ")";
+                    } else {
+                        throw new UnsupportedOperationException(mirror.getClass().getName());
+                    }
+                })
                 .collect(Collectors.joining(",\n", "\n", "\n"));
     }
 
-    private Set<String> getClassNameHierarchy(final TypeMirror base) {
-        final Set<String> hierarchy = new TreeSet<>();
-        walkClassHierarchy(base, hierarchy);
-        return hierarchy;
+    private String javaSetOfImplementedInterfaces(final TypeMirror base) {
+        final Set<Name> implementedInterfaces = getImplementedInterfaces(base);
+        return implementedInterfaces.isEmpty() ? "Set.of()" : "Set.of(" +
+                implementedInterfaces.stream().map(name -> name + ".class").collect(Collectors.joining(", ")) +
+                ")";
     }
 
-    private void walkClassHierarchy(final TypeMirror base, final Set<String> encountered) {
+    private Set<Name> getImplementedInterfaces(final TypeMirror base) {
+        final Set<Name> implementedInterfaces = new LinkedHashSet<>();
         final Types types = processingEnv.getTypeUtils();
-        final Elements elements = processingEnv.getElementUtils();
-        final Element element = types.asElement(base);
-        if (element != null) {
-            element.accept(new ElementKindVisitor9<Void, Void>() {
-                @Override
-                public Void visitTypeAsClass(final TypeElement e, final Void unused) {
-                    if (!elements.getPackageOf(e).getQualifiedName().contentEquals("java.lang")) {
-                        encountered.add(elements.getBinaryName(e).toString());
-                    }
-                    return null;
+        base.accept(new SimpleTypeVisitor9<Void, Void>() {
+            @Override
+            public Void visitDeclared(final DeclaredType t, final Void unused) {
+                for (final TypeMirror directSupertype : types.directSupertypes(t)) {
+                    directSupertype.accept(this, null);
                 }
-
-                @Override
-                public Void visitTypeAsEnum(final TypeElement e, final Void unused) {
-                    encountered.add(elements.getBinaryName(e).toString());
-                    return null;
-                }
-
-                @Override
-                public Void visitTypeAsInterface(final TypeElement e, final Void unused) {
-                    encountered.add(elements.getBinaryName(e).toString());
-                    return null;
-                }
-            }, null);
-        }
-        for (final TypeMirror directSupertype : types.directSupertypes(base)) {
-            walkClassHierarchy(directSupertype, encountered);
-        }
+                t.asElement().accept(new ElementKindVisitor9<Void, Void>() {
+                    @Override
+                    public Void visitTypeAsInterface(final TypeElement e, final Void unused) {
+                        if (e.getModifiers().contains(Modifier.PUBLIC)) {
+                            implementedInterfaces.add(e.getQualifiedName());
+                        }
+                        return null;
+                    }
+                }, null);
+                return null;
+            }
+        }, null);
+        return implementedInterfaces;
     }
 
     private static CharSequence commonPrefix(final CharSequence str1, final CharSequence str2) {
diff --git a/log4j-plugins/src/main/java9/module-info.java b/log4j-plugins/src/main/java9/module-info.java
index 01e587d..daee9e0 100644
--- a/log4j-plugins/src/main/java9/module-info.java
+++ b/log4j-plugins/src/main/java9/module-info.java
@@ -18,7 +18,7 @@ module org.apache.logging.log4j.plugins {
     exports org.apache.logging.log4j.plugins;
     exports org.apache.logging.log4j.plugins.convert;
     exports org.apache.logging.log4j.plugins.di;
-    exports org.apache.logging.log4j.plugins.di.spi;
+    exports org.apache.logging.log4j.plugins.di.model;
     exports org.apache.logging.log4j.plugins.processor;
     exports org.apache.logging.log4j.plugins.util;
     exports org.apache.logging.log4j.plugins.validation;
@@ -34,12 +34,12 @@ module org.apache.logging.log4j.plugins {
 
     provides org.apache.logging.log4j.plugins.processor.PluginService with
             org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins;
-    provides org.apache.logging.log4j.plugins.di.spi.PluginModule with
+    provides org.apache.logging.log4j.plugins.di.model.PluginModule with
             org.apache.logging.log4j.plugins.convert.plugins.Log4jModule;
     provides javax.annotation.processing.Processor with
             org.apache.logging.log4j.plugins.processor.PluginProcessor,
             org.apache.logging.log4j.plugins.processor.BeanProcessor;
 
     uses org.apache.logging.log4j.plugins.processor.PluginService;
-//    uses org.apache.logging.log4j.plugins.di.spi.PluginModule;
+//    uses org.apache.logging.log4j.plugins.di.model.PluginModule;
 }
diff --git a/log4j-plugins/src/test/java-test9/module-info.java b/log4j-plugins/src/test/java-test9/module-info.java
index 3ac8a6a..1975b79 100644
--- a/log4j-plugins/src/test/java-test9/module-info.java
+++ b/log4j-plugins/src/test/java-test9/module-info.java
@@ -6,6 +6,6 @@ module org.apache.logging.log4j.plugins.test {
 
     provides org.apache.logging.log4j.plugins.processor.PluginService with
             org.apache.logging.log4j.plugins.test.validation.plugins.Log4jPlugins;
-    provides org.apache.logging.log4j.plugins.di.spi.PluginModule with
+    provides org.apache.logging.log4j.plugins.di.model.PluginModule with
             org.apache.logging.log4j.plugins.test.validation.plugins.Log4jModule;
 }
diff --git a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java
index 7afe944..b699b4a 100644
--- a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java
+++ b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/processor/BeanProcessorTest.java
@@ -17,7 +17,11 @@
 
 package org.apache.logging.log4j.plugins.processor;
 
-import org.apache.logging.log4j.plugins.di.spi.PluginModule;
+import org.apache.logging.log4j.plugins.di.model.DisposesMethod;
+import org.apache.logging.log4j.plugins.di.model.GenericPlugin;
+import org.apache.logging.log4j.plugins.di.model.InjectionTarget;
+import org.apache.logging.log4j.plugins.di.model.PluginModule;
+import org.apache.logging.log4j.plugins.di.model.ProducerField;
 import org.junit.jupiter.api.Test;
 
 import java.util.ServiceLoader;
@@ -30,12 +34,20 @@ class BeanProcessorTest {
         final PluginModule service = ServiceLoader.load(PluginModule.class, getClass().getClassLoader())
                 .findFirst()
                 .orElseThrow();
-        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ExampleBean"));
-        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ImplicitBean"));
-        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ImplicitMethodBean"));
-        assertTrue(service.getInjectionBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ProductionBean$Builder"));
-        assertTrue(service.getProducerBeans().containsKey("org.apache.logging.log4j.plugins.test.validation.ProductionBean"));
-        assertTrue(service.getDestructorBeans().contains("org.apache.logging.log4j.plugins.test.validation.ProductionBean"));
-        assertTrue(service.getPluginBeans().containsKey(FakePlugin.class.getName()));
+        final var plugins = service.getPluginSources();
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ExampleBean")));
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ImplicitBean")));
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ImplicitMethodBean")));
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof InjectionTarget &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean$Builder")));
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof ProducerField &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean")));
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof DisposesMethod &&
+                plugin.getDeclaringClassName().equals("org.apache.logging.log4j.plugins.test.validation.ProductionBean")));
+        assertTrue(plugins.stream().anyMatch(plugin -> plugin instanceof GenericPlugin &&
+                plugin.getDeclaringClassName().equals(FakePlugin.class.getName())));
     }
 }
\ No newline at end of file
diff --git a/log4j-plugins/src/test/java9/module-info.java b/log4j-plugins/src/test/java9/module-info.java
index 4195946..99357b5 100644
--- a/log4j-plugins/src/test/java9/module-info.java
+++ b/log4j-plugins/src/test/java9/module-info.java
@@ -2,7 +2,7 @@ open module org.apache.logging.log4j.plugins {
     exports org.apache.logging.log4j.plugins;
     exports org.apache.logging.log4j.plugins.convert;
     exports org.apache.logging.log4j.plugins.di;
-    exports org.apache.logging.log4j.plugins.di.spi;
+    exports org.apache.logging.log4j.plugins.di.model;
     exports org.apache.logging.log4j.plugins.name;
     exports org.apache.logging.log4j.plugins.processor;
     exports org.apache.logging.log4j.plugins.util;
@@ -25,12 +25,12 @@ open module org.apache.logging.log4j.plugins {
 
     provides org.apache.logging.log4j.plugins.processor.PluginService with
             org.apache.logging.log4j.plugins.convert.plugins.Log4jPlugins;
-    provides org.apache.logging.log4j.plugins.di.spi.PluginModule with
+    provides org.apache.logging.log4j.plugins.di.model.PluginModule with
             org.apache.logging.log4j.plugins.convert.plugins.Log4jModule;
     provides javax.annotation.processing.Processor with
             org.apache.logging.log4j.plugins.processor.PluginProcessor,
             org.apache.logging.log4j.plugins.processor.BeanProcessor;
 
     uses org.apache.logging.log4j.plugins.processor.PluginService;
-    uses org.apache.logging.log4j.plugins.di.spi.PluginModule;
+    uses org.apache.logging.log4j.plugins.di.model.PluginModule;
 }