• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TAKETODAY / today-infrastructure / 20224960533

15 Dec 2025 08:11AM UTC coverage: 84.388% (-0.02%) from 84.404%
20224960533

push

github

TAKETODAY
:white_check_mark: 在测试中排除 jacoco 初始化方法以避免干扰

61869 of 78367 branches covered (78.95%)

Branch coverage included in aggregate %.

145916 of 167860 relevant lines covered (86.93%)

3.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.58
today-context/src/main/java/infra/context/annotation/ConfigurationClassPostProcessor.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.context.annotation;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.io.IOException;
23
import java.io.UncheckedIOException;
24
import java.lang.reflect.Constructor;
25
import java.lang.reflect.Executable;
26
import java.lang.reflect.Method;
27
import java.util.ArrayList;
28
import java.util.Arrays;
29
import java.util.HashMap;
30
import java.util.HashSet;
31
import java.util.LinkedHashMap;
32
import java.util.LinkedHashSet;
33
import java.util.List;
34
import java.util.Map;
35
import java.util.Set;
36
import java.util.function.Function;
37
import java.util.function.Predicate;
38
import java.util.function.Supplier;
39

40
import javax.lang.model.element.Modifier;
41

42
import infra.aop.framework.autoproxy.AutoProxyUtils;
43
import infra.aot.generate.AccessControl;
44
import infra.aot.generate.GeneratedClass;
45
import infra.aot.generate.GeneratedMethod;
46
import infra.aot.generate.GeneratedMethods;
47
import infra.aot.generate.GenerationContext;
48
import infra.aot.generate.MethodReference;
49
import infra.aot.generate.MethodReference.ArgumentCodeGenerator;
50
import infra.aot.hint.ExecutableMode;
51
import infra.aot.hint.MemberCategory;
52
import infra.aot.hint.ReflectionHints;
53
import infra.aot.hint.ResourceHints;
54
import infra.aot.hint.RuntimeHints;
55
import infra.aot.hint.TypeReference;
56
import infra.beans.BeanUtils;
57
import infra.beans.PropertyValues;
58
import infra.beans.factory.BeanClassLoaderAware;
59
import infra.beans.factory.BeanDefinitionStoreException;
60
import infra.beans.factory.BeanFactory;
61
import infra.beans.factory.BeanRegistrar;
62
import infra.beans.factory.DependenciesBeanPostProcessor;
63
import infra.beans.factory.InitializationBeanPostProcessor;
64
import infra.beans.factory.annotation.AnnotatedBeanDefinition;
65
import infra.beans.factory.aot.AotServices;
66
import infra.beans.factory.aot.BeanFactoryInitializationAotContribution;
67
import infra.beans.factory.aot.BeanFactoryInitializationAotProcessor;
68
import infra.beans.factory.aot.BeanFactoryInitializationCode;
69
import infra.beans.factory.aot.BeanRegistrationAotContribution;
70
import infra.beans.factory.aot.BeanRegistrationAotProcessor;
71
import infra.beans.factory.aot.BeanRegistrationCode;
72
import infra.beans.factory.aot.BeanRegistrationCodeFragments;
73
import infra.beans.factory.aot.BeanRegistrationCodeFragmentsDecorator;
74
import infra.beans.factory.aot.InstanceSupplierCodeGenerator;
75
import infra.beans.factory.config.BeanDefinition;
76
import infra.beans.factory.config.BeanDefinitionCustomizer;
77
import infra.beans.factory.config.BeanDefinitionHolder;
78
import infra.beans.factory.config.BeanFactoryPostProcessor;
79
import infra.beans.factory.config.ConfigurableBeanFactory;
80
import infra.beans.factory.config.SingletonBeanRegistry;
81
import infra.beans.factory.support.AbstractBeanDefinition;
82
import infra.beans.factory.support.BeanDefinitionRegistry;
83
import infra.beans.factory.support.BeanDefinitionRegistryPostProcessor;
84
import infra.beans.factory.support.BeanNameGenerator;
85
import infra.beans.factory.support.BeanRegistryAdapter;
86
import infra.beans.factory.support.RegisteredBean;
87
import infra.beans.factory.support.RegisteredBean.InstantiationDescriptor;
88
import infra.beans.factory.support.RootBeanDefinition;
89
import infra.beans.factory.support.StandardBeanFactory;
90
import infra.context.BootstrapContext;
91
import infra.context.BootstrapContextAware;
92
import infra.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration;
93
import infra.core.Ordered;
94
import infra.core.PriorityOrdered;
95
import infra.core.env.ConfigurableEnvironment;
96
import infra.core.env.Environment;
97
import infra.core.io.ClassPathResource;
98
import infra.core.io.PatternResourceLoader;
99
import infra.core.io.PropertySourceDescriptor;
100
import infra.core.io.PropertySourceProcessor;
101
import infra.core.io.Resource;
102
import infra.core.io.ResourceLoader;
103
import infra.core.type.AnnotationMetadata;
104
import infra.core.type.MethodMetadata;
105
import infra.core.type.classreading.CachingMetadataReaderFactory;
106
import infra.core.type.classreading.MetadataReaderFactory;
107
import infra.javapoet.ClassName;
108
import infra.javapoet.CodeBlock;
109
import infra.javapoet.MethodSpec;
110
import infra.javapoet.NameAllocator;
111
import infra.javapoet.ParameterizedTypeName;
112
import infra.lang.Assert;
113
import infra.logging.Logger;
114
import infra.logging.LoggerFactory;
115
import infra.stereotype.Component;
116
import infra.util.ClassUtils;
117
import infra.util.CollectionUtils;
118
import infra.util.LinkedMultiValueMap;
119
import infra.util.MultiValueMap;
120
import infra.util.ObjectUtils;
121
import infra.util.ReflectionUtils;
122
import infra.util.StringUtils;
123

124
import static infra.context.annotation.ConfigurationClassUtils.CONFIGURATION_CLASS_LITE;
125

126
/**
127
 * {@link BeanFactoryPostProcessor} used for bootstrapping processing of
128
 * {@link Configuration @Configuration} classes.
129
 *
130
 * <p>This post processor is priority-ordered as it is important that any
131
 * {@link Component @Component} methods declared in {@code @Configuration} classes have
132
 * their corresponding bean definitions registered before any other
133
 * {@code BeanFactoryPostProcessor} executes.
134
 *
135
 * @author Chris Beams
136
 * @author Juergen Hoeller
137
 * @author Phillip Webb
138
 * @author Sam Brannen
139
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
140
 * @since 4.0 2021/12/7 21:36
141
 */
