├── 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 | --------------------------------------------------------------------------------