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

TAKETODAY / today-infrastructure / 18943093076

30 Oct 2025 01:55PM UTC coverage: 83.399% (+0.01%) from 83.385%
18943093076

push

github

TAKETODAY
:bug: Fix potential CRaC hangup after restoring

60836 of 78019 branches covered (77.98%)

Branch coverage included in aggregate %.

143845 of 167405 relevant lines covered (85.93%)

3.66 hits per line

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

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

120
import static infra.context.annotation.ConfigurationClassUtils.CONFIGURATION_CLASS_LITE;
121

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

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

143
  private static final String IMPORT_REGISTRY_BEAN_NAME =
1✔
144
          ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
3✔
145

146
  public static final AnnotationBeanNameGenerator IMPORT_BEAN_NAME_GENERATOR =
3✔
147
          FullyQualifiedAnnotationBeanNameGenerator.INSTANCE;
148

149
  @Nullable
150
  private BootstrapContext bootstrapContext;
151

152
  private final Set<Integer> registriesPostProcessed = new HashSet<>();
10✔
153

154
  private final Set<Integer> factoriesPostProcessed = new HashSet<>();
10✔
155

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

158
  @Nullable
159
  private ConfigurationClassBeanDefinitionReader reader;
160

161
  private boolean localBeanNameGeneratorSet = false;
6✔
162

163
  /* Using fully qualified class names as default bean names by default. */
164
  private BeanNameGenerator importBeanNameGenerator = IMPORT_BEAN_NAME_GENERATOR;
6✔
165

166
  @Nullable
2✔
167
  private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
4✔
168

169
  @Nullable
170
  private List<PropertySourceDescriptor> propertySourceDescriptors;
171

172
  public ConfigurationClassPostProcessor() {
2✔
173
  }
1✔
174

175
  public ConfigurationClassPostProcessor(BootstrapContext bootstrapContext) {
2✔
176
    setBootstrapContext(bootstrapContext);
3✔
177
  }
1✔
178

179
  @Override
180
  public void setBootstrapContext(BootstrapContext context) {
181
    Assert.notNull(context, "BootstrapContext is required");
3✔
182
    this.bootstrapContext = context;
3✔
183
  }
1✔
184

185
  // @since 4.0
186
  protected final BootstrapContext obtainBootstrapContext() {
187
    Assert.state(bootstrapContext != null, "BootstrapContext is required");
7!
188
    return bootstrapContext;
3✔
189
  }
190

191
  @Override
192
  public int getOrder() {
193
    return Ordered.LOWEST_PRECEDENCE;  // within PriorityOrdered
2✔
194
  }
195

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

219
  @Override
220
  public void setBeanClassLoader(ClassLoader beanClassLoader) {
221
    this.beanClassLoader = beanClassLoader;
3✔
222
  }
1✔
223

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

240
    processConfigBeanDefinitions(registry);
3✔
241
  }
1✔
242

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

264
    enhanceConfigurationClasses(beanFactory);
3✔
265
    beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
6✔
266
  }
1✔
267

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

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

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

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

336
    // Return immediately if no @Configuration classes were found
337
    if (configCandidates.isEmpty()) {
3✔
338
      return;
1✔
339
    }
340

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

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

368
    // Parse each @Configuration class
369
    var parser = new ConfigurationClassParser(bootstrapContext);
5✔
370

371
    LinkedHashSet<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
5✔
372
    HashSet<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
6✔
373
    do {
374
      parser.parse(candidates);
3✔
375
      parser.validate();
2✔
376

377
      Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
6✔
378
      configClasses.removeAll(alreadyParsed);
4✔
379

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

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

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

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

420
    bootstrapContext.clearCache();
2✔
421
  }
1✔
422

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

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

500
  private record ImportAwareBeanPostProcessor(BeanFactory beanFactory)