142
public class ConfigurationClassPostProcessor implements PriorityOrdered, BeanClassLoaderAware, BootstrapContextAware,
143
        BeanDefinitionRegistryPostProcessor, BeanRegistrationAotProcessor, BeanFactoryInitializationAotProcessor {
144

145
  private static final Logger log = LoggerFactory.getLogger(ConfigurationClassPostProcessor.class);
3✔
146

147
  private static final String IMPORT_REGISTRY_BEAN_NAME =
1✔
148
          ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
3✔
149

150
  public static final AnnotationBeanNameGenerator IMPORT_BEAN_NAME_GENERATOR =
3✔
151
          FullyQualifiedAnnotationBeanNameGenerator.INSTANCE;
152

153
  @Nullable
154
  private BootstrapContext bootstrapContext;
155

156
  private final Set<Integer> registriesPostProcessed = new HashSet<>();
10✔
157

158
  private final Set<Integer> factoriesPostProcessed = new HashSet<>();
10✔
159

160
  private final MultiValueMap<String, BeanRegistrar> beanRegistrars = new LinkedMultiValueMap<>();
10✔
161

162
  @Nullable
163
  private ConfigurationClassBeanDefinitionReader reader;
164

165
  private boolean localBeanNameGeneratorSet = false;
6✔
166

167
  /* Using fully qualified class names as default bean names by default. */
168
  private BeanNameGenerator importBeanNameGenerator = IMPORT_BEAN_NAME_GENERATOR;
6✔
169

170
  @Nullable
2✔
171
  private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
4✔
172

173
  @Nullable
174
  private List<PropertySourceDescriptor> propertySourceDescriptors;
175

176
  public ConfigurationClassPostProcessor() {
2✔
177
  }
1✔
178

179
  public ConfigurationClassPostProcessor(BootstrapContext bootstrapContext) {
2✔
180
    setBootstrapContext(bootstrapContext);
3✔
181
  }
1✔
182

183
  @Override
184
  public void setBootstrapContext(BootstrapContext context) {
185
    Assert.notNull(context, "BootstrapContext is required");
3✔
186
    this.bootstrapContext = context;
3✔
187
  }
1✔
188

189
  // @since 4.0
190
  protected final BootstrapContext obtainBootstrapContext() {
191
    Assert.state(bootstrapContext != null, "BootstrapContext is required");
7!
192
    return bootstrapContext;
3✔
193
  }
194

195
  @Override
196
  public int getOrder() {
197
    return Ordered.LOWEST_PRECEDENCE;  // within PriorityOrdered
2✔
198
  }
199

200
  /**
201
   * Set the {@link BeanNameGenerator} to be used when triggering component scanning
202
   * from {@link Configuration} classes and when registering {@link Import}'ed
203
   * configuration classes. The default is a standard {@link AnnotationBeanNameGenerator}
204
   * for scanned components (compatible with the default in {@link ClassPathBeanDefinitionScanner})
205
   * and a variant thereof for imported configuration classes (using unique fully-qualified
206
   * class names instead of standard component overriding).
207
   * <p>Note that this strategy does <em>not</em> apply to {@link Bean} methods.
208
   * <p>This setter is typically only appropriate when configuring the post-processor as a
209
   * standalone bean definition in XML, e.g. not using the dedicated {@code AnnotationConfig*}
210
   * application contexts or the {@code <context:annotation-config>} element. Any bean name
211
   * generator specified against the application context will take precedence over any set here.
212
   *
213
   * @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
214
   * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR
215
   */
216
  public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
217
    Assert.notNull(beanNameGenerator, "BeanNameGenerator is required");
3✔
218
    this.localBeanNameGeneratorSet = true;
3✔
219
    this.importBeanNameGenerator = beanNameGenerator;
3✔
220
    obtainBootstrapContext().setBeanNameGenerator(beanNameGenerator);
4✔
221
  }
1✔
222

223
  @Override
224
  public void setBeanClassLoader(ClassLoader beanClassLoader) {
225
    this.beanClassLoader = beanClassLoader;
3✔
226
  }
1✔
227

228
  /**
229
   * Derive further bean definitions from the configuration classes in the registry.
230
   */
231
  @Override
232
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
233
    int registryId = System.identityHashCode(registry);
3✔
234
    if (this.registriesPostProcessed.contains(registryId)) {
6!
235
      throw new IllegalStateException(
×
236
              "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
237
    }
238
    if (this.factoriesPostProcessed.contains(registryId)) {
6!
239
      throw new IllegalStateException(
×
240
              "postProcessBeanFactory already called on this post-processor against " + registry);
241
    }
242
    this.registriesPostProcessed.add(registryId);
6✔
243

244
    processConfigBeanDefinitions(registry);
3✔
245
  }
1✔
246

247
  /**
248
   * Prepare the Configuration classes for servicing bean requests at runtime
249
   * by replacing them with CGLIB-enhanced subclasses.
250
   */
251
  @Override
252
  public void postProcessBeanFactory(ConfigurableBeanFactory beanFactory) {
253
    if (bootstrapContext == null) {
3✔
254
      this.bootstrapContext = BootstrapContext.obtain(beanFactory);
4✔
255
    }
256
    int factoryId = System.identityHashCode(beanFactory);
3✔
257
    if (this.factoriesPostProcessed.contains(factoryId)) {
6✔
258
      throw new IllegalStateException(
7✔
259
              "postProcessBeanFactory already called on this post-processor against " + beanFactory);
260
    }
261
    this.factoriesPostProcessed.add(factoryId);
6✔
262
    if (!this.registriesPostProcessed.contains(factoryId)) {
6✔
263
      // BeanDefinitionRegistryPostProcessor hook apparently not supported...
264
      // Simply call processConfigurationClasses lazily at this point then.
265
      processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
4✔
266
    }
267

268
    enhanceConfigurationClasses(beanFactory);
3✔
269
    beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
6✔
270
  }
1✔
271

272
  @Nullable
273
  @Override
274
  public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
275
    Object configClassAttr = registeredBean.getMergedBeanDefinition()
3✔
276
            .getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
2✔
277
    if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
4✔
278
      return BeanRegistrationAotContribution.withCustomCodeFragments(codeFragments ->
4✔
279
              new ConfigurationClassProxyBeanRegistrationCodeFragments(codeFragments, registeredBean));
×
280
    }
281
    return null;
2✔
282
  }
283

