Skip to content

Commit 5bc701d

Browse files
committed
Introduce BeanFactoryContribution
This commit introduces an infrastructure to contribute generated code ahead of time to initialize a BeanFactory. Code and hints can be contributed to a BeanFactorInitialization, with the ability to write to other packages if necessary. An implementation of that new interface that registers a BeanDefinition is also included in this commit. It delegates to a BeanInstantiationGenerator for geenerating the instance supplier that creates the bean instance. For corner cases, a BeanRegistrationContributionProvider can be implemented. It allows to return a custom BeanFactoryContribution for a particualr bean definition. This usually uses the default implementation with a custom instance supplier. Note that this commit adds an temporary executable resolution that is meant to be replaced by the use of ConstructorResolver See gh-28088
1 parent cc57b55 commit 5bc701d

11 files changed

+1965
-8
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.beans.factory.generator;
18+
19+
/**
20+
* Contribute optimizations ahead of time to initialize a bean factory.
21+
*
22+
* @author Stephane Nicoll
23+
* @since 6.0
24+
*/
25+
public interface BeanFactoryContribution {
26+
27+
/**
28+
* Contribute ahead of time optimizations to the specific
29+
* {@link BeanFactoryInitialization}.
30+
* @param initialization {@link BeanFactoryInitialization} to contribute to
31+
*/
32+
void applyTo(BeanFactoryInitialization initialization);
33+
34+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.beans.factory.generator;
18+
19+
import java.util.function.Consumer;
20+
import java.util.function.Supplier;
21+
22+
import javax.lang.model.element.Modifier;
23+
24+
import org.springframework.aot.generator.GeneratedType;
25+
import org.springframework.aot.generator.GeneratedTypeContext;
26+
import org.springframework.aot.generator.ProtectedAccess;
27+
import org.springframework.beans.factory.BeanFactory;
28+
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
29+
import org.springframework.javapoet.CodeBlock;
30+
import org.springframework.javapoet.CodeBlock.Builder;
31+
import org.springframework.javapoet.MethodSpec;
32+
33+
/**
34+
* The initialization of a {@link BeanFactory}.
35+
*
36+
* @author Andy Wilkinson
37+
* @author Stephane Nicoll
38+
* @since 6.0
39+
*/
40+
public class BeanFactoryInitialization {
41+
42+
private final GeneratedTypeContext generatedTypeContext;
43+
44+
private final CodeBlock.Builder codeContributions;
45+
46+
public BeanFactoryInitialization(GeneratedTypeContext generatedTypeContext) {
47+
this.generatedTypeContext = generatedTypeContext;
48+
this.codeContributions = CodeBlock.builder();
49+
}
50+
51+
/**
52+
* Return the {@link GeneratedTypeContext} to use to contribute
53+
* additional methods or hints.
54+
* @return the generation context
55+
*/
56+
public GeneratedTypeContext generatedTypeContext() {
57+
return this.generatedTypeContext;
58+
}
59+
60+
/**
61+
* Contribute code that initializes the bean factory and that does not
62+
* require any privileged access.
63+
* @param code the code to contribute
64+
*/
65+
public void contribute(Consumer<Builder> code) {
66+
CodeBlock.Builder builder = CodeBlock.builder();
67+
code.accept(builder);
68+
CodeBlock codeBlock = builder.build();
69+
this.codeContributions.add(codeBlock);
70+
if (!codeBlock.toString().endsWith("\n")) {
71+
this.codeContributions.add("\n");
72+
}
73+
}
74+
75+
/**
76+
* Contribute code that initializes the bean factory. If privileged access
77+
* is required, a public method in the target package is created and
78+
* invoked, rather than contributing the code directly.
79+
* @param protectedAccess the {@link ProtectedAccess} instance to use
80+
* @param methodName a method name to use if privileged access is required
81+
* @param methodBody the contribution
82+
*/
83+
public void contribute(ProtectedAccess protectedAccess, Supplier<String> methodName,
84+
Consumer<Builder> methodBody) {
85+
String targetPackageName = this.generatedTypeContext.getMainGeneratedType().getClassName().packageName();
86+
String protectedPackageName = protectedAccess.getPrivilegedPackageName(targetPackageName);
87+
if (protectedPackageName != null) {
88+
GeneratedType type = this.generatedTypeContext.getGeneratedType(protectedPackageName);
89+
MethodSpec.Builder method = MethodSpec.methodBuilder(methodName.get())
90+
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
91+
.addParameter(DefaultListableBeanFactory.class, "beanFactory");
92+
CodeBlock.Builder code = CodeBlock.builder();
93+
methodBody.accept(code);
94+
method.addCode(code.build());
95+
contribute(main -> main.addStatement("$T.$N(beanFactory)", type.getClassName(), type.addMethod(method)));
96+
}
97+
else {
98+
contribute(methodBody);
99+
}
100+
}
101+
102+
/**
103+
* Return the code that has been contributed to this instance.
104+
* @return the code
105+
*/
106+
public CodeBlock toCodeBlock() {
107+
return this.codeContributions.build();
108+
}
109+
110+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.beans.factory.generator;
18+
19+
import java.lang.reflect.Executable;
20+
21+
import org.springframework.aot.generator.CodeContribution;
22+
import org.springframework.aot.hint.RuntimeHints;
23+
24+
/**
25+
* Generate code that instantiate a particular bean.
26+
*
27+
* @author Stephane Nicoll
28+
* @since 6.0
29+
*/
30+
public interface BeanInstantiationGenerator {
31+
32+
/**
33+
* Return the {@link Executable} that is used to create the bean instance
34+
* for further metadata processing.
35+
* @return the executable that is used to create the bean instance
36+
*/
37+
Executable getInstanceCreator();
38+
39+
/**
40+
* Return the necessary code to instantiate a bean.
41+
* @param runtimeHints the runtime hints instance to use
42+
* @return a code contribution that provides an initialized bean instance
43+
*/
44+
CodeContribution generateBeanInstantiation(RuntimeHints runtimeHints);
45+
46+
}
47+

spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanParameterGenerator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
*/
5050
public final class BeanParameterGenerator {
5151

52+
/**
53+
* A default instance that does not handle inner bean definitions.
54+
*/
55+
public static final BeanParameterGenerator INSTANCE = new BeanParameterGenerator();
56+
5257
private final ResolvableTypeGenerator typeGenerator = new ResolvableTypeGenerator();
5358

5459
private final Function<BeanDefinition, CodeBlock> innerBeanDefinitionGenerator;

0 commit comments

Comments
 (0)