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

TAKETODAY / today-infrastructure / 16489896461

24 Jul 2025 06:51AM UTC coverage: 81.782%. Remained the same
16489896461

push

github

TAKETODAY
:sparkles: LogMessage API

59446 of 77637 branches covered (76.57%)

Branch coverage included in aggregate %.

140767 of 167176 relevant lines covered (84.2%)

3.6 hits per line

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

81.34
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 java.io.IOException;
21
import java.io.UncheckedIOException;
22
import java.lang.reflect.Constructor;
23
import java.lang.reflect.Executable;
24
import java.lang.reflect.Method;
25
import java.util.ArrayList;
26
import java.util.Arrays;
27
import java.util.HashMap;
28
import java.util.HashSet;
29
import java.util.LinkedHashMap;
30
import java.util.LinkedHashSet;
31
import java.util.List;
32
import java.util.Map;
33
import java.util.Set;
34
import java.util.function.Function;
35
import java.util.function.Predicate;
36
import java.util.function.Supplier;
37

38
import javax.lang.model.element.Modifier;
39

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

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

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

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

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

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

148
  @Nullable
149
  private BootstrapContext bootstrapContext;
150

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

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

155
  private final LinkedHashMap<String, BeanRegistrar> beanRegistrars = new LinkedHashMap<>();
10✔
156

157
  @Nullable
158
  private ConfigurationClassBeanDefinitionReader reader;
159

160
  private boolean localBeanNameGeneratorSet = false;
6✔
161

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

418
    bootstrapContext.clearCache();
2✔
419
  }
1✔
420

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

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

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

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

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

523
    @Override
524
    public int getOrder() {
525
      return HIGHEST_PRECEDENCE;
×
526
    }
527
  }
528

529
  private static class ImportAwareAotContribution implements BeanFactoryInitializationAotContribution {
530

531
    private static final String BEAN_FACTORY_VARIABLE = BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE;
532

533
    private static final ParameterizedTypeName STRING_STRING_MAP =
12✔
534
            ParameterizedTypeName.get(Map.class, String.class, String.class);
2✔
535

536
    private static final String MAPPINGS_VARIABLE = "mappings";
537

538
    private static final String BEAN_DEFINITION_VARIABLE = "beanDefinition";
539

540
    private static final String BEAN_NAME = "infra.context.annotation.internalImportAwareAotProcessor";
541

542
    private final ConfigurableBeanFactory beanFactory;
543

544
    public ImportAwareAotContribution(ConfigurableBeanFactory beanFactory) {
2✔
545
      this.beanFactory = beanFactory;
3✔
546
    }
1✔
547

548
    @Override
549
    public void applyTo(GenerationContext generationContext,
550
            BeanFactoryInitializationCode beanFactoryInitializationCode) {
551

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

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

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

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

602
  }
603

604
  private static class PropertySourcesAotContribution implements BeanFactoryInitializationAotContribution {
605

606
    private static final String ENVIRONMENT_VARIABLE = "environment";
607

608
    private static final String RESOURCE_LOADER_VARIABLE = "resourceLoader";
609

610
    private final List<PropertySourceDescriptor> descriptors;
611

612
    private final Function<String, Resource> resourceResolver;
613

614
    PropertySourcesAotContribution(List<PropertySourceDescriptor> descriptors,
615
            Function<String, Resource> resourceResolver) {
2✔
616
      this.descriptors = descriptors;
3✔
617
      this.resourceResolver = resourceResolver;
3✔
618
    }
1✔
619

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

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

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

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

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

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

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

717
  }
718

719
  private static class ConfigurationClassProxyBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
720

721
    private final RegisteredBean registeredBean;
722

723
    private final Class<?> proxyClass;
724

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

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

743
    @Override
744
    public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
745
            BeanRegistrationCode beanRegistrationCode, boolean allowDirectSupplierShortcut) {
746

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

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

769
  }
770

771
  private static class BeanRegistrarAotContribution implements BeanFactoryInitializationAotContribution {
772

773
    private static final String CUSTOMIZER_MAP_VARIABLE = "customizers";
774

775
    private static final String ENVIRONMENT_VARIABLE = "environment";
776

777
    private final ConfigurableBeanFactory beanFactory;
778

779
    private final Map<String, BeanRegistrar> beanRegistrars;
780

781
    private final AotServices<BeanRegistrationAotProcessor> aotProcessors;
782

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

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

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

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

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

838
    private void checkUnsupportedFeatures(AbstractBeanDefinition beanDefinition) {
839
      if (!ObjectUtils.isEmpty(beanDefinition.getFactoryBeanName())) {
4!
840
        throw new UnsupportedOperationException("AOT post processing of the factory bean name is not supported yet with BeanRegistrar");
×
841
      }
842
      if (beanDefinition.hasConstructorArgumentValues()) {
3!
843
        throw new UnsupportedOperationException("AOT post processing of argument values is not supported yet with BeanRegistrar");
×
844
      }
845
      if (!beanDefinition.getQualifiers().isEmpty()) {
4!
846
        throw new UnsupportedOperationException("AOT post processing of qualifiers is not supported yet with BeanRegistrar");
×
847
      }
848
      for (String attributeName : beanDefinition.attributeNames()) {
11✔
849
        if (!attributeName.equals(AbstractBeanDefinition.ORDER_ATTRIBUTE)
6!
850
                && !attributeName.equals("aotProcessingIgnoreRegistration")) {
2!
851
          throw new UnsupportedOperationException("AOT post processing of attribute " + attributeName +
×
852
                  " is not supported yet with BeanRegistrar");
853
        }
854
      }
1✔
855
    }
1✔
856

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

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

891
    private CodeBlock generateInitDestroyMethods(String beanName, AbstractBeanDefinition beanDefinition,
892
            String[] methodNames, String method, ReflectionHints reflectionHints) {
893

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

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

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

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

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

938
    static class UnsupportedBeanRegistrationCode implements BeanRegistrationCode {
939

940
      private final String message;
941

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

947
      @Override
948
      public ClassName getClassName() {
949
        throw new UnsupportedOperationException(this.message);
×
950
      }
951

952
      @Override
953
      public GeneratedMethods getMethods() {
954
        throw new UnsupportedOperationException(this.message);
×
955
      }
956

957
      @Override
958
      public void addInstancePostProcessor(MethodReference methodReference) {
959
        throw new UnsupportedOperationException(this.message);
×
960
      }
961
    }
962
  }
963

964
}
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