284
  @Override
285
  @Nullable
286
  @SuppressWarnings("NullAway")
287
  public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableBeanFactory beanFactory) {
288
    boolean hasPropertySourceDescriptors = CollectionUtils.isNotEmpty(this.propertySourceDescriptors);
4✔
289
    boolean hasImportRegistry = beanFactory.containsBean(IMPORT_REGISTRY_BEAN_NAME);
4✔
290
    boolean hasBeanRegistrars = !this.beanRegistrars.isEmpty();
8✔
291
    if (hasPropertySourceDescriptors || hasImportRegistry || hasBeanRegistrars) {
6!
292
      return (generationContext, code) -> {
7✔
293
        if (hasPropertySourceDescriptors) {
2✔
294
          new PropertySourcesAotContribution(this.propertySourceDescriptors, this::resolvePropertySourceLocation)
9✔
295
                  .applyTo(generationContext, code);
1✔
296
        }
297
        if (hasImportRegistry) {
2!
298
          new ImportAwareAotContribution(beanFactory).applyTo(generationContext, code);
7✔
299
        }
300
        if (hasBeanRegistrars) {
2✔
301
          new BeanRegistrarAotContribution(this.beanRegistrars, beanFactory).applyTo(generationContext, code);
9✔
302
        }
303
      };
1✔
304
    }
305
    return null;
2✔
306
  }
307

308
  @Nullable
309
  private Resource resolvePropertySourceLocation(String location) {
310
    BootstrapContext bootstrapContext = obtainBootstrapContext();
3✔
311
    try {
312
      String resolvedLocation = bootstrapContext.getEnvironment().resolveRequiredPlaceholders(location);
5✔
313
      return bootstrapContext.getResource(resolvedLocation);
4✔
314
    }
315
    catch (Exception ex) {
×
316
      return null;
×
317
    }
318
  }
319

320
  /**
321
   * Build and validate a configuration model based on the registry of
322
   * {@link Configuration} classes.
323
   */
324
  public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
325
    ArrayList<BeanDefinitionHolder> configCandidates = new ArrayList<>();
4✔
326
    String[] candidateNames = registry.getBeanDefinitionNames();
3✔
327
    BootstrapContext bootstrapContext = obtainBootstrapContext();
3✔
328
    for (String beanName : candidateNames) {
16✔
329
      BeanDefinition beanDef = registry.getBeanDefinition(beanName);
4✔
330
      if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
4✔
331
        if (log.isDebugEnabled()) {
3!
332
          log.debug("Bean definition has already been processed as a configuration class: {}", beanDef);
×
333
        }
334
      }
335
      else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, bootstrapContext)) {
4✔
336
        configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
8✔
337
      }
338
    }
339

340
    // Return immediately if no @Configuration classes were found
341
    if (configCandidates.isEmpty()) {
3✔
342
      return;
1✔
343
    }
344

345
    // Sort by previously determined @Order value, if applicable
346
    configCandidates.sort((bd1, bd2) -> {
3✔
347
      int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
4✔
348
      int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
4✔
349
      return Integer.compare(i1, i2);
4✔
350
    });
351

352
    // Detect any custom bean name generation strategy supplied through the enclosing application context
353
    SingletonBeanRegistry sbr = null;
2✔
354
    if (registry instanceof SingletonBeanRegistry) {
3!
355
      sbr = (SingletonBeanRegistry) registry;
3✔
356
      var configGenerator = (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
5✔
357
      if (configGenerator != null) {
2✔
358
        if (this.localBeanNameGeneratorSet) {
3!
359
          if (configGenerator instanceof ConfigurationBeanNameGenerator
×
360
                  && configGenerator != this.importBeanNameGenerator) {
361
            throw new IllegalStateException("Context-level ConfigurationBeanNameGenerator [" +
×
362
                    configGenerator + "] must not be overridden with processor-level generator [" +
363
                    this.importBeanNameGenerator + "]");
364
          }
365
        }
366
        else {
367
          this.importBeanNameGenerator = configGenerator;
3✔
368
        }
369
      }
370
    }
371

372
    // Parse each @Configuration class
373
    var parser = new ConfigurationClassParser(bootstrapContext);
5✔
374

375
    LinkedHashSet<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
5✔
376
    HashSet<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
6✔
377
    do {
378
      parser.parse(candidates);
3✔
379
      parser.validate();
2✔
380

381
      Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
6✔
382
      configClasses.removeAll(alreadyParsed);
4✔
383

384
      // Read the model and create bean definitions based on its content
385
      if (reader == null) {
3✔
386
        this.reader = new ConfigurationClassBeanDefinitionReader(bootstrapContext, importBeanNameGenerator, parser.importRegistry);
10✔
387
      }
388
      reader.loadBeanDefinitions(configClasses);
4✔
389
      for (ConfigurationClass configClass : configClasses) {
10✔
390
        beanRegistrars.putAll(configClass.beanRegistrars);
5✔
391
      }
1✔
392
      alreadyParsed.addAll(configClasses);
4✔
393

394
      candidates.clear();
2✔
395
      if (registry.getBeanDefinitionCount() > candidateNames.length) {
5✔
396
        String[] newCandidateNames = registry.getBeanDefinitionNames();
3✔
397
        HashSet<String> oldCandidateNames = CollectionUtils.newHashSet(candidateNames);
3✔
398
        HashSet<String> alreadyParsedClasses = new HashSet<>();
4✔
399
        for (ConfigurationClass configurationClass : alreadyParsed) {
10✔
400
          alreadyParsedClasses.add(configurationClass.metadata.getClassName());
6✔
401
        }
1✔
402
        for (String candidateName : newCandidateNames) {
16✔
403
          if (!oldCandidateNames.contains(candidateName)) {
4✔
404
            BeanDefinition bd = registry.getBeanDefinition(candidateName);
4✔
405
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, bootstrapContext)
6✔
406
                    && !alreadyParsedClasses.contains(bd.getBeanClassName())) {
3✔
407
              candidates.add(new BeanDefinitionHolder(bd, candidateName));
8✔
408
            }
409
          }
410
        }
411
        candidateNames = newCandidateNames;
2✔
412
      }
413
    }
414
    while (!candidates.isEmpty());
3✔
415

416
    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
417
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
6!
418
      sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.importRegistry);
5✔
419
    }
420

421
    // Store the PropertySourceDescriptors to contribute them Ahead-of-time if necessary
422
    this.propertySourceDescriptors = parser.getPropertySourceDescriptors();
4✔
423