6✔
501
          implements DependenciesBeanPostProcessor, InitializationBeanPostProcessor, Ordered {
502

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

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

526
    @Override
527
    public int getOrder() {
528
      return HIGHEST_PRECEDENCE;
×
529
    }
530
  }
531

532
  private static class ImportAwareAotContribution implements BeanFactoryInitializationAotContribution {
533

534
    private static final String BEAN_FACTORY_VARIABLE = BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE;
535

536
    private static final ParameterizedTypeName STRING_STRING_MAP =
12✔
537
            ParameterizedTypeName.get(Map.class, String.class, String.class);
2✔
538

539
    private static final String MAPPINGS_VARIABLE = "mappings";
540

541
    private static final String BEAN_DEFINITION_VARIABLE = "beanDefinition";
542

543
    private static final String BEAN_NAME = "infra.context.annotation.internalImportAwareAotProcessor";
544

545
    private final ConfigurableBeanFactory beanFactory;
546

547
    public ImportAwareAotContribution(ConfigurableBeanFactory beanFactory) {
2✔
548
      this.beanFactory = beanFactory;
3✔
549
    }
1✔
550

551
    @Override
552
    public void applyTo(GenerationContext generationContext,
553
            BeanFactoryInitializationCode beanFactoryInitializationCode) {
554

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

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

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

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

605
  }
606

607
  private static class PropertySourcesAotContribution implements BeanFactoryInitializationAotContribution {
608

609
    private static final String ENVIRONMENT_VARIABLE = "environment";
610

611
    private static final String RESOURCE_LOADER_VARIABLE = "resourceLoader";
612

613
    private final List<PropertySourceDescriptor> descriptors;
614

615
    private final Function<String, @Nullable Resource> resourceResolver;
616

617
    PropertySourcesAotContribution(List<PropertySourceDescriptor> descriptors,
618
            Function<String, @Nullable Resource> resourceResolver) {
2✔
619
      this.descriptors = descriptors;
3✔
620
      this.resourceResolver = resourceResolver;
3✔
621
    }
1✔
622

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

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

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

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

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

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

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

720
  }
721

722
  private static class ConfigurationClassProxyBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
723

724
    private final RegisteredBean registeredBean;
725

726
    private final Class<?> proxyClass;
727

728
    public ConfigurationClassProxyBeanRegistrationCodeFragments(
729
            BeanRegistrationCodeFragments codeFragments, RegisteredBean registeredBean) {
730
      super(codeFragments);
×
731
      this.registeredBean = registeredBean;
×
732
      this.proxyClass = registeredBean.getBeanType().toClass();
×
733
    }
×
734

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

746
    @Override
747
    public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
748
            BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
749

750
      InstantiationDescriptor instantiationDescriptor = proxyInstantiationDescriptor(
×
751
              generationContext.getRuntimeHints(), this.registeredBean.resolveInstantiationDescriptor());
×
752
      return new InstanceSupplierCodeGenerator(generationContext,
×
753
              beanRegistrationCode.getClassName(), beanRegistrationCode.getMethods(), allowDirectSupplierShortcut)
×
754
              .generateCode(this.registeredBean, instantiationDescriptor);
×
755
    }
756

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

772
  }
773

774
  private static class BeanRegistrarAotContribution implements BeanFactoryInitializationAotContribution {
775

776
    private static final String CUSTOMIZER_MAP_VARIABLE = "customizers";
777

778
    private static final String ENVIRONMENT_VARIABLE = "environment";
779

780
    private final ConfigurableBeanFactory beanFactory;
781

782
    private final MultiValueMap<String, BeanRegistrar> beanRegistrars;
783

784
    private final AotServices<BeanRegistrationAotProcessor> aotProcessors;
785

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

792
    @Override
793
    public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
794
      GeneratedMethod generatedMethod = beanFactoryInitializationCode.getMethods().add(
8✔
795
              "applyBeanRegistrars", builder -> this.generateApplyBeanRegistrarsMethod(builder, generationContext));
5✔
796
      beanFactoryInitializationCode.addInitializer(generatedMethod.toMethodReference());
4✔
797
    }
