diff --git a/application-graph/src/main/java/ru/tinkoff/kora/application/graph/ApplicationGraphDraw.java b/application-graph/src/main/java/ru/tinkoff/kora/application/graph/ApplicationGraphDraw.java index 7f945d7c0..34dcaeac9 100644 --- a/application-graph/src/main/java/ru/tinkoff/kora/application/graph/ApplicationGraphDraw.java +++ b/application-graph/src/main/java/ru/tinkoff/kora/application/graph/ApplicationGraphDraw.java @@ -2,9 +2,12 @@ import reactor.core.publisher.Mono; +import javax.annotation.Nullable; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.TreeMap; public class ApplicationGraphDraw { private final List> graphNodes = new ArrayList<>(); @@ -18,18 +21,18 @@ public Class getRoot() { return root; } - public Node addNode0(Class[] tags, Graph.Factory factory, Node... dependencies) { - return this.addNode0(tags, factory, List.of(), dependencies); + public Node addNode0(Type type, Class[] tags, Graph.Factory factory, Node... dependencies) { + return this.addNode0(type, tags, factory, List.of(), dependencies); } - public Node addNode0(Class[] tags, Graph.Factory factory, List>> interceptors, Node... dependencies) { + public Node addNode0(Type type, Class[] tags, Graph.Factory factory, List>> interceptors, Node... dependencies) { for (var dependency : dependencies) { if (dependency.index >= 0 && dependency.graphDraw != this) { throw new IllegalArgumentException("Dependency is from another graph"); } } - var node = new Node<>(this, this.graphNodes.size(), factory, List.of(dependencies), List.copyOf(interceptors), tags); + var node = new Node<>(this, this.graphNodes.size(), factory, type, List.of(dependencies), List.copyOf(interceptors), tags); this.graphNodes.add(node); for (var dependency : dependencies) { if (dependency.isValueOf()) { @@ -60,4 +63,97 @@ public int size() { return this.graphNodes.size(); } + @Nullable + public Node findNodeByType(Type type) { + for (var graphNode : this.graphNodes) { + if (graphNode.type().equals(type) && graphNode.tags().length == 0) { + return graphNode; + } + } + return null; + } + + public void replaceNode(Node node, Graph.Factory factory) { + this.graphNodes.set(node.index, new Node( + this, node.index, factory, node.type(), List.of(), List.of(), node.tags() + )); + for (var graphNode : graphNodes) { + graphNode.deleteDependentNode(node); + } + } + + public ApplicationGraphDraw copy() { + var draw = new ApplicationGraphDraw(this.root); + for (var node : this.graphNodes) { + class T { + static void addNode(ApplicationGraphDraw draw, Node node) { + var dependencies = new Node[node.getDependencyNodes().size()]; + for (int i = 0; i < dependencies.length; i++) { + var dependency = node.getDependencyNodes().get(i); + dependencies[i] = draw.graphNodes.get(dependency.index); + } + draw.addNode0(node.type(), node.tags(), node.factory, node.getInterceptors(), dependencies); + } + } + T.addNode(draw, node); + } + return draw; + } + + public ApplicationGraphDraw subgraph(Node... rootNodes) { + var seen = new TreeMap(); + var subgraph = new ApplicationGraphDraw(this.root); + var visitor = new Object() { + public Node accept(Node node) { + if (!seen.containsKey(node.index)) { + var dependencies = new ArrayList>(); + var interceptors = new ArrayList>>(); + for (var dependencyNode : node.getDependencyNodes()) { + dependencies.add(this.accept(dependencyNode)); + } + for (var interceptor : node.getInterceptors()) { + interceptors.add(this.accept(interceptor)); + } + Graph.Factory factory = graph -> node.factory.get(new Graph() { + @Override + public ApplicationGraphDraw draw() { + return subgraph; + } + + @Override + public Q get(Node node1) { + @SuppressWarnings("unchecked") + var realNode = (Node) subgraph.graphNodes.get(seen.get(node1.index)); + return graph.get(realNode); + } + + @Override + public ValueOf valueOf(Node node1) { + @SuppressWarnings("unchecked") + var realNode = (Node) subgraph.graphNodes.get(seen.get(node1.index)); + return graph.valueOf(realNode); + } + + @Override + public PromiseOf promiseOf(Node node1) { + @SuppressWarnings("unchecked") + var realNode = (Node) subgraph.graphNodes.get(seen.get(node1.index)); + return graph.promiseOf(realNode); + } + }); + var newNode = subgraph.addNode0(node.type(), node.tags(), factory, interceptors, dependencies.toArray(new Node[0])); + seen.put(node.index, newNode.index); + return newNode; + } + var index = seen.get(node.index); + @SuppressWarnings("unchecked") + var newNode = (Node) subgraph.graphNodes.get(index); + return newNode; + } + }; + for (var rootNode : rootNodes) { + visitor.accept(this.graphNodes.get(rootNode.index)); + } + return subgraph; + } } diff --git a/application-graph/src/main/java/ru/tinkoff/kora/application/graph/GraphImpl.java b/application-graph/src/main/java/ru/tinkoff/kora/application/graph/GraphImpl.java index ecb219342..f89ef6c11 100644 --- a/application-graph/src/main/java/ru/tinkoff/kora/application/graph/GraphImpl.java +++ b/application-graph/src/main/java/ru/tinkoff/kora/application/graph/GraphImpl.java @@ -17,9 +17,6 @@ import java.util.stream.Stream; final class GraphImpl implements RefreshableGraph, Lifecycle { - public static int EMPTY_OPTIONAL_INDEX = -1; - public static int EMPTY_NULLABLE_INDEX = -2; - private volatile AtomicReferenceArray objects; private final ApplicationGraphDraw draw; private final Logger log; @@ -32,18 +29,13 @@ final class GraphImpl implements RefreshableGraph, Lifecycle { this.objects = new AtomicReferenceArray<>(this.draw.size()); } + @Override public ApplicationGraphDraw draw() { return this.draw; } @Override public T get(Node node) { - if (node.index == EMPTY_OPTIONAL_INDEX) { - return (T) Optional.empty(); - } - if (node.index == EMPTY_NULLABLE_INDEX) { - return null; - } if (node.graphDraw != this.draw) { throw new IllegalArgumentException("Node is from another graph"); } @@ -57,7 +49,7 @@ public T get(Node node) { @Override public ValueOf valueOf(final Node node) { - if (node.index >= 0 && node.graphDraw != this.draw) { + if (node.graphDraw != this.draw) { throw new IllegalArgumentException("Node is from another graph"); } return new ValueOf<>() { @@ -244,10 +236,14 @@ private Mono release(AtomicReferenceArray objects, Mono[] r .switchIfEmpty(Mono.just(o)) .doOnEach(s -> { switch (s.getType()) { - case CANCEL -> this.log.trace("Intercepting release node {} of class {} with node {} of class {} cancelled", node.index, o.getClass(), interceptorNode.index, interceptor.getClass()); - case ON_SUBSCRIBE -> this.log.trace("Intercepting release node {} of class {} with node {} of class {}", node.index, o.getClass(), interceptorNode.index, interceptor.getClass()); - case ON_ERROR -> this.log.trace("Intercepting release node {} of class {} with node {} of class {} error", node.index, o.getClass(), interceptorNode.index, interceptor.getClass(), s.getThrowable()); - case ON_COMPLETE -> this.log.trace("Intercepting release node {} of class {} with node {} of class {} complete", node.index, o.getClass(), interceptorNode.index, interceptor.getClass()); + case CANCEL -> + this.log.trace("Intercepting release node {} of class {} with node {} of class {} cancelled", node.index, o.getClass(), interceptorNode.index, interceptor.getClass()); + case ON_SUBSCRIBE -> + this.log.trace("Intercepting release node {} of class {} with node {} of class {}", node.index, o.getClass(), interceptorNode.index, interceptor.getClass()); + case ON_ERROR -> + this.log.trace("Intercepting release node {} of class {} with node {} of class {} error", node.index, o.getClass(), interceptorNode.index, interceptor.getClass(), s.getThrowable()); + case ON_COMPLETE -> + this.log.trace("Intercepting release node {} of class {} with node {} of class {} complete", node.index, o.getClass(), interceptorNode.index, interceptor.getClass()); default -> {} } }) @@ -352,10 +348,14 @@ private void createNode(Node node, AtomicIntegerArray dependencies) { .switchIfEmpty(Mono.just(o)) .doOnEach(s -> { switch (s.getType()) { - case CANCEL -> this.rootGraph.log.trace("Intercepting init node {} of class {} with node {} of class {} cancelled", node.index, o.getClass(), interceptor.index, interceptorObject.getClass()); - case ON_SUBSCRIBE -> this.rootGraph.log.trace("Intercepting init node {} of class {} with node {} of class {}", node.index, o.getClass(), interceptor.index, interceptorObject.getClass()); - case ON_ERROR -> this.rootGraph.log.trace("Intercepting init node {} of class {} with node {} of class {} error", node.index, o.getClass(), interceptor.index, interceptorObject.getClass(), s.getThrowable()); - case ON_COMPLETE -> this.rootGraph.log.trace("Intercepting init node {} of class {} with node {} of class {} complete", node.index, o.getClass(), interceptor.index, interceptorObject.getClass()); + case CANCEL -> + this.rootGraph.log.trace("Intercepting init node {} of class {} with node {} of class {} cancelled", node.index, o.getClass(), interceptor.index, interceptorObject.getClass()); + case ON_SUBSCRIBE -> + this.rootGraph.log.trace("Intercepting init node {} of class {} with node {} of class {}", node.index, o.getClass(), interceptor.index, interceptorObject.getClass()); + case ON_ERROR -> + this.rootGraph.log.trace("Intercepting init node {} of class {} with node {} of class {} error", node.index, o.getClass(), interceptor.index, interceptorObject.getClass(), s.getThrowable()); + case ON_COMPLETE -> + this.rootGraph.log.trace("Intercepting init node {} of class {} with node {} of class {} complete", node.index, o.getClass(), interceptor.index, interceptorObject.getClass()); default -> {} } })); diff --git a/application-graph/src/main/java/ru/tinkoff/kora/application/graph/Node.java b/application-graph/src/main/java/ru/tinkoff/kora/application/graph/Node.java index 0cb33c5dd..e8c3dffa0 100644 --- a/application-graph/src/main/java/ru/tinkoff/kora/application/graph/Node.java +++ b/application-graph/src/main/java/ru/tinkoff/kora/application/graph/Node.java @@ -1,18 +1,16 @@ package ru.tinkoff.kora.application.graph; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Optional; - -import static ru.tinkoff.kora.application.graph.GraphImpl.EMPTY_NULLABLE_INDEX; -import static ru.tinkoff.kora.application.graph.GraphImpl.EMPTY_OPTIONAL_INDEX; public final class Node { final ApplicationGraphDraw graphDraw; final int index; final Graph.Factory factory; // leaks for the test purposes + private final Type type; private final Class[] tags; private final List> dependencyNodes; private final List>> interceptors; @@ -20,10 +18,11 @@ public final class Node { private final List> dependentNodes; private final boolean isValueOf; - Node(ApplicationGraphDraw graphDraw, int index, Graph.Factory factory, List> dependencyNodes, List>> interceptors, Class[] tags) { + Node(ApplicationGraphDraw graphDraw, int index, Graph.Factory factory, Type type, List> dependencyNodes, List>> interceptors, Class[] tags) { this.graphDraw = graphDraw; this.index = index; this.factory = factory; + this.type = type; this.dependencyNodes = List.copyOf(dependencyNodes); this.dependentNodes = new ArrayList<>(); this.interceptors = List.copyOf(interceptors); @@ -32,10 +31,11 @@ public final class Node { this.tags = tags; } - private Node(ApplicationGraphDraw graphDraw, int index, Graph.Factory factory, List> dependencyNodes, List>> interceptors, List> dependentNodes, List> intercepts, boolean isValueOf, Class[] tags) { + private Node(ApplicationGraphDraw graphDraw, int index, Graph.Factory factory, Type type, List> dependencyNodes, List>> interceptors, List> dependentNodes, List> intercepts, boolean isValueOf, Class[] tags) { this.graphDraw = graphDraw; this.index = index; this.factory = factory; + this.type = type; this.dependencyNodes = List.copyOf(dependencyNodes); this.interceptors = List.copyOf(interceptors); this.dependentNodes = dependentNodes; @@ -45,13 +45,17 @@ private Node(ApplicationGraphDraw graphDraw, int index, Graph.Factory factory } public Node valueOf() { - return new Node<>(this.graphDraw, this.index, this.factory, this.dependencyNodes, this.interceptors, this.dependentNodes, this.intercepts, true, this.tags); + return new Node<>(this.graphDraw, this.index, this.factory, this.type, this.dependencyNodes, this.interceptors, this.dependentNodes, this.intercepts, true, this.tags); } void addDependentNode(Node node) { this.dependentNodes.add(node); } + public void deleteDependentNode(Node node) { + this.dependentNodes.remove(node); + } + void intercepts(Node node) { this.intercepts.add(node); } @@ -76,16 +80,11 @@ boolean isValueOf() { return this.isValueOf; } - public Class[] getTags() { - return tags; - } - - public static Node> emptyOptional() { - return new Node<>(null, EMPTY_OPTIONAL_INDEX, g -> Optional.empty(), List.of(), List.of(), new Class[0]); + public Type type() { + return this.type; } - public static Node emptyNullable() { - return new Node<>(null, EMPTY_NULLABLE_INDEX, g -> null, List.of(), List.of(), new Class[0]); + public Class[] tags() { + return tags; } - } diff --git a/application-graph/src/test/java/ru/tinkoff/kora/application/graph/GraphTest.java b/application-graph/src/test/java/ru/tinkoff/kora/application/graph/GraphTest.java index ff487fb7e..11a171978 100644 --- a/application-graph/src/test/java/ru/tinkoff/kora/application/graph/GraphTest.java +++ b/application-graph/src/test/java/ru/tinkoff/kora/application/graph/GraphTest.java @@ -5,6 +5,7 @@ import ch.qos.logback.classic.Logger; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @@ -352,6 +353,35 @@ void graphRefreshCallsRefreshListeners() { assertThat(object2.refreshTime).isGreaterThan(beforeRefresh); } + @Test + void subgraphTest() { + var graph = ReferenceGraph.graph(); + var subgraphDraw = graph.draw.subgraph(graph.object4Node); + assertThat(subgraphDraw.getNodes()).hasSize(5); + var subgraph = subgraphDraw.init().block(); + } + + @Test + void replaceNodeTest() { + var graph = ReferenceGraph.graph(); + var draw = graph.draw.copy(); + var mock = Mockito.mock(TestObject.class); + Mockito.when(mock.init()).thenReturn(Mono.empty()); + Mockito.when(mock.release()).thenReturn(Mono.empty()); + + draw.replaceNode(graph.object2Node, g -> mock); + var newGraph = draw.init().block(); + + @SuppressWarnings("unchecked") + var object5Node = (Node) draw.getNodes().stream() + .filter(n -> n.factory == graph.object5Factory) + .findFirst() + .get(); + + Mockito.verify(mock).init(); + var o5 = newGraph.get(object5Node); + assertThat(o5.dependencies.get(0)).isSameAs(mock); + } /** *
@@ -369,21 +399,20 @@ void graphRefreshCallsRefreshListeners() {
     private static class ReferenceGraph {
         private final ApplicationGraphDraw draw = new ApplicationGraphDraw(ReferenceGraph.class);
         private final TestObjectFactory rootFactory = factory();
+        private final Node rootNode = draw.addNode0(TestObject.class, TAGS, rootFactory);
+        private final TestObjectFactory object1Factory = factory(rootNode);
+        private final Node object1Node = draw.addNode0(TestObject.class, TAGS, object1Factory, rootNode);
         private final TestObjectFactory interceptor1Factory = factory();
-        private final TestObjectFactory object1Factory = factory();
-        private final TestObjectFactory object2Factory = factory();
-        private final TestObjectFactory object3Factory = factory();
-        private final TestObjectFactory object5Factory = factory();
-        private final Node rootNode = draw.addNode0(TAGS, rootFactory);
-        private final Node interceptor1 = draw.addNode0(TAGS, interceptor1Factory);
-        private final Node object1Node = draw.addNode0(TAGS, object1Factory, rootNode);
-        private final Node object2Node = draw.addNode0(TAGS, object2Factory, List.of(interceptor1), rootNode);
-        private final Node object3Node = draw.addNode0(TAGS, object3Factory, object1Node);
+        private final Node interceptor1 = draw.addNode0(TestObject.class, TAGS, interceptor1Factory);
+        private final TestObjectFactory object2Factory = factory(rootNode);
+        private final Node object2Node = draw.addNode0(TestObject.class, TAGS, object2Factory, List.of(interceptor1), rootNode);
+        private final TestObjectFactory object3Factory = factory(object1Node);
+        private final Node object3Node = draw.addNode0(TestObject.class, TAGS, object3Factory, object1Node);
         private final TestObjectFactory object4Factory = factory(object1Node, object2Node.valueOf());
+        private final Node object4Node = draw.addNode0(TestObject.class, TAGS, object4Factory, object1Node, object2Node.valueOf());
+        private final TestObjectFactory object5Factory = factory(object2Node);
+        private final Node object5Node = draw.addNode0(TestObject.class, TAGS, object5Factory, object2Node);
 
-
-        private final Node object4Node = draw.addNode0(TAGS, object4Factory, object1Node, object2Node.valueOf());
-        private final Node object5Node = draw.addNode0(TAGS, object5Factory, object2Node);
         private final RefreshableGraph graph = this.draw.init().block();
 
         public static ReferenceGraph graph() {
diff --git a/kora-app-annotation-processor/src/main/java/ru/tinkoff/kora/kora/app/annotation/processor/KoraAppProcessor.java b/kora-app-annotation-processor/src/main/java/ru/tinkoff/kora/kora/app/annotation/processor/KoraAppProcessor.java
index 2cab515e3..690c26586 100644
--- a/kora-app-annotation-processor/src/main/java/ru/tinkoff/kora/kora/app/annotation/processor/KoraAppProcessor.java
+++ b/kora-app-annotation-processor/src/main/java/ru/tinkoff/kora/kora/app/annotation/processor/KoraAppProcessor.java
@@ -22,9 +22,9 @@
 import javax.lang.model.type.DeclaredType;
 import javax.tools.Diagnostic;
 import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
 import java.util.*;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -293,95 +293,66 @@ private void write(TypeElement type, ProcessingState.Ok ok) throws IOException {
 
 
     private JavaFile generateApplicationGraph(Element classElement, List allModules, ComponentInterceptors interceptors, List components) {
-        var graphDraw = this.elements.getTypeElement(CommonClassNames.applicationGraphDraw.canonicalName());
         var packageElement = (PackageElement) classElement.getEnclosingElement();
         var implClass = ClassName.get(packageElement.getQualifiedName().toString(), classElement.getSimpleName().toString() + "Impl");
+        var graphName = classElement.getSimpleName().toString() + "Graph";
+        var graphTypeName = ClassName.get(packageElement.getQualifiedName().toString(), graphName);
 
-        var classBuilder = TypeSpec.classBuilder(classElement.getSimpleName().toString() + "Graph")
+        var classBuilder = TypeSpec.classBuilder(graphName)
             .addAnnotation(AnnotationSpec.builder(CommonClassNames.koraGenerated).addMember("value", CodeBlock.of("$S", KoraAppProcessor.class.getCanonicalName())).build())
             .addOriginatingElement(classElement)
             .addModifiers(Modifier.PUBLIC)
-            .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Supplier.class), TypeName.get(graphDraw.asType())))
-            .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Function.class), implClass, TypeName.get(graphDraw.asType())))
+            .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Supplier.class), CommonClassNames.applicationGraphDraw))
+            .addField(CommonClassNames.applicationGraphDraw, "graphDraw", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
             .addMethod(MethodSpec
                 .methodBuilder("get")
                 .addAnnotation(Override.class)
                 .addModifiers(Modifier.PUBLIC)
                 .returns(CommonClassNames.applicationGraphDraw)
-                .addStatement("return $L.graph(new $T())", classElement.getSimpleName().toString() + "Graph", implClass)
-                .build())
-            .addMethod(MethodSpec
-                .methodBuilder("apply")
-                .addAnnotation(Override.class)
-                .addModifiers(Modifier.PUBLIC)
-                .addParameter(implClass, "impl")
-                .returns(CommonClassNames.applicationGraphDraw)
-                .addStatement("return $L.graph(impl)", classElement.getSimpleName().toString() + "Graph")
+                .addStatement("return graphDraw")
                 .build());
-
         for (var component : this.components) {
             classBuilder.addOriginatingElement(component);
         }
         for (var module : this.modules) {
             classBuilder.addOriginatingElement(module);
         }
-        var functionMethodBuilder = MethodSpec.methodBuilder("graph")
-            .addParameter(implClass, "impl")
-            .returns(CommonClassNames.applicationGraphDraw)
-            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
-            .addStatement("var graphDraw = new $T($T.class)", CommonClassNames.applicationGraphDraw, classElement);
-        var promisedComponents = new TreeSet();
+        var staticBlock = CodeBlock.builder()
+            .addStatement("var map = new $T<$T, $T>()", HashMap.class, String.class, Type.class)
+            .beginControlFlow("for (var field : $L.class.getDeclaredFields())", graphName)
+            .addStatement("if (!field.getName().startsWith($S)) continue", "component")
+            .addStatement("map.put(field.getName(), (($T) field.getGenericType()).getActualTypeArguments()[0])", ParameterizedType.class)
+            .endControlFlow();
         for (var component : components) {
-            for (var dependency : component.dependencies()) {
-                if (dependency instanceof ComponentDependency.PromiseOfDependency pod && pod.component() != null) {
-                    promisedComponents.add(pod.component().index());
-                }
-                if (dependency instanceof ComponentDependency.PromisedProxyParameterDependency pod) {
-                    var d = GraphResolutionHelper.findDependency(ctx, pod.declaration(), components, pod.claim());
-                    promisedComponents.add(d.component().index());
-                }
-                if (dependency instanceof ComponentDependency.AllOfDependency allOf && allOf.claim().claimType().equals(DependencyClaim.DependencyClaimType.ALL_OF_PROMISE)) {
-                    for (var d : GraphResolutionHelper.findDependenciesForAllOf(ctx, dependency.claim(), components)) {
-                        promisedComponents.add(d.component().index());
-                    }
-                }
-            }
-        }
-        for (var promisedComponent : promisedComponents) {
-            functionMethodBuilder.addStatement("var component$L = new $T<$T<$T>>()", promisedComponent, AtomicReference.class, CommonClassNames.node, components.get(promisedComponent).type());
+            classBuilder.addField(FieldSpec.builder(ParameterizedTypeName.get(CommonClassNames.node, TypeName.get(component.type()).box()), component.name(), Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL).build());
+            staticBlock.addStatement("var _type_of_$L = map.get($S)", component.name(), component.name());
         }
-        functionMethodBuilder.addCode("\n");
 
+        staticBlock
+            .addStatement("var impl = new $T()", implClass)
+            .addStatement("graphDraw = new $T($T.class)", CommonClassNames.applicationGraphDraw, classElement);
 
         for (var component : components) {
-            var statement = this.generateComponentStatement(allModules, interceptors, components, promisedComponents, component);
-
-            functionMethodBuilder.addStatement(statement);
+            var statement = this.generateComponentStatement(graphTypeName, allModules, interceptors, components, component);
+            staticBlock.addStatement(statement);
         }
-        functionMethodBuilder.addStatement("return graphDraw");
         var supplierMethodBuilder = MethodSpec.methodBuilder("graph")
             .returns(CommonClassNames.applicationGraphDraw)
             .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
-            .addStatement("var impl = new $L()", implClass)
-            .addStatement("return $L.graph(impl)", classElement.getSimpleName().toString() + "Graph");
+            .addStatement("return graphDraw", graphName);
 
 
         return JavaFile.builder(packageElement.getQualifiedName().toString(), classBuilder
-                .addMethod(functionMethodBuilder.build())
                 .addMethod(supplierMethodBuilder.build())
+                .addStaticBlock(staticBlock.build())
                 .build())
             .build();
     }
 
-    private CodeBlock generateComponentStatement(List allModules, ComponentInterceptors interceptors, List components, Set promisedComponents, ResolvedComponent component) {
+    private CodeBlock generateComponentStatement(ClassName graphTypeName, List allModules, ComponentInterceptors interceptors, List components, ResolvedComponent component) {
         var statement = CodeBlock.builder();
         var declaration = component.declaration();
-        var isPromised = promisedComponents.contains(component.index());
-        if (isPromised) {
-            statement.add("$L.set(graphDraw.addNode0(", component.name());
-        } else {
-            statement.add("var $L = graphDraw.addNode0(", component.name());
-        }
+        statement.add("$L = graphDraw.addNode0(_type_of_$L, ", component.name(), component.name());
         statement.add("new Class[]{");
         for (var tag : component.tags()) {
             statement.add("$L.class, ", tag);
@@ -454,7 +425,7 @@ private CodeBlock generateComponentStatement(List allModules, Compo
         for (int i = 0, dependenciesSize = resolvedDependencies.size(); i < dependenciesSize; i++) {
             if (i > 0) statement.add(",\n");
             var resolvedDependency = resolvedDependencies.get(i);
-            statement.add(resolvedDependency.write(this.ctx, components, promisedComponents));
+            statement.add(resolvedDependency.write(this.ctx, graphTypeName, components));
         }
         if (!resolvedDependencies.isEmpty()) {
             statement.unindent();
@@ -477,9 +448,6 @@ private CodeBlock generateComponentStatement(List allModules, Compo
                     var dependencies = GraphResolutionHelper.findDependenciesForAllOf(ctx, allOf.claim(), components);
                     for (var dependency : dependencies) {
                         statement.add(", $L", dependency.component().name());
-                        if (promisedComponents.contains(dependency.component().index())) {
-                            statement.add(".get()");
-                        }
                         if (allOf.claim().claimType() == DependencyClaim.DependencyClaimType.ALL_OF_VALUE) {
                             statement.add(".valueOf()");
                         }
@@ -493,18 +461,12 @@ private CodeBlock generateComponentStatement(List allModules, Compo
 
             if (resolvedDependency instanceof ComponentDependency.SingleDependency dependency && dependency.component() != null) {
                 statement.add(", $L", dependency.component().name());
-                if (promisedComponents.contains(dependency.component().index())) {
-                    statement.add(".get()");
-                }
                 if (resolvedDependency instanceof ComponentDependency.ValueOfDependency) {
                     statement.add(".valueOf()");
                 }
             }
         }
         statement.add(")");
-        if (isPromised) {
-            statement.add(")");
-        }
         return statement.build();
     }
 
@@ -515,7 +477,7 @@ private JavaFile generateImpl(TypeElement classElement, List module
         var classBuilder = TypeSpec.classBuilder(className)
             .addAnnotation(AnnotationSpec.builder(CommonClassNames.koraGenerated).addMember("value", CodeBlock.of("$S", KoraAppProcessor.class.getCanonicalName())).build())
             .addOriginatingElement(classElement)
-            .addModifiers(Modifier.PUBLIC)
+            .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
             .addSuperinterface(typeMirror);
 
         for (int i = 0; i < modules.size(); i++) {
diff --git a/kora-app-annotation-processor/src/main/java/ru/tinkoff/kora/kora/app/annotation/processor/component/ComponentDependency.java b/kora-app-annotation-processor/src/main/java/ru/tinkoff/kora/kora/app/annotation/processor/component/ComponentDependency.java
index 3bf5283e8..dce3f3710 100644
--- a/kora-app-annotation-processor/src/main/java/ru/tinkoff/kora/kora/app/annotation/processor/component/ComponentDependency.java
+++ b/kora-app-annotation-processor/src/main/java/ru/tinkoff/kora/kora/app/annotation/processor/component/ComponentDependency.java
@@ -1,5 +1,6 @@
 package ru.tinkoff.kora.kora.app.annotation.processor.component;
 
+import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import ru.tinkoff.kora.annotation.processor.common.CommonClassNames;
 import ru.tinkoff.kora.application.graph.TypeRef;
@@ -10,12 +11,11 @@
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.Types;
 import java.util.List;
-import java.util.Set;
 
 public sealed interface ComponentDependency {
     DependencyClaim claim();
 
-    CodeBlock write(ProcessingContext ctx, List resolvedComponents, Set promisedComponents);
+    CodeBlock write(ProcessingContext ctx, ClassName graphTypeName, List resolvedComponents);
 
     sealed interface SingleDependency extends ComponentDependency {
         ResolvedComponent component();
@@ -24,24 +24,22 @@ sealed interface SingleDependency extends ComponentDependency {
     record TargetDependency(DependencyClaim claim, ResolvedComponent component) implements SingleDependency {
 
         @Override
-        public CodeBlock write(ProcessingContext ctx, List resolvedComponents, Set promisedComponents) {
-            var node = promisedComponents.contains(component.index()) ? CodeBlock.of("$L.get()", this.component.name()) : CodeBlock.of("$L", this.component.name());
-            return CodeBlock.of("g.get($L)", node);
+        public CodeBlock write(ProcessingContext ctx, ClassName graphTypeName, List resolvedComponents) {
+            return CodeBlock.of("g.get($T.$L)", graphTypeName, this.component.name());
         }
     }
 
     record WrappedTargetDependency(DependencyClaim claim, ResolvedComponent component) implements SingleDependency {
 
         @Override
-        public CodeBlock write(ProcessingContext ctx, List resolvedComponents, Set promisedComponents) {
-            var node = promisedComponents.contains(component.index()) ? CodeBlock.of("$L.get()", this.component.name()) : CodeBlock.of("$L", this.component.name());
-            return CodeBlock.of("g.get($L).value()", node);
+        public CodeBlock write(ProcessingContext ctx, ClassName graphTypeName, List resolvedComponents) {
+            return CodeBlock.of("g.get($T.$L).value()", graphTypeName, this.component.name());
         }
     }
 
     record NullDependency(DependencyClaim claim) implements ComponentDependency {
         @Override
-        public CodeBlock write(ProcessingContext ctx, List resolvedComponents, Set promisedComponents) {
+        public CodeBlock write(ProcessingContext ctx, ClassName graphTypeName, List resolvedComponents) {
             return switch (this.claim.claimType()) {
                 case ONE_NULLABLE -> CodeBlock.of("($T) null", this.claim.type());
                 case NULLABLE_VALUE_OF -> CodeBlock.of("($T<$T>) null", CommonClassNames.valueOf, this.claim.type());
@@ -53,12 +51,11 @@ public CodeBlock write(ProcessingContext ctx, List resolvedCo
 
     record ValueOfDependency(DependencyClaim claim, SingleDependency delegate) implements SingleDependency {
         @Override
-        public CodeBlock write(ProcessingContext ctx, List resolvedComponents, Set promisedComponents) {
-            var node = promisedComponents.contains(delegate.component().index()) ? CodeBlock.of("$L.get()", delegate.component().name()) : CodeBlock.of("$L", delegate.component().name());
+        public CodeBlock write(ProcessingContext ctx, ClassName graphTypeName, List resolvedComponents) {
             if (this.delegate instanceof WrappedTargetDependency) {
-                return CodeBlock.of("g.valueOf($L).map($T::value).map(v -> ($T) v)", node, CommonClassNames.wrapped, claim.type());
+                return CodeBlock.of("g.valueOf($T.$L).map($T::value).map(v -> ($T) v)", graphTypeName, delegate.component().name(), CommonClassNames.wrapped, claim.type());
             }
-            return CodeBlock.of("g.valueOf($L).map(v -> ($T) v)", node, claim.type());
+            return CodeBlock.of("g.valueOf($T.$L).map(v -> ($T) v)", graphTypeName, delegate.component().name(), claim.type());
         }
 
         @Override
@@ -69,11 +66,11 @@ public ResolvedComponent component() {
 
     record PromiseOfDependency(DependencyClaim claim, SingleDependency delegate) implements SingleDependency {
         @Override
-        public CodeBlock write(ProcessingContext ctx, List resolvedComponents, Set promisedComponents) {
+        public CodeBlock write(ProcessingContext ctx, ClassName graphTypeName, List resolvedComponents) {
             if (this.delegate instanceof WrappedTargetDependency) {
-                return CodeBlock.of("g.promiseOf($L.get()).map($T::value).map(v -> ($T) v)", this.delegate.component().name(), CommonClassNames.wrapped, this.claim.type());
+                return CodeBlock.of("g.promiseOf($T.$L).map($T::value).map(v -> ($T) v)", graphTypeName, this.delegate.component().name(), CommonClassNames.wrapped, this.claim.type());
             }
-            return CodeBlock.of("g.promiseOf($L.get()).map(v -> ($T) v)", delegate.component().name(), this.claim.type());
+            return CodeBlock.of("g.promiseOf($T.$L).map(v -> ($T) v)", graphTypeName, delegate.component().name(), this.claim.type());
         }
 
         @Override
@@ -84,7 +81,7 @@ public ResolvedComponent component() {
 
     record TypeOfDependency(DependencyClaim claim) implements SingleDependency {
         @Override
-        public CodeBlock write(ProcessingContext ctx, List resolvedComponents, Set promisedComponents) {
+        public CodeBlock write(ProcessingContext ctx, ClassName graphTypeName, List resolvedComponents) {
             return this.buildTypeRef(ctx.types, this.claim.type());
         }
 
@@ -119,7 +116,7 @@ public ResolvedComponent component() {
 
     record AllOfDependency(DependencyClaim claim) implements ComponentDependency {
         @Override
-        public CodeBlock write(ProcessingContext ctx, List resolvedComponents, Set promisedComponents) {
+        public CodeBlock write(ProcessingContext ctx, ClassName graphTypeName, List resolvedComponents) {
             var codeBlock = CodeBlock.builder().add("$T.of(", CommonClassNames.all);
             var dependencies = GraphResolutionHelper.findDependenciesForAllOf(ctx, this.claim, resolvedComponents);
             for (int i = 0; i < dependencies.size(); i++) {
@@ -127,7 +124,7 @@ public CodeBlock write(ProcessingContext ctx, List resolvedCo
                 if (i == 0) {
                     codeBlock.indent().add("\n");
                 }
-                codeBlock.add(dependency.write(ctx, resolvedComponents, promisedComponents));
+                codeBlock.add(dependency.write(ctx, graphTypeName, resolvedComponents));
                 if (i == dependencies.size() - 1) {
                     codeBlock.unindent();
                 } else {
@@ -143,9 +140,9 @@ public CodeBlock write(ProcessingContext ctx, List resolvedCo
     record PromisedProxyParameterDependency(ru.tinkoff.kora.kora.app.annotation.processor.declaration.ComponentDeclaration declaration, DependencyClaim claim) implements ComponentDependency {
 
         @Override
-        public CodeBlock write(ProcessingContext ctx, List resolvedComponents, Set promisedComponents) {
+        public CodeBlock write(ProcessingContext ctx, ClassName graphTypeName, List resolvedComponents) {
             var dependencies = GraphResolutionHelper.findDependency(ctx, declaration, resolvedComponents, this.claim);
-            return CodeBlock.of("g.promiseOf($L.get())", dependencies.component().name());
+            return CodeBlock.of("g.promiseOf($T.$L)", graphTypeName, dependencies.component().name());
         }
     }
 }
diff --git a/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/DependencyTest.java b/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/DependencyTest.java
index 2600e4b13..6a6d8082b 100644
--- a/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/DependencyTest.java
+++ b/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/DependencyTest.java
@@ -1,5 +1,6 @@
 package ru.tinkoff.kora.kora.app.annotation.processor;
 
+import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import java.util.Locale;
@@ -227,4 +228,25 @@ final class TestClass1 {}
             "ru.tinkoff.kora.kora.app.annotation.processor.packageForDependencyTest.testDiscoveredFinalClassDependencyTaggedDependencyNoTagOnClass.ExampleApplication.TestClass1"
         );
     }
+
+    @Test
+    public void testRecursiveDependency() {
+        var draw = compile("""
+            @KoraApp
+            public interface ExampleApplication {
+                interface TestInterface1 {}
+                interface TestInterface2 {}
+                class TestClass2 implements TestInterface1, TestInterface2 {}
+                class TestClass1 implements MockLifecycle {}
+
+                default TestInterface1 testInterface1(TestInterface2 p) { return new TestClass2(); }
+                default TestInterface2 testInterface2(TestInterface1 p) { return new TestClass2(); }
+
+                default TestClass1 root(TestInterface1 testInterface1, TestInterface2 testInterface2) { return new TestClass1(); }
+            }
+            """);
+        Assertions.assertThat(draw.getNodes()).hasSize(4);
+        draw.init().block();
+    }
+
 }
diff --git a/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/KoraAppProcessorTest.java b/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/KoraAppProcessorTest.java
index cc4bd28e4..1120ac9ad 100644
--- a/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/KoraAppProcessorTest.java
+++ b/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/KoraAppProcessorTest.java
@@ -388,14 +388,6 @@ void appWithDefaultComponent() throws Throwable {
         var class1Node = class1Nodes.get(0);
         assertThat(graph.get(class1Node).value()).isEqualTo(2);
     }
-    @Test
-    void appWithCycleProxy() throws Throwable {
-        var graphDraw = testClass(AppWithCycleProxy.class);
-        Assertions.assertThat(graphDraw.getNodes()).hasSize(7);
-        var graph = graphDraw.init().block();
-        Assertions.assertThat(graph).isNotNull();
-
-    }
 
     @SuppressWarnings("unchecked")
      Node findNodeOf(ApplicationGraphDraw graphDraw, Class type, Class... tags) {
@@ -420,9 +412,9 @@  List> findNodesOf(ApplicationGraphDraw graphDraw, Class
                     return true;
                 }
                 if (nonTagged) {
-                    return node.getTags().length == 0;
+                    return node.tags().length == 0;
                 }
-                return Arrays.stream(tags).allMatch(tag -> Arrays.asList(node.getTags()).contains(tag));
+                return Arrays.stream(tags).allMatch(tag -> Arrays.asList(node.tags()).contains(tag));
             })
             .collect(Collectors.toList());
     }
diff --git a/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/NodeTypeTest.java b/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/NodeTypeTest.java
new file mode 100644
index 000000000..0ee60db70
--- /dev/null
+++ b/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/NodeTypeTest.java
@@ -0,0 +1,30 @@
+package ru.tinkoff.kora.kora.app.annotation.processor;
+
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.ParameterizedType;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class NodeTypeTest extends AbstractKoraAppTest {
+    @Test
+    public void testNodeType() {
+        var draw = compile("""
+            import java.util.HashMap;
+            import java.util.Map;
+            @KoraApp
+            public interface ExampleApplication {
+                class TestClass1 implements MockLifecycle {}
+
+                default Map testMap() { return new HashMap<>(); }
+                default TestClass1 root(Map p0) { return new TestClass1(); }
+            }
+            """);
+        assertThat(draw.getNodes()).hasSize(2);
+        var root = draw.init().block().get(draw.getNodes().get(1));
+        var nodeType = (ParameterizedType) draw.getNodes().get(0).type();
+        assertThat(nodeType.getRawType()).isEqualTo(Map.class);
+        assertThat(nodeType.getActualTypeArguments()).containsExactly(String.class, root.getClass());
+    }
+}
diff --git a/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/app/AppWithCycleProxy.java b/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/app/AppWithCycleProxy.java
deleted file mode 100644
index e01211a77..000000000
--- a/kora-app-annotation-processor/src/test/java/ru/tinkoff/kora/kora/app/annotation/processor/app/AppWithCycleProxy.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package ru.tinkoff.kora.kora.app.annotation.processor.app;
-
-import ru.tinkoff.kora.annotation.processor.common.MockLifecycle;
-import ru.tinkoff.kora.common.KoraApp;
-
-@KoraApp
-public interface AppWithCycleProxy {
-    default MockLifecycle someClass(JsonWriter w1, JsonWriter w3) {
-        return new MockLifecycle() {};
-    }
-
-    default Writer1 writer1(JsonWriter w) {
-        return new Writer1();
-    }
-
-    default Writer2 writer2(JsonWriter w) {
-        return new Writer2();
-    }
-
-    default Writer3 writer3(JsonWriter w) {
-        return new Writer3();
-    }
-
-    default Writer4 writer4(JsonWriter w) {
-        return new Writer4();
-    }
-
-    interface JsonWriter {
-        void write(T test);
-
-        String writeWithReturn(T test);
-    }
-
-    class Class1 {}
-
-    class Class2 {}
-
-    class Class3 {}
-
-    class Class4 {}
-
-    class Writer1 implements JsonWriter {
-        @Override
-        public void write(Class1 test) {
-
-        }
-
-        @Override
-        public String writeWithReturn(Class1 test) {
-            return "";
-        }
-    }
-
-    class Writer2 implements JsonWriter {
-        @Override
-        public void write(Class2 test) {
-
-        }
-
-        @Override
-        public String writeWithReturn(Class2 test) {
-            return "";
-        }
-    }
-
-    class Writer3 implements JsonWriter {
-        @Override
-        public void write(Class3 test) {
-
-        }
-
-        @Override
-        public String writeWithReturn(Class3 test) {
-            return "";
-        }
-    }
-
-    class Writer4 implements JsonWriter {
-        @Override
-        public void write(Class4 test) {
-
-        }
-
-        @Override
-        public String writeWithReturn(Class4 test) {
-            return "";
-        }
-    }
-
-
-}
diff --git a/kora-app-symbol-processor/src/main/kotlin/ru/tinkoff/kora/kora/app/ksp/KoraAppProcessor.kt b/kora-app-symbol-processor/src/main/kotlin/ru/tinkoff/kora/kora/app/ksp/KoraAppProcessor.kt
index 44e021b75..caee0ed2b 100644
--- a/kora-app-symbol-processor/src/main/kotlin/ru/tinkoff/kora/kora/app/ksp/KoraAppProcessor.kt
+++ b/kora-app-symbol-processor/src/main/kotlin/ru/tinkoff/kora/kora/app/ksp/KoraAppProcessor.kt
@@ -25,11 +25,14 @@ import ru.tinkoff.kora.ksp.common.*
 import ru.tinkoff.kora.ksp.common.AnnotationUtils.findAnnotation
 import ru.tinkoff.kora.ksp.common.AnnotationUtils.isAnnotationPresent
 import ru.tinkoff.kora.ksp.common.KspCommonUtils.generated
+import ru.tinkoff.kora.ksp.common.BaseSymbolProcessor
+import ru.tinkoff.kora.ksp.common.CommonClassNames
+import ru.tinkoff.kora.ksp.common.KotlinPoetUtils.controlFlow
 import ru.tinkoff.kora.ksp.common.exception.ProcessingErrorException
 import java.io.IOException
+import java.lang.reflect.ParameterizedType
+import java.lang.reflect.Type
 import java.util.*
-import java.util.concurrent.atomic.AtomicReference
-import java.util.function.Function
 import java.util.function.Supplier
 import javax.annotation.processing.SupportedOptions
 
@@ -425,8 +428,6 @@ class KoraAppProcessor(
         interceptors: ComponentInterceptors
     ): FileSpec {
         val supplier: KSClassDeclaration = resolver.getClassDeclarationByName(Supplier::class.qualifiedName.toString())!!
-        val functionDeclaration: KSClassDeclaration = resolver.getClassDeclarationByName(Function::class.qualifiedName.toString())!!
-
         val containingFile = declaration.containingFile!!
         val packageName = containingFile.packageName.asString()
         val graphName = "${declaration.simpleName.asString()}Graph"
@@ -438,24 +439,15 @@ class KoraAppProcessor(
 
         val implClass = ClassName(packageName, "${declaration.simpleName.asString()}Impl")
         val supplierSuperInterface = supplier.toClassName().parameterizedBy(CommonClassNames.applicationGraphDraw)
-        val functionSuperInterface = functionDeclaration.toClassName().parameterizedBy(implClass, CommonClassNames.applicationGraphDraw)
         val classBuilder = TypeSpec.classBuilder(graphName)
             .addOriginatingKSFile(containingFile)
             .generated(KoraAppProcessor::class)
             .addSuperinterface(supplierSuperInterface)
-            .addSuperinterface(functionSuperInterface)
             .addFunction(
                 FunSpec.builder("get")
                     .addModifiers(KModifier.OVERRIDE)
                     .returns(CommonClassNames.applicationGraphDraw)
-                    .addStatement("return %N.graph(%T())", "${declaration.simpleName.asString()}Graph", implClass)
-                    .build()
-            ).addFunction(
-                FunSpec.builder("apply")
-                    .addModifiers(KModifier.OVERRIDE)
-                    .addParameter("impl", implClass)
-                    .returns(CommonClassNames.applicationGraphDraw)
-                    .addStatement("return %N.graph(impl)", "${declaration.simpleName.asString()}Graph")
+                    .addStatement("return graphDraw")
                     .build()
             )
 
@@ -466,60 +458,31 @@ class KoraAppProcessor(
             classBuilder.addOriginatingKSFile(module.containingFile!!)
         }
         val companion = TypeSpec.companionObjectBuilder()
-        val functionMethodBuilder = FunSpec.builder("graph")
-            .addParameter("impl", implClass)
-            .returns(CommonClassNames.applicationGraphDraw)
-            .addStatement("val graphDraw =  %T(%T::class.java)", ApplicationGraphDraw::class, declaration.toClassName())
-        val promisedComponents = TreeSet()
-        for (component in graph) {
-            for (dependency in component.dependencies) {
-                if (dependency is ComponentDependency.PromiseOfDependency) {
-                    dependency.component?.let {
-                        promisedComponents.add(it.index)
-                    }
-                }
-                if (dependency is ComponentDependency.PromisedProxyParameterDependency) {
-                    val d = GraphResolutionHelper.findDependency(ctx!!, dependency.declaration, graph, dependency.claim)
-                    promisedComponents.add(d!!.component!!.index)
-                }
-                if (dependency is ComponentDependency.AllOfDependency && dependency.claim.claimType == DependencyClaim.DependencyClaimType.ALL_OF_PROMISE) {
-                    for (d in GraphResolutionHelper.findDependenciesForAllOf(ctx!!, dependency.claim, graph)) {
-                        promisedComponents.add(d.component!!.index)
-                    }
-                }
+            .addProperty("graphDraw", CommonClassNames.applicationGraphDraw)
+        val initBlock = CodeBlock.builder()
+            .addStatement("val self = %T", ClassName(packageName, graphName))
+            .addStatement("val map = %T<%T, %T>()", HashMap::class.asClassName(), String::class.asClassName(), Type::class.asClassName())
+            .controlFlow("for (field in %L::class.java.declaredFields)", graphName) {
+                addStatement("if (!field.name.startsWith(%S)) continue", "component")
+                addStatement("map[field.name] = (field.genericType as %T).actualTypeArguments[0]", ParameterizedType::class.asClassName())
             }
+            .addStatement("val impl = %T()", implClass)
+            .addStatement("graphDraw =  %T(%T::class.java)", ApplicationGraphDraw::class, declaration.toClassName())
+        for (component in graph) {
+            companion.addProperty(component.name, CommonClassNames.node.parameterizedBy(component.type.toTypeName()))
         }
-        for (promisedComponent in promisedComponents) {
-            functionMethodBuilder.addStatement(
-                "val component%L = %T<%T<%T>>()",
-                promisedComponent,
-                AtomicReference::class.asClassName(),
-                CommonClassNames.node,
-                graph[promisedComponent].type.toTypeName()
-            )
-        }
-        functionMethodBuilder.addCode("\n")
-
 
         for (component in graph) {
-            val statement = this.generateComponentStatement(allModules, interceptors, graph, promisedComponents, component)
-            functionMethodBuilder.addCode(statement)
+            val statement = this.generateComponentStatement(allModules, interceptors, graph, component)
+            initBlock.add(statement).add("\n")
         }
-        functionMethodBuilder.addStatement("\n")
-        functionMethodBuilder.addStatement("return graphDraw")
 
         val supplierMethodBuilder = FunSpec.builder("graph")
             .returns(ApplicationGraphDraw::class)
-            .addCode("\nval impl = %T()", implClass)
-            .addCode("\nreturn %N.graph(impl)\n", declaration.simpleName.asString() + "Graph")
+            .addCode("\nreturn graphDraw\n", declaration.simpleName.asString() + "Graph")
         return fileSpec.addType(
             classBuilder
-                .addType(
-                    companion
-                        .addFunction(supplierMethodBuilder.build())
-                        .addFunction(functionMethodBuilder.build())
-                        .build()
-                )
+                .addType(companion.addInitializerBlock(initBlock.build()).addFunction(supplierMethodBuilder.build()).build())
                 .addFunction(supplierMethodBuilder.build())
                 .build()
         ).build()
@@ -529,24 +492,18 @@ class KoraAppProcessor(
         allModules: List,
         interceptors: ComponentInterceptors,
         components: List,
-        promisedComponents: Set,
         component: ResolvedComponent
     ): CodeBlock {
         val statement = CodeBlock.builder()
         val declaration = component.declaration
-        val isPromised = promisedComponents.contains(component.index)
-        if (isPromised) {
-            statement.add("%L.set(graphDraw.addNode0(", component.name)
-        } else {
-            statement.add("val %L = graphDraw.addNode0(", component.name)
-        }
+        statement.add("%L = graphDraw.addNode0(map[%S], ", component.name, component.name)
         statement.indent().add("\n")
         statement.add("arrayOf(")
         for (tag in component.tags) {
             statement.add("%L::class.java, ", tag)
         }
         statement.add("),\n")
-        statement.add("{ g -> ")
+        statement.add("{ ")
 
         when (declaration) {
             is ComponentDeclaration.AnnotatedComponent -> {
@@ -618,7 +575,7 @@ class KoraAppProcessor(
             if (i > 0) {
                 statement.add(",\n")
             }
-            statement.add(dependency.write(ctx!!, components, promisedComponents))
+            statement.add(dependency.write(ctx!!, components))
         }
         if (component.dependencies.isNotEmpty()) {
             statement.unindent().add("\n")
@@ -646,9 +603,6 @@ class KoraAppProcessor(
                             statement.add(", ")
                         }
                         statement.add("%L", d.component!!.name)
-                        if (promisedComponents.contains(d.component!!.index)) {
-                            statement.add(".get()")
-                        }
                         if (dependency.claim.claimType == DependencyClaim.DependencyClaimType.ALL_OF_VALUE) {
                             statement.add(".valueOf()")
                         }
@@ -670,9 +624,6 @@ class KoraAppProcessor(
                     statement.add(", ")
                 }
                 statement.add("%L", dependency.component!!.name)
-                if (promisedComponents.contains(dependency.component!!.index)) {
-                    statement.add(".get()")
-                }
                 if (dependency is ComponentDependency.ValueOfDependency) {
                     statement.add(".valueOf()")
                 }
@@ -680,9 +631,6 @@ class KoraAppProcessor(
         }
         statement.unindent()
         statement.add("\n)")
-        if (isPromised) {
-            statement.add(")")
-        }
         return statement.add("\n").build()
     }
 }
diff --git a/kora-app-symbol-processor/src/main/kotlin/ru/tinkoff/kora/kora/app/ksp/component/ComponentDependency.kt b/kora-app-symbol-processor/src/main/kotlin/ru/tinkoff/kora/kora/app/ksp/component/ComponentDependency.kt
index b65341928..6755410e4 100644
--- a/kora-app-symbol-processor/src/main/kotlin/ru/tinkoff/kora/kora/app/ksp/component/ComponentDependency.kt
+++ b/kora-app-symbol-processor/src/main/kotlin/ru/tinkoff/kora/kora/app/ksp/component/ComponentDependency.kt
@@ -17,36 +17,28 @@ import ru.tinkoff.kora.ksp.common.CommonClassNames
 sealed interface ComponentDependency {
     val claim: DependencyClaim
 
-    fun write(ctx: ProcessingContext, resolvedComponents: List, promisedComponents: Set): CodeBlock
+    fun write(ctx: ProcessingContext, resolvedComponents: List): CodeBlock
 
     sealed interface SingleDependency : ComponentDependency {
         val component: ResolvedComponent?
     }
 
     data class TargetDependency(override val claim: DependencyClaim, override val component: ResolvedComponent) : SingleDependency {
-        override fun write(ctx: ProcessingContext, resolvedComponents: List, promisedComponents: Set): CodeBlock {
-            if (promisedComponents.contains(component.index)) {
-                return CodeBlock.of("g.get(%L.get())", component.name)
-            } else {
-                return CodeBlock.of("g.get(%L)", component.name)
-            }
+        override fun write(ctx: ProcessingContext, resolvedComponents: List): CodeBlock {
+            return CodeBlock.of("it.get(%L)", component.name)
         }
     }
 
     data class WrappedTargetDependency(override val claim: DependencyClaim, override val component: ResolvedComponent) : SingleDependency {
-        override fun write(ctx: ProcessingContext, resolvedComponents: List, promisedComponents: Set): CodeBlock {
-            if (promisedComponents.contains(component.index)) {
-                return CodeBlock.of("g.get(%L.get()).value()", component.name)
-            } else {
-                return CodeBlock.of("g.get(%L).value()", component.name)
-            }
+        override fun write(ctx: ProcessingContext, resolvedComponents: List): CodeBlock {
+            return CodeBlock.of("it.get(%L).value()", component.name)
         }
     }
 
     data class NullDependency(override val claim: DependencyClaim) : SingleDependency {
         override val component = null
 
-        override fun write(ctx: ProcessingContext, resolvedComponents: List, promisedComponents: Set): CodeBlock {
+        override fun write(ctx: ProcessingContext, resolvedComponents: List): CodeBlock {
             return when (claim.claimType) {
                 DependencyClaim.DependencyClaimType.NULLABLE_ONE -> CodeBlock.of("null as %T", claim.type.toTypeName().copy(true))
                 DependencyClaim.DependencyClaimType.NULLABLE_VALUE_OF -> CodeBlock.of("null as %T", CommonClassNames.valueOf.parameterizedBy(claim.type.toTypeName()).copy(true))
@@ -61,16 +53,15 @@ sealed interface ComponentDependency {
         override val component
             get() = delegate.component
 
-        override fun write(ctx: ProcessingContext, resolvedComponents: List, promisedComponents: Set): CodeBlock {
+        override fun write(ctx: ProcessingContext, resolvedComponents: List): CodeBlock {
             if (delegate is NullDependency) {
                 return CodeBlock.of("%T.valueOfNull()", CommonClassNames.valueOf)
             }
             val component = delegate.component!!
-            val node = if (promisedComponents.contains(component.index)) CodeBlock.of("%L.get()", component.name) else CodeBlock.of("%L", component.name)
             if (delegate is WrappedTargetDependency) {
-                return CodeBlock.of("g.valueOf(%L).map { it.value() }.map { it as %T }", node, claim.type.toTypeName())
+                return CodeBlock.of("it.valueOf(%L).map { it.value() }.map { it as %T }", component.name, claim.type.toTypeName())
             }
-            return CodeBlock.of("g.valueOf(%L).map { it as %T }", node, claim.type.toTypeName())
+            return CodeBlock.of("it.valueOf(%L).map { it as %T }", component.name, claim.type.toTypeName())
         }
     }
 
@@ -78,21 +69,20 @@ sealed interface ComponentDependency {
         override val component
             get() = delegate.component
 
-        override fun write(ctx: ProcessingContext, resolvedComponents: List, promisedComponents: Set): CodeBlock {
+        override fun write(ctx: ProcessingContext, resolvedComponents: List): CodeBlock {
             if (delegate is NullDependency) {
                 return CodeBlock.of("%T.promiseOfNull()", CommonClassNames.promiseOf)
             }
             val component = delegate.component!!
-            val node = if (promisedComponents.contains(component.index)) CodeBlock.of("%L.get()", component.name) else CodeBlock.of("%L", component.name)
             if (delegate is WrappedTargetDependency) {
-                return CodeBlock.of("g.promiseOf(%L).map { it.value() }.map { it as %T }", node, claim.type.toTypeName())
+                return CodeBlock.of("it.promiseOf(%L).map { it.value() }.map { it as %T }", component.name, claim.type.toTypeName())
             }
-            return CodeBlock.of("g.promiseOf(%L).map { it as %T }", node, claim.type.toTypeName())
+            return CodeBlock.of("it.promiseOf(%L).map { it as %T }", component.name, claim.type.toTypeName())
         }
     }
 
     data class TypeOfDependency(override val claim: DependencyClaim) : ComponentDependency {
-        override fun write(ctx: ProcessingContext, resolvedComponents: List, promisedComponents: Set): CodeBlock {
+        override fun write(ctx: ProcessingContext, resolvedComponents: List): CodeBlock {
             return buildTypeRef(claim.type)
         }
 
@@ -123,14 +113,14 @@ sealed interface ComponentDependency {
     }
 
     data class AllOfDependency(override val claim: DependencyClaim) : ComponentDependency {
-        override fun write(ctx: ProcessingContext, resolvedComponents: List, promisedComponents: Set): CodeBlock {
+        override fun write(ctx: ProcessingContext, resolvedComponents: List): CodeBlock {
             val codeBlock = CodeBlock.builder().add("%T.of(", CommonClassNames.all)
             val dependencies = GraphResolutionHelper.findDependenciesForAllOf(ctx, claim, resolvedComponents)
             for ((i, dependency) in dependencies.withIndex()) {
                 if (i == 0) {
                     codeBlock.indent().add("\n")
                 }
-                codeBlock.add(dependency.write(ctx, resolvedComponents, promisedComponents))
+                codeBlock.add(dependency.write(ctx, resolvedComponents))
                 if (i == dependencies.size - 1) {
                     codeBlock.unindent()
                 } else {
@@ -143,9 +133,9 @@ sealed interface ComponentDependency {
     }
 
     data class PromisedProxyParameterDependency(val declaration: ComponentDeclaration, override val claim: DependencyClaim) : ComponentDependency {
-        override fun write(ctx: ProcessingContext, resolvedComponents: List, promisedComponents: Set): CodeBlock {
+        override fun write(ctx: ProcessingContext, resolvedComponents: List): CodeBlock {
             val dependencies = GraphResolutionHelper.findDependency(ctx, declaration, resolvedComponents, this.claim)
-            return CodeBlock.of("g.promiseOf(%L.get())", dependencies!!.component!!.name)
+            return CodeBlock.of("it.promiseOf(self.%L)", dependencies!!.component!!.name)
         }
 
     }
diff --git a/kora-app-symbol-processor/src/test/kotlin/ru/tinkoff/kora/kora/app/ksp/KoraAppKspTest.kt b/kora-app-symbol-processor/src/test/kotlin/ru/tinkoff/kora/kora/app/ksp/KoraAppKspTest.kt
index 9e07c885f..919313a8a 100644
--- a/kora-app-symbol-processor/src/test/kotlin/ru/tinkoff/kora/kora/app/ksp/KoraAppKspTest.kt
+++ b/kora-app-symbol-processor/src/test/kotlin/ru/tinkoff/kora/kora/app/ksp/KoraAppKspTest.kt
@@ -449,9 +449,9 @@ class KoraAppKspTest {
                     return@filter true
                 }
                 if (nonTagged) {
-                    return@filter node.tags.isEmpty()
+                    return@filter node.tags().isEmpty()
                 }
-                return@filter tags.all { listOf(*node.tags).contains(it) }
+                return@filter tags.all { listOf(*node.tags()).contains(it) }
             }.map { it as Node }
             .toList()
     }