424
    bootstrapContext.clearCache();
2✔
425
  }
1✔
426

427
  /**
428
   * Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
429
   * any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
430
   * Candidate status is determined by BeanDefinition attribute metadata.
431
   *
432
   * @see ConfigurationClassEnhancer
433
   */
434
  public void enhanceConfigurationClasses(ConfigurableBeanFactory beanFactory) {
435
    LinkedHashMap<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
4✔
436
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
17✔
437
      BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
4✔
438
      Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
4✔
439
      AnnotationMetadata annotationMetadata = null;
2✔
440
      MethodMetadata methodMetadata = null;
2✔
441
      if (beanDef instanceof AnnotatedBeanDefinition annotatedBeanDefinition) {
6✔
442
        annotationMetadata = annotatedBeanDefinition.getMetadata();
3✔
443
        methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
3✔
444
      }
445
      if ((configClassAttr != null || methodMetadata != null)
7!
446
              && (beanDef instanceof AbstractBeanDefinition abd) && !abd.hasBeanClass()) {
6✔
447
        // Configuration class (full or lite) or a configuration-derived @Bean method
448
        // -> eagerly resolve bean class at this point, unless it's a 'lite' configuration
449
        // or component class without @Bean methods.
450
        boolean liteConfigurationCandidateWithoutBeanMethods = CONFIGURATION_CLASS_LITE.equals(configClassAttr)
7✔
451
                && (annotationMetadata != null) && !ConfigurationClassUtils.hasComponentMethods(annotationMetadata);
6✔
452
        if (!liteConfigurationCandidateWithoutBeanMethods) {
2✔
453
          try {
454
            abd.resolveBeanClass(this.beanClassLoader);
5✔
455
          }
456
          catch (Throwable ex) {
×
457
            throw new IllegalStateException(
×
458
                    "Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
×
459
          }
1✔
460
        }
461
      }
462
      if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
4✔
463
        if (!(beanDef instanceof AbstractBeanDefinition abd)) {
7!
464
          throw new BeanDefinitionStoreException(
×
465
                  "Cannot enhance @Configuration bean definition '%s' since it is not stored in an AbstractBeanDefinition subclass"
466
                          .formatted(beanName));
×
467
        }
468
        else if (beanFactory.containsSingleton(beanName)) {
4!
469
          if (log.isWarnEnabled()) {
×
470
            log.warn("Cannot enhance @Configuration bean definition '{}' " +
×
471
                    "since its singleton instance has been created too early. The typical cause " +
472
                    "is a non-static @Component method with a BeanDefinitionRegistryPostProcessor " +
473
                    "return type: Consider declaring such methods as 'static'.", beanName);
474
          }
475
        }
476
        else {
477
          configBeanDefs.put(beanName, abd);
5✔
478
        }
479
      }
480
    }
481
    if (configBeanDefs.isEmpty()) {
3✔
482
      // nothing to enhance -> return immediately
483
      return;
1✔
484
    }
485

486
    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
4✔
487
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
11✔
488
      AbstractBeanDefinition beanDef = entry.getValue();
4✔
489
      // If a @Configuration class gets proxied, always proxy the target class
490
      beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
4✔
491
      // Set enhanced subclass of the user-specified bean class
492
      Class<?> configClass = beanDef.getBeanClass();
3✔
493
      Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
6✔
494
      if (configClass != enhancedClass) {
3✔
495
        if (log.isTraceEnabled()) {
3!
496
          log.trace("Replacing bean definition '{}' existing class '{}' with " +
×
497
                  "enhanced class '{}'", entry.getKey(), configClass.getName(), enhancedClass.getName());
×
498
        }
499
        beanDef.setBeanClass(enhancedClass);
3✔
500
      }
501
    }
1✔
502
  }
1✔
503

504
  private record ImportAwareBeanPostProcessor(BeanFactory beanFactory)
6✔
505
          implements DependenciesBeanPostProcessor, InitializationBeanPostProcessor, Ordered {
506

507
    @Override
508
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
509
      if (bean instanceof ImportAware importAware) {
6✔
510
        ImportRegistry registry = beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
7✔
511
        AnnotationMetadata importingClass = registry.getImportingClassFor(ClassUtils.getUserClass(bean).getName());
6✔
512
        if (importingClass != null) {
2!
513
          importAware.setImportMetadata(importingClass);
3✔
514
        }
515
      }
516
      return bean;
2✔
517
    }
518

519
    @Override
520
    @Nullable
521
    public PropertyValues processDependencies(@Nullable PropertyValues propertyValues, Object bean, String beanName) {
522
      // postProcessDependencies method attempts to autowire other configuration beans.
523
      if (bean instanceof EnhancedConfiguration enhancedConfiguration) {
6✔
524
        // FIXME
525
        enhancedConfiguration.setBeanFactory(this.beanFactory);
4✔
526
      }
527
      return propertyValues;
2✔
528
    }
529

530
    @Override
531
    public int getOrder() {
532
      return HIGHEST_PRECEDENCE;
×
533
    }
534
  }
535