1✔
798

799
    private void generateApplyBeanRegistrarsMethod(MethodSpec.Builder method, GenerationContext generationContext) {
800
      ReflectionHints reflectionHints = generationContext.getRuntimeHints().reflection();
4✔
801
      method.addJavadoc("Apply bean registrars.");
6✔
802
      method.addModifiers(Modifier.PRIVATE);
9✔
803
      method.addParameter(BeanFactory.class, BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE);
7✔
804
      method.addParameter(Environment.class, ENVIRONMENT_VARIABLE);
7✔
805
      method.addCode(generateCustomizerMap());
5✔
806

807
      for (String name : this.beanFactory.getBeanDefinitionNames()) {
18✔
808
        BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition(name);
5✔
809
        if (beanDefinition.getSource() instanceof Class<?> sourceClass
11✔
810
                && BeanRegistrar.class.isAssignableFrom(sourceClass)) {
2!
811

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

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

853
    private CodeBlock generateCustomizerMap() {
854
      var code = CodeBlock.builder();
2✔
855
      code.addStatement("$T<$T, $T> $L = new $T<>()", MultiValueMap.class, String.class, BeanDefinitionCustomizer.class,
26✔
856
              CUSTOMIZER_MAP_VARIABLE, LinkedMultiValueMap.class);
857
      return code.build();
3✔
858
    }
859

860
    private CodeBlock generateRegisterCode() {
861
      var code = CodeBlock.builder();
2✔
862
      CodeBlock.Builder metadataReaderFactoryCode = null;
2✔
863
      NameAllocator nameAllocator = new NameAllocator();
4✔
864
      for (Map.Entry<String, List<BeanRegistrar>> beanRegistrarEntry : beanRegistrars.entrySet()) {
12✔
865
        for (BeanRegistrar beanRegistrar : beanRegistrarEntry.getValue()) {
12✔
866
          String beanRegistrarName = nameAllocator.newName(StringUtils.uncapitalize(beanRegistrar.getClass().getSimpleName()));
7✔
867
          code.addStatement("$T $L = new $T()", beanRegistrar.getClass(), beanRegistrarName, beanRegistrar.getClass());
20✔
868
          if (beanRegistrar instanceof ImportAware) {
3✔
869
            if (metadataReaderFactoryCode == null) {
2!
870
              metadataReaderFactoryCode = CodeBlock.builder();
2✔
871
              metadataReaderFactoryCode.addStatement("$T metadataReaderFactory = new $T()", MetadataReaderFactory.class, CachingMetadataReaderFactory.class);
14✔
872
            }
873
            code.beginControlFlow("try")
15✔
874
                    .addStatement("$L.setImportMetadata(metadataReaderFactory.getMetadataReader($S).getAnnotationMetadata())", beanRegistrarName, beanRegistrarEntry.getKey())
10✔
875
                    .nextControlFlow("catch ($T ex)", IOException.class)
11✔
876
                    .addStatement("throw new $T(\"Failed to read metadata for '$L'\", ex)", IllegalStateException.class, beanRegistrarEntry.getKey())
3✔
877
                    .endControlFlow();
2✔
878
          }
879
          code.addStatement("$L.register(new $T(($T)$L, $L, $L, $T.class, $L), $L)", beanRegistrarName,
33✔
880
                  BeanRegistryAdapter.class, BeanDefinitionRegistry.class, BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE,
881
                  BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE, ENVIRONMENT_VARIABLE, beanRegistrar.getClass(),
10✔
882
                  CUSTOMIZER_MAP_VARIABLE, ENVIRONMENT_VARIABLE);
883
        }
1✔
884
      }
1✔
885
      return (metadataReaderFactoryCode == null ? code.build() : metadataReaderFactoryCode.add(code.build()).build());
11✔
886
    }
887

888
    private CodeBlock generateInitDestroyMethods(String beanName, AbstractBeanDefinition beanDefinition,
889
            String[] methodNames, String method, ReflectionHints reflectionHints) {
890

891
      var code = CodeBlock.builder();
2✔
892
      // For Publisher-based destroy methods
893
      reflectionHints.registerType(TypeReference.of("org.reactivestreams.Publisher"));
7✔
894
      Class<?> beanType = ClassUtils.getUserClass(beanDefinition.getResolvableType().toClass());
5✔
895
      Arrays.stream(methodNames).forEach(methodName -> addInitDestroyHint(beanType, methodName, reflectionHints));
11✔
896
      CodeBlock arguments = Arrays.stream(methodNames)
3✔
897
              .map(name -> CodeBlock.of("$S", name))
11✔
898
              .collect(CodeBlock.joining(", "));
4✔
899

900
      code.addStatement("$L.add($S, $L -> (($T)$L).$L($L))", CUSTOMIZER_MAP_VARIABLE, beanName, "bd",
34✔
901
              AbstractBeanDefinition.class, "bd", method, arguments);
902
      return code.build();
3✔
903
    }
904

905
    // Inspired from BeanDefinitionPropertiesCodeGenerator#addInitDestroyHint
906
    private static void addInitDestroyHint(Class<?> beanUserClass, String methodName, ReflectionHints reflectionHints) {
907
      Class<?> methodDeclaringClass = beanUserClass;
2✔
908

909
      // Parse fully-qualified method name if necessary.
910
      int indexOfDot = methodName.lastIndexOf('.');
4✔
911
      if (indexOfDot > 0) {
2!
912
        String className = methodName.substring(0, indexOfDot);
×
913
        methodName = methodName.substring(indexOfDot + 1);
×
914
        if (!beanUserClass.getName().equals(className)) {
×
915
          try {
916
            methodDeclaringClass = ClassUtils.forName(className, beanUserClass.getClassLoader());
×
917
          }
918
          catch (Throwable ex) {
×
919
            throw new IllegalStateException("Failed to load Class [%s] from ClassLoader [%s]"
×
920
                    .formatted(className, beanUserClass.getClassLoader()), ex);
×
921
          }
×
922
        }
923
      }
924

925
      Method method = ReflectionUtils.findMethod(methodDeclaringClass, methodName);
4✔
926
      if (method != null) {
2!
927
        reflectionHints.registerMethod(method, ExecutableMode.INVOKE);
5✔
928
        Method publiclyAccessibleMethod = ReflectionUtils.getPubliclyAccessibleMethodIfPossible(method, beanUserClass);
4✔
929
        if (!publiclyAccessibleMethod.equals(method)) {
4!
930
          reflectionHints.registerMethod(publiclyAccessibleMethod, ExecutableMode.INVOKE);
×
931
        }
932
      }
933
    }
1✔
934

935
    static class UnsupportedBeanRegistrationCode implements BeanRegistrationCode {
936

937
      private final String message;
938

939
      public UnsupportedBeanRegistrationCode(String beanName, Class<?> aotProcessorClass) {
×
940
        this.message = "Code generation attempted for bean %s by the AOT Processor %s is not supported with BeanRegistrar yet"
×
941
                .formatted(beanName, aotProcessorClass);
×
942
      }
×
943

944
      @Override
945
      public ClassName getClassName() {
946
        throw new UnsupportedOperationException(this.message);
×
947
      }
948

949
      @Override
950
      public GeneratedMethods getMethods() {
951
        throw new UnsupportedOperationException(this.message);
×
952
      }
953

954
      @Override
955
      public void addInstancePostProcessor(MethodReference methodReference) {
956
        throw new UnsupportedOperationException(this.message);
×
957
      }
958
    }
959
  }
960

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

© 2026 Coveralls, Inc