├── src
└── main
│ └── java
│ ├── com
│ └── acme
│ │ ├── Foo.java
│ │ ├── Bar.java
│ │ ├── SampleApplication.java
│ │ └── SampleConfiguration.java
│ └── plugin
│ └── StaticConfigutationInitializer.java
└── pom.xml
/src/main/java/com/acme/Foo.java:
--------------------------------------------------------------------------------
1 | package com.acme;
2 |
3 | public class Foo {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/acme/Bar.java:
--------------------------------------------------------------------------------
1 | package com.acme;
2 |
3 | public class Bar {
4 |
5 | private final Foo foo;
6 |
7 | public Bar(Foo foo) {
8 | this.foo = foo;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/acme/SampleApplication.java:
--------------------------------------------------------------------------------
1 | package com.acme;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class SampleApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(SampleApplication.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/acme/SampleConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.acme;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | @Configuration
7 | public class SampleConfiguration {
8 |
9 | @Bean
10 | public Foo foo() {
11 | return new Foo();
12 | }
13 |
14 | @Bean
15 | public Bar bar(Foo foo) {
16 | return new Bar(foo);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | spring-init-experiment
8 | spring-init-experiment
9 | 1.0-SNAPSHOT
10 |
11 |
12 | org.springframework.boot
13 | spring-boot-starter-parent
14 | 2.0.4.RELEASE
15 |
16 |
17 |
18 |
19 | org.springframework.boot
20 | spring-boot-starter-web
21 |
22 |
23 | org.springframework
24 | spring-context
25 |
26 |
27 | net.bytebuddy
28 | byte-buddy
29 |
30 |
31 |
32 |
33 | 1.8
34 | 1.8.17
35 |
36 |
37 |
38 |
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-maven-plugin
43 |
44 |
45 | net.bytebuddy
46 | byte-buddy-maven-plugin
47 | ${byte-buddy.version}
48 |
49 |
50 |
51 | transform
52 |
53 |
54 |
55 |
56 |
57 |
58 | plugin.StaticConfigutationInitializer
59 |
60 |
61 |
62 |
63 |
64 | org.codehaus.mojo
65 | exec-maven-plugin
66 | 1.6.0
67 |
68 |
69 |
70 | java
71 |
72 |
73 |
74 |
75 | com.acme.SampleApplication
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/src/main/java/plugin/StaticConfigutationInitializer.java:
--------------------------------------------------------------------------------
1 | package plugin;
2 |
3 | import com.acme.SampleConfiguration;
4 | import net.bytebuddy.ByteBuddy;
5 | import net.bytebuddy.build.Plugin;
6 | import net.bytebuddy.description.method.MethodDescription;
7 | import net.bytebuddy.description.method.ParameterDescription;
8 | import net.bytebuddy.description.modifier.Ownership;
9 | import net.bytebuddy.description.modifier.Visibility;
10 | import net.bytebuddy.description.type.TypeDescription;
11 | import net.bytebuddy.dynamic.DynamicType;
12 | import net.bytebuddy.implementation.Implementation;
13 | import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
14 | import net.bytebuddy.implementation.bytecode.StackManipulation;
15 | import net.bytebuddy.implementation.bytecode.assign.TypeCasting;
16 | import net.bytebuddy.implementation.bytecode.collection.ArrayFactory;
17 | import net.bytebuddy.implementation.bytecode.constant.ClassConstant;
18 | import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
19 | import net.bytebuddy.implementation.bytecode.member.MethodReturn;
20 | import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
21 | import net.bytebuddy.utility.CompoundList;
22 | import net.bytebuddy.utility.JavaConstant;
23 | import org.springframework.beans.factory.BeanFactory;
24 | import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
25 | import org.springframework.context.ApplicationContextInitializer;
26 | import org.springframework.context.annotation.Bean;
27 | import org.springframework.context.annotation.Configuration;
28 | import org.springframework.context.support.GenericApplicationContext;
29 |
30 | import java.lang.invoke.LambdaMetafactory;
31 | import java.lang.invoke.MethodHandle;
32 | import java.lang.invoke.MethodHandles;
33 | import java.lang.invoke.MethodType;
34 | import java.lang.reflect.Modifier;
35 | import java.util.ArrayList;
36 | import java.util.Arrays;
37 | import java.util.Collections;
38 | import java.util.List;
39 | import java.util.function.Supplier;
40 |
41 | import static net.bytebuddy.matcher.ElementMatchers.*;
42 |
43 | public class StaticConfigutationInitializer implements Plugin {
44 |
45 | private final MethodDescription.InDefinedShape registerBean, registerBeanWithSupplier, getBean, lambdaMeta, get;
46 |
47 | public StaticConfigutationInitializer() {
48 | try {
49 | registerBean = new MethodDescription.ForLoadedMethod(GenericApplicationContext.class.getMethod("registerBean",
50 | Class.class, BeanDefinitionCustomizer[].class));
51 | registerBeanWithSupplier = new MethodDescription.ForLoadedMethod(GenericApplicationContext.class.getMethod("registerBean",
52 | Class.class, Supplier.class, BeanDefinitionCustomizer[].class));
53 | getBean = new MethodDescription.ForLoadedMethod(BeanFactory.class.getMethod("getBean", Class.class));
54 | lambdaMeta = new MethodDescription.ForLoadedMethod(LambdaMetafactory.class.getMethod("metafactory",
55 | MethodHandles.Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class));
56 | get = new MethodDescription.ForLoadedMethod(Supplier.class.getMethod("get"));
57 | } catch (NoSuchMethodException e) {
58 | throw new RuntimeException(e);
59 | }
60 | }
61 |
62 | @Override
63 | public DynamicType.Builder> apply(DynamicType.Builder> builder, TypeDescription typeDescription) {
64 | List initializers = new ArrayList<>();
65 | for (MethodDescription.InDefinedShape methodDescription : typeDescription.getDeclaredMethods().filter(isAnnotatedWith(Bean.class))) {
66 | List stackManipulations = new ArrayList<>();
67 | for (TypeDescription argumentType : methodDescription.isStatic()
68 | ? methodDescription.getParameters().asTypeList().asErasures()
69 | : CompoundList.of(typeDescription, methodDescription.getParameters().asTypeList().asErasures())) {
70 | stackManipulations.add(MethodVariableAccess.REFERENCE.loadFrom(0));
71 | stackManipulations.add(ClassConstant.of(argumentType));
72 | stackManipulations.add(MethodInvocation.invoke(getBean));
73 | stackManipulations.add(TypeCasting.to(argumentType));
74 | }
75 | stackManipulations.add(MethodInvocation.invoke(methodDescription));
76 | stackManipulations.add(MethodReturn.of(methodDescription.getReturnType()));
77 | builder = builder.defineMethod("init_" + methodDescription.getName(), methodDescription.getReturnType().asErasure(), Visibility.PRIVATE, Ownership.STATIC)
78 | .withParameters(BeanFactory.class)
79 | .intercept(new Implementation.Simple(new ByteCodeAppender.Simple(stackManipulations)));
80 | initializers.add(MethodVariableAccess.REFERENCE.loadFrom(1));
81 | initializers.add(TypeCasting.to(new TypeDescription.ForLoadedType(GenericApplicationContext.class)));
82 | initializers.add(ClassConstant.of(methodDescription.getReturnType().asErasure()));
83 | initializers.add(MethodVariableAccess.REFERENCE.loadFrom(1));
84 | initializers.add(TypeCasting.to(new TypeDescription.ForLoadedType(GenericApplicationContext.class)));
85 | MethodDescription.InDefinedShape lambda = new MethodDescription.Latent(typeDescription,
86 | "init_" + methodDescription.getName(),
87 | Modifier.PRIVATE | Modifier.STATIC,
88 | Collections.emptyList(),
89 | methodDescription.getReturnType().asRawType(),
90 | Collections.singletonList(new ParameterDescription.Token(new TypeDescription.ForLoadedType(BeanFactory.class).asGenericType())),
91 | Collections.emptyList(),
92 | Collections.emptyList(),
93 | null,
94 | null);
95 | initializers.add(MethodInvocation.invoke(lambdaMeta).dynamic("get",
96 | new TypeDescription.ForLoadedType(Supplier.class),
97 | Collections.singletonList(new TypeDescription.ForLoadedType(GenericApplicationContext.class)),
98 | Arrays.asList(
99 | JavaConstant.MethodType.of(get).asConstantPoolValue(),
100 | JavaConstant.MethodHandle.of(lambda).asConstantPoolValue(),
101 | JavaConstant.MethodType.of(methodDescription.getReturnType().asErasure(), Collections.emptyList()).asConstantPoolValue()
102 | )
103 | ));
104 | initializers.add(ArrayFactory.forType(new TypeDescription.ForLoadedType(BeanDefinitionCustomizer.class).asGenericType()).withValues(Collections.emptyList()));
105 | initializers.add(MethodInvocation.invoke(registerBeanWithSupplier));
106 | }
107 | initializers.add(0, MethodVariableAccess.REFERENCE.loadFrom(1));
108 | initializers.add(1, TypeCasting.to(new TypeDescription.ForLoadedType(GenericApplicationContext.class)));
109 | initializers.add(2, ClassConstant.of(typeDescription));
110 | initializers.add(3, ArrayFactory.forType(new TypeDescription.ForLoadedType(BeanDefinitionCustomizer.class).asGenericType()).withValues(Collections.emptyList()));
111 | initializers.add(4, MethodInvocation.invoke(registerBean));
112 | initializers.add(MethodReturn.VOID);
113 | return builder.implement(ApplicationContextInitializer.class)
114 | .method(named("initialize").and(isDeclaredBy(ApplicationContextInitializer.class)))
115 | .intercept(new Implementation.Simple(new ByteCodeAppender.Simple(initializers)));
116 | }
117 |
118 | @Override
119 | public boolean matches(TypeDescription target) {
120 | return target.getDeclaredAnnotations().isAnnotationPresent(Configuration.class);
121 | }
122 |
123 | public static void main(String[] args) {
124 | new StaticConfigutationInitializer().apply(new ByteBuddy().rebase(SampleConfiguration.class), TypeDescription.ForLoadedType.of(SampleConfiguration.class)).make();
125 | }
126 | }
127 |
--------------------------------------------------------------------------------