536
  private static class ImportAwareAotContribution implements BeanFactoryInitializationAotContribution {
537

538
    private static final String BEAN_FACTORY_VARIABLE = BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE;
539

540
    private static final ParameterizedTypeName STRING_STRING_MAP =
12✔
541
            ParameterizedTypeName.get(Map.class, String.class, String.class);
2✔
542

543
    private static final String MAPPINGS_VARIABLE = "mappings";
544

545
    private static final String BEAN_DEFINITION_VARIABLE = "beanDefinition";
546

547
    private static final String BEAN_NAME = "infra.context.annotation.internalImportAwareAotProcessor";
548

549
    private final ConfigurableBeanFactory beanFactory;
550

551
    public ImportAwareAotContribution(ConfigurableBeanFactory beanFactory) {
2✔
552
      this.beanFactory = beanFactory;
3✔
553
    }
1✔
554

555
    @Override
556
    public void applyTo(GenerationContext generationContext,
557
            BeanFactoryInitializationCode beanFactoryInitializationCode) {
558

559
      Map<String, String> mappings = buildImportAwareMappings();
3✔
560
      if (!mappings.isEmpty()) {
3✔
561
        GeneratedMethod generatedMethod = beanFactoryInitializationCode
1✔
562
                .getMethods()
5✔
563
                .add("addImportAwareBeanPostProcessors", method ->
2✔
564
                        generateAddPostProcessorMethod(method, mappings));
5✔
565
        beanFactoryInitializationCode
2✔
566
                .addInitializer(generatedMethod.toMethodReference());
2✔
567
        ResourceHints hints = generationContext.getRuntimeHints().resources();
4✔
568
        mappings.forEach(
4✔
569
                (target, from) -> hints.registerType(TypeReference.of(from)));
6✔
570
      }
571
    }
1✔
572

573
    private void generateAddPostProcessorMethod(MethodSpec.Builder method, Map<String, String> mappings) {
574
      method.addJavadoc("Add ImportAwareBeanPostProcessor to support ImportAware beans.");
6✔
575
      method.addModifiers(Modifier.PRIVATE);
9✔
576
      method.addParameter(StandardBeanFactory.class, BEAN_FACTORY_VARIABLE);
7✔
577
      method.addCode(generateAddPostProcessorCode(mappings));
6✔
578
    }
1✔
579

580
    private CodeBlock generateAddPostProcessorCode(Map<String, String> mappings) {
581
      CodeBlock.Builder code = CodeBlock.builder();
2✔
582
      code.addStatement("$T $L = new $T<>()", STRING_STRING_MAP, MAPPINGS_VARIABLE, HashMap.class);
18✔
583
      mappings.forEach((type, from) -> code.addStatement("$L.put($S, $S)", MAPPINGS_VARIABLE, type, from));
23✔
584
      code.addStatement("$T $L = new $T($T.class)", RootBeanDefinition.class, BEAN_DEFINITION_VARIABLE,
22✔
585
              RootBeanDefinition.class, ImportAwareAotBeanPostProcessor.class);
586
      code.addStatement("$L.setRole($T.ROLE_INFRASTRUCTURE)", BEAN_DEFINITION_VARIABLE, BeanDefinition.class);
14✔
587
      code.addStatement("$L.setInstanceSupplier(() -> new $T($L))", BEAN_DEFINITION_VARIABLE, ImportAwareAotBeanPostProcessor.class, MAPPINGS_VARIABLE);
18✔
588
      code.addStatement("$L.registerBeanDefinition($S, $L)", BEAN_FACTORY_VARIABLE, BEAN_NAME, BEAN_DEFINITION_VARIABLE);
18✔
589
      return code.build();
3✔
590
    }
591

592
    private Map<String, String> buildImportAwareMappings() {
593
      ImportRegistry importRegistry = this.beanFactory
4✔
594
              .getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
3✔
595
      Map<String, String> mappings = new LinkedHashMap<>();
4✔
596
      for (String name : this.beanFactory.getBeanDefinitionNames()) {
18✔
597
        Class<?> beanType = this.beanFactory.getType(name);
5✔
598
        if (beanType != null && ImportAware.class.isAssignableFrom(beanType)) {
6!
599
          String target = ClassUtils.getUserClass(beanType).getName();
4✔
600
          AnnotationMetadata from = importRegistry.getImportingClassFor(target);
4✔
601
          if (from != null) {
2!
602
            mappings.put(target, from.getClassName());
6✔
603
          }
604
        }
605
      }
606
      return mappings;
2✔
607
    }
608

609
  }
610

611
  private static class PropertySourcesAotContribution implements BeanFactoryInitializationAotContribution {
612

613
    private static final String ENVIRONMENT_VARIABLE = "environment";
614

615
    private static final String RESOURCE_LOADER_VARIABLE = "resourceLoader";
616

617
    private final List<PropertySourceDescriptor> descriptors;
618

619
    private final Function<String, @Nullable Resource> resourceResolver;
620

621
    PropertySourcesAotContribution(List<PropertySourceDescriptor> descriptors,
622
            Function<String, @Nullable Resource> resourceResolver) {
2✔
623
      this.descriptors = descriptors;
3✔
624
      this.resourceResolver = resourceResolver;
3✔
625
    }
1✔
626

627
    @Override
628
    public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
629
      registerRuntimeHints(generationContext.getRuntimeHints());
4✔
630
      GeneratedMethod generatedMethod = beanFactoryInitializationCode
1✔
631
              .getMethods()
4✔
632
              .add("processPropertySources", this::generateAddPropertySourceProcessorMethod);
2✔
633
      beanFactoryInitializationCode
2✔
634
              .addInitializer(generatedMethod.toMethodReference());
2✔
635
    }
1✔
636

637
    private void registerRuntimeHints(RuntimeHints hints) {
638
      for (PropertySourceDescriptor descriptor : this.descriptors) {
11✔
639
        Class<?> factory = descriptor.propertySourceFactory();
3✔
640
        if (factory != null) {
2✔
641
          hints.reflection().registerType(factory, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
11✔
642
        }
643
        for (String location : descriptor.locations()) {
11✔
644
          if (location.startsWith(PatternResourceLoader.CLASSPATH_ALL_URL_PREFIX)
6✔
645
                  || (location.startsWith(PatternResourceLoader.CLASSPATH_URL_PREFIX)
4✔
646
                  && (location.contains("*") || location.contains("?")))) {
6!
647

648
            if (log.isWarnEnabled()) {
3!
649
              log.warn("""
×
650
                      Runtime hint registration is not supported for the 'classpath*:' \
651
                      prefix or wildcards in @PropertySource locations. Please manually \
652
                      register a resource hint for each property source location represented \
653
                      by '{}'.""", location);
654
            }
655
          }
656
          else {
657
            Resource resource = this.resourceResolver.apply(location);
6✔
658
            if (resource instanceof ClassPathResource classPathResource && classPathResource.exists()) {
9!
659
              hints.resources().registerPattern(classPathResource.getPath());
6✔
660
            }
661
          }
662
        }
1✔
663
      }
1✔
664
    }
1✔
665

666
    private void generateAddPropertySourceProcessorMethod(MethodSpec.Builder method) {
667
      method.addJavadoc("Apply known @PropertySources to the environment.");
6✔
668
      method.addModifiers(Modifier.PRIVATE);
9✔
669
      method.addParameter(ConfigurableEnvironment.class, ENVIRONMENT_VARIABLE);
7✔
670
      method.addParameter(ResourceLoader.class, RESOURCE_LOADER_VARIABLE);
7✔
671
      method.addCode(generateAddPropertySourceProcessorCode());
5✔
672
    }
1✔
673

674
    private CodeBlock generateAddPropertySourceProcessorCode() {
675
      CodeBlock.Builder code = CodeBlock.builder();
2✔
676
      String processorVariable = "processor";
2✔
677
      code.addStatement("$T $L = new $T($L, $L)", PropertySourceProcessor.class,
26✔
678
              processorVariable, PropertySourceProcessor.class, ENVIRONMENT_VARIABLE,
679
              RESOURCE_LOADER_VARIABLE);
680
      code.beginControlFlow("try");
6✔
681
      for (PropertySourceDescriptor descriptor : this.descriptors) {
11✔
682
        code.addStatement("$L.processPropertySource($L)", processorVariable,
14✔
683
                generatePropertySourceDescriptorCode(descriptor));
2✔
684
      }
1✔
685
      code.nextControlFlow("catch ($T ex)", IOException.class);
10✔
686
      code.addStatement("throw new $T(ex)", UncheckedIOException.class);
10✔
687
      code.endControlFlow();
3✔
688
      return code.build();
3✔
689
    }
690

691
    private CodeBlock generatePropertySourceDescriptorCode(PropertySourceDescriptor descriptor) {
692
      CodeBlock.Builder code = CodeBlock.builder();
2✔
693
      code.add("new $T(", PropertySourceDescriptor.class);
10✔
694
      CodeBlock values = descriptor.locations().stream()
4✔
695
              .map(value -> CodeBlock.of("$S", value)).collect(CodeBlock.joining(", "));
15✔
696
      if (descriptor.name() == null && descriptor.propertySourceFactory() == null
7✔
697
              && descriptor.encoding() == null && !descriptor.ignoreResourceNotFound()) {
5!
698
        code.add("$L)", values);
11✔
699
      }
700
      else {
701
        List<CodeBlock> arguments = new ArrayList<>();
4✔
702
        arguments.add(CodeBlock.of("$T.of($L)", List.class, values));
15✔
703
        arguments.add(CodeBlock.of("$L", descriptor.ignoreResourceNotFound()));
13✔
704
        arguments.add(handleNull(descriptor.name(), () -> CodeBlock.of("$S", descriptor.name())));
19✔
705
        arguments.add(handleNull(descriptor.propertySourceFactory(),
9✔
706
                () -> CodeBlock.of("$T.class", descriptor.propertySourceFactory())));
10✔
707
        arguments.add(handleNull(descriptor.encoding(),
9✔
708
                () -> CodeBlock.of("$S", descriptor.encoding())));
×
709
        code.add(CodeBlock.join(arguments, ", "));
6✔
710
        code.add(")");
6✔
711
      }
712
      return code.build();
3✔
713
    }
714

715
    private CodeBlock handleNull(@Nullable Object value, Supplier<CodeBlock> nonNull) {
716
      if (value == null) {
2✔
717
        return CodeBlock.of("null");
5✔
718
      }
719
      else {
720
        return nonNull.get();
4✔
721
      }
722
    }
723

724
  }
725

726
  private static class ConfigurationClassProxyBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
727

728
    private final RegisteredBean registeredBean;
729

730
    private final Class<?> proxyClass;
731

732
    public ConfigurationClassProxyBeanRegistrationCodeFragments(
733
            BeanRegistrationCodeFragments codeFragments, RegisteredBean registeredBean) {
734
      super(codeFragments);
×
735
      this.registeredBean = registeredBean;
×
736
      this.proxyClass = registeredBean.getBeanType().toClass();
×
737
    }
×
738

739
    @Override
740
    public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext,
741
            BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) {
742
      CodeBlock.Builder code = CodeBlock.builder();
×
743
      code.add(super.generateSetBeanDefinitionPropertiesCode(generationContext,
×
744
              beanRegistrationCode, beanDefinition, attributeFilter));
745
      code.addStatement("$T.initializeConfigurationClass($T.class)",
×
746
              ConfigurationClassUtils.class, ClassUtils.getUserClass(this.proxyClass));
×
747
      return code.build();
×
748
    }
749

750
    @Override
751
    public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
752
            BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
753

754
      InstantiationDescriptor instantiationDescriptor = proxyInstantiationDescriptor(
×
755
              generationContext.getRuntimeHints(), this.registeredBean.resolveInstantiationDescriptor());
×
756
      return new InstanceSupplierCodeGenerator(generationContext,
×
757
              beanRegistrationCode.getClassName(), beanRegistrationCode.getMethods(), allowDirectSupplierShortcut)
×
758
              .generateCode(this.registeredBean, instantiationDescriptor);
×
759
    }
760

761
    private InstantiationDescriptor proxyInstantiationDescriptor(RuntimeHints runtimeHints, InstantiationDescriptor instantiationDescriptor) {
762
      Executable userExecutable = instantiationDescriptor.executable();
×
763
      if (userExecutable instanceof Constructor<?> userConstructor) {
×
764
        try {
765
          runtimeHints.reflection().registerType(userConstructor.getDeclaringClass());
×
766
          Constructor<?> constructor = this.proxyClass.getConstructor(userExecutable.getParameterTypes());
×
767
          return new InstantiationDescriptor(constructor);
×
768
        }
769
        catch (NoSuchMethodException ex) {
×
770
          throw new IllegalStateException("No matching constructor found on proxy " + this.proxyClass, ex);
×
771
        }
772
      }
773
      return instantiationDescriptor;
×
774
    }
775

776
  }
777

778
  private static class BeanRegistrarAotContribution implements BeanFactoryInitializationAotContribution {
779

780
    private static final String CUSTOMIZER_MAP_VARIABLE = "customizers";
781

782
    private static final String ENVIRONMENT_VARIABLE = "environment";
783

784
    private final ConfigurableBeanFactory beanFactory;
785

786
    private final MultiValueMap<String, BeanRegistrar> beanRegistrars;
787

788
    private final AotServices<BeanRegistrationAotProcessor> aotProcessors;
789

790
    public BeanRegistrarAotContribution(MultiValueMap<String, BeanRegistrar> beanRegistrars, ConfigurableBeanFactory beanFactory) {
2✔
791
      this.beanFactory = beanFactory;
3✔
792
      this.beanRegistrars = beanRegistrars;
3✔
793
      this.aotProcessors = AotServices.factoriesAndBeans(this.beanFactory).load(BeanRegistrationAotProcessor.class);
7✔
794
    }
1✔
795

796
    @Override
797
    public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
798
      GeneratedMethod generatedMethod = beanFactoryInitializationCode.getMethods().add(
9✔
799
              "applyBeanRegistrars", builder -> this.generateApplyBeanRegistrarsMethod(
6✔
800
                      builder, generationContext, beanFactoryInitializationCode.getClassName()));
1✔
801
      beanFactoryInitializationCode.addInitializer(generatedMethod.toMethodReference());
4✔
802
    }
1✔
803

804
    private void generateApplyBeanRegistrarsMethod(MethodSpec.Builder method, GenerationContext generationContext, ClassName className) {
805
      ReflectionHints reflectionHints = generationContext.getRuntimeHints().reflection();
4✔
806
      method.addJavadoc("Apply bean registrars.");
6✔
807
      method.addModifiers(Modifier.PRIVATE);
9✔
808
      method.addParameter(BeanFactory.class, BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE);
7✔
809
      method.addParameter(Environment.class, ENVIRONMENT_VARIABLE);
7✔
810
      method.addCode(generateCustomizerMap());
5✔
811

812
      for (String name : this.beanFactory.getBeanDefinitionNames()) {
18✔
813
        BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition(name);
5✔
814
        if (beanDefinition.getSource() instanceof Class<?> sourceClass
11✔
815
                && BeanRegistrar.class.isAssignableFrom(sourceClass)) {
2!
816

817
          for (BeanRegistrationAotProcessor aotProcessor : this.aotProcessors) {
11✔
818
            BeanRegistrationAotContribution contribution =
4✔
819
                    aotProcessor.processAheadOfTime(RegisteredBean.of(this.beanFactory, name));
3✔
820
            if (contribution != null) {
2!
821
              contribution.applyTo(generationContext,
×
822
                      new UnsupportedBeanRegistrationCode(name, aotProcessor.getClass()));
×
823
            }
824
          }
1✔
825
          if (beanDefinition instanceof RootBeanDefinition rootBeanDefinition) {
6!
826
            if (rootBeanDefinition.getPreferredConstructors() != null) {
3✔
827
              for (Constructor<?> constructor : rootBeanDefinition.getPreferredConstructors()) {
17✔
828
                reflectionHints.registerConstructor(constructor, ExecutableMode.INVOKE);
5✔
829
              }
830
            }
831
            if (ObjectUtils.isNotEmpty(rootBeanDefinition.getInitMethodNames())) {
4✔
832
              method.addCode(generateInitDestroyMethods(name, rootBeanDefinition,
8✔
833
                      rootBeanDefinition.getInitMethodNames(), "setInitMethodNames", reflectionHints));
3✔
834
            }
835
            if (ObjectUtils.isNotEmpty(rootBeanDefinition.getDestroyMethodNames())) {
4!
836
              method.addCode(generateInitDestroyMethods(name, rootBeanDefinition,
×
837
                      rootBeanDefinition.getDestroyMethodNames(), "setDestroyMethodNames", reflectionHints));
×
838
            }
839
            checkUnsupportedFeatures(rootBeanDefinition);
3✔
840
          }
841
        }
842
      }
843
      method.addCode(generateRegisterCode(className, generationContext));
7✔
844
    }
1✔
845

846
    private void checkUnsupportedFeatures(AbstractBeanDefinition beanDefinition) {
847
      if (ObjectUtils.isNotEmpty(beanDefinition.getFactoryBeanName())) {
4!
848
        throw new UnsupportedOperationException("AOT post processing of the factory bean name is not supported yet with BeanRegistrar");
×
849
      }
850
      if (beanDefinition.hasConstructorArgumentValues()) {
3!
851
        throw new UnsupportedOperationException("AOT post processing of argument values is not supported yet with BeanRegistrar");
×
852
      }
853
      if (!beanDefinition.getQualifiers().isEmpty()) {
4!
854
        throw new UnsupportedOperationException("AOT post processing of qualifiers is not supported yet with BeanRegistrar");
×
855
      }
856
    }
1✔
857

858
    private CodeBlock generateCustomizerMap() {
859
      var code = CodeBlock.builder();
2✔
860
      code.addStatement("$T<$T, $T> $L = new $T<>()", MultiValueMap.class, String.class, BeanDefinitionCustomizer.class,
26✔
861
              CUSTOMIZER_MAP_VARIABLE, LinkedMultiValueMap.class);
862
      return code.build();
3✔
863
    }
864

865
    private CodeBlock generateRegisterCode(ClassName className, GenerationContext generationContext) {
866
      var code = CodeBlock.builder();
2✔
867
      CodeBlock.Builder metadataReaderFactoryCode = null;
2✔
868
      NameAllocator nameAllocator = new NameAllocator();
4✔
869
      for (var beanRegistrarEntry : this.beanRegistrars.entrySet()) {
12✔
870
        for (BeanRegistrar beanRegistrar : beanRegistrarEntry.getValue()) {
12✔
871
          String beanRegistrarName = nameAllocator.newName(StringUtils.uncapitalize(beanRegistrar.getClass().getSimpleName()));
7✔
872
          Constructor<?> constructor = BeanUtils.obtainConstructor(beanRegistrar.getClass());
4✔
873
          boolean visible = isVisible(constructor, className);
5✔
874
          if (visible) {
2!
875
            code.addStatement("$T $L = new $T()", beanRegistrar.getClass(), beanRegistrarName, beanRegistrar.getClass());
21✔
876
          }
877
          else {
878
            try {
879
              Class<?> configClass = ClassUtils.forName(beanRegistrarEntry.getKey(), beanRegistrar.getClass().getClassLoader());
×
880
              GeneratedClass generatedClass = generationContext.getGeneratedClasses()
×
881
                      .getOrAddForFeatureComponent("BeanRegistrars", configClass, type ->
×
882
                              type.addJavadoc("Bean registrars for {@link $T}.", configClass)
×
883
                                      .addModifiers(Modifier.PUBLIC));
×
884
              GeneratedMethod generatedMethod = generatedClass.getMethods().add(
×
885
                      "get" + beanRegistrar.getClass().getSimpleName(),
×
886
                      method -> method
×
887
                              .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
×
888
                              .returns(BeanRegistrar.class)
×
889
                              .addStatement("return new $T()", beanRegistrar.getClass()));
×
890
              code.addStatement("$T $L = $L", BeanRegistrar.class, beanRegistrarName,
×
891
                      generatedMethod.toMethodReference().toInvokeCodeBlock(ArgumentCodeGenerator.none()));
×
892
            }
893
            catch (ClassNotFoundException ex) {
×
894
              throw new IllegalStateException(ex);
×
895
            }
×
896
          }
897
          if (beanRegistrar instanceof ImportAware) {
3✔
898
            if (metadataReaderFactoryCode == null) {
2!
899
              metadataReaderFactoryCode = CodeBlock.builder();
2✔
900
              metadataReaderFactoryCode.addStatement("$T metadataReaderFactory = new $T()",
14✔
901
                      MetadataReaderFactory.class, CachingMetadataReaderFactory.class);
902
            }
903
            CodeBlock setImportMetadataCode;
904
            if (visible) {
2!
905
              setImportMetadataCode = CodeBlock.builder()
11✔
906
                      .addStatement("$L.setImportMetadata(metadataReaderFactory.getMetadataReader($S).getAnnotationMetadata())",
1✔
907
                              beanRegistrarName, beanRegistrarEntry.getKey()).build();
5✔
908
            }
909
            else {
910
              setImportMetadataCode = CodeBlock.builder()
×
911
                      .addStatement("(($T)$L).setImportMetadata(metadataReaderFactory.getMetadataReader($S).getAnnotationMetadata())",
×
912
                              ImportAware.class, beanRegistrarName, beanRegistrarEntry.getKey()).build();
×
913
            }
914
            code.beginControlFlow("try")
6✔
915
                    .add(setImportMetadataCode)
8✔
916
                    .nextControlFlow("catch ($T ex)", IOException.class)
11✔
917
                    .addStatement("throw new $T(\"Failed to read metadata for '$L'\", ex)",
1✔
918
                            IllegalStateException.class, beanRegistrarEntry.getKey())
2✔
919
                    .endControlFlow();
2✔
920
          }
921
          code.addStatement("$L.register(new $T(($T)$L, $L, $L, $L.getClass(), $L), $L)", beanRegistrarName,
42✔
922
                  BeanRegistryAdapter.class, BeanDefinitionRegistry.class, BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE,
923
                  BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE, ENVIRONMENT_VARIABLE, beanRegistrarName,
924
                  CUSTOMIZER_MAP_VARIABLE, ENVIRONMENT_VARIABLE);
925
        }
1✔
926
      }
1✔
927
      return (metadataReaderFactoryCode == null ? code.build() : metadataReaderFactoryCode.add(code.build()).build());
11✔
928
    }
929

930
    private boolean isVisible(Constructor<?> ctor, ClassName className) {
931
      AccessControl classAccessControl = AccessControl.forClass(ctor.getDeclaringClass());
4✔
932
      AccessControl memberAccessControl = AccessControl.forMember(ctor);
3✔
933
      AccessControl.Visibility visibility = AccessControl.lowest(classAccessControl, memberAccessControl).getVisibility();
13✔
934
      return (visibility == AccessControl.Visibility.PUBLIC || (visibility != AccessControl.Visibility.PRIVATE &&
4!
935
              ctor.getDeclaringClass().getPackageName().equals(className.packageName())));
2!
936
    }
937

938
    private CodeBlock generateInitDestroyMethods(String beanName, AbstractBeanDefinition beanDefinition,
939
            String[] methodNames, String method, ReflectionHints reflectionHints) {
940

941
      var code = CodeBlock.builder();
2✔
942
      // For Publisher-based destroy methods
943
      reflectionHints.registerType(TypeReference.of("org.reactivestreams.Publisher"));
7✔
944
      Class<?> beanType = ClassUtils.getUserClass(beanDefinition.getResolvableType().toClass());
5✔
945
      Arrays.stream(methodNames).forEach(methodName -> addInitDestroyHint(beanType, methodName, reflectionHints));
11✔
946
      CodeBlock arguments = Arrays.stream(methodNames)
3✔
947
              .map(name -> CodeBlock.of("$S", name))
11✔
948
              .collect(CodeBlock.joining(", "));
4✔
949

950
      code.addStatement("$L.add($S, $L -> (($T)$L).$L($L))", CUSTOMIZER_MAP_VARIABLE, beanName, "bd",
34✔
951
              AbstractBeanDefinition.class, "bd", method, arguments);
952
      return code.build();
3✔
953
    }
954

955
    // Inspired from BeanDefinitionPropertiesCodeGenerator#addInitDestroyHint
956
    private static void addInitDestroyHint(Class<?> beanUserClass, String methodName, ReflectionHints reflectionHints) {
957
      Class<?> methodDeclaringClass = beanUserClass;
2✔
958

959
      // Parse fully-qualified method name if necessary.
960
      int indexOfDot = methodName.lastIndexOf('.');
4✔
961
      if (indexOfDot > 0) {
2!
962
        String className = methodName.substring(0, indexOfDot);
×
963
        methodName = methodName.substring(indexOfDot + 1);
×
964
        if (!beanUserClass.getName().equals(className)) {
×
965
          try {
966
            methodDeclaringClass = ClassUtils.forName(className, beanUserClass.getClassLoader());
×
967
          }
968
          catch (Throwable ex) {
×
969
            throw new IllegalStateException("Failed to load Class [%s] from ClassLoader [%s]"
×
970
                    .formatted(className, beanUserClass.getClassLoader()), ex);
×
971
          }
×
972
        }
973
      }
974

975
      Method method = ReflectionUtils.findMethod(methodDeclaringClass, methodName);
4✔
976
      if (method != null) {
2!
977
        reflectionHints.registerMethod(method, ExecutableMode.INVOKE);
5✔
978
        Method publiclyAccessibleMethod = ReflectionUtils.getPubliclyAccessibleMethodIfPossible(method, beanUserClass);
4✔
979
        if (!publiclyAccessibleMethod.equals(method)) {
4!
980
          reflectionHints.registerMethod(publiclyAccessibleMethod, ExecutableMode.INVOKE);
×
981
        }
982
      }
983
    }
1✔
984

985
    static class UnsupportedBeanRegistrationCode implements BeanRegistrationCode {
986

987
      private final String message;
988

989
      public UnsupportedBeanRegistrationCode(String beanName, Class<?> aotProcessorClass) {
×
990
        this.message = "Code generation attempted for bean %s by the AOT Processor %s is not supported with BeanRegistrar yet"
×
991
                .formatted(beanName, aotProcessorClass);
×
992
      }
×
993

994
      @Override
995
      public ClassName getClassName() {
996
        throw new UnsupportedOperationException(this.message);
×
997
      }
998

999
      @Override
1000
      public GeneratedMethods getMethods() {
1001
        throw new UnsupportedOperationException(this.message);
×
1002
      }
1003

1004
      @Override
1005
      public void addInstancePostProcessor(MethodReference methodReference) {
1006
        throw new UnsupportedOperationException(this.message);
×
1007
      }
1008
    }
1009
  }
1010

1011
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc