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

TAKETODAY / today-infrastructure / 6100547694

06 Sep 2023 05:42PM UTC coverage: 77.72% (-0.02%) from 77.736%
6100547694

push

github

TAKETODAY
:bug:

61545 of 83980 branches covered (0.0%)

Branch coverage included in aggregate %.

153278 of 192427 relevant lines covered (79.66%)

3.37 hits per line

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

89.61
today-context/src/main/java/cn/taketoday/context/annotation/ConfigurationClassPostProcessor.java
1
/*
2
 * Copyright 2017 - 2023 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 [http://www.gnu.org/licenses/]
16
 */
17

18
package cn.taketoday.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.util.ArrayList;
25
import java.util.HashMap;
26
import java.util.HashSet;
27
import java.util.LinkedHashMap;
28
import java.util.LinkedHashSet;
29
import java.util.List;
30
import java.util.Map;
31
import java.util.Set;
32
import java.util.function.Function;
33
import java.util.function.Predicate;
34
import java.util.function.Supplier;
35

36
import javax.lang.model.element.Modifier;
37

38
import cn.taketoday.aop.framework.autoproxy.AutoProxyUtils;
39
import cn.taketoday.aot.generate.GeneratedMethod;
40
import cn.taketoday.aot.generate.GenerationContext;
41
import cn.taketoday.aot.hint.ExecutableMode;
42
import cn.taketoday.aot.hint.MemberCategory;
43
import cn.taketoday.aot.hint.ResourceHints;
44
import cn.taketoday.aot.hint.RuntimeHints;
45
import cn.taketoday.aot.hint.TypeReference;
46
import cn.taketoday.beans.PropertyValues;
47
import cn.taketoday.beans.factory.BeanClassLoaderAware;
48
import cn.taketoday.beans.factory.BeanDefinitionStoreException;
49
import cn.taketoday.beans.factory.BeanFactory;
50
import cn.taketoday.beans.factory.DependenciesBeanPostProcessor;
51
import cn.taketoday.beans.factory.InitializationBeanPostProcessor;
52
import cn.taketoday.beans.factory.annotation.AnnotatedBeanDefinition;
53
import cn.taketoday.beans.factory.aot.BeanFactoryInitializationAotContribution;
54
import cn.taketoday.beans.factory.aot.BeanFactoryInitializationAotProcessor;
55
import cn.taketoday.beans.factory.aot.BeanFactoryInitializationCode;
56
import cn.taketoday.beans.factory.aot.BeanRegistrationAotContribution;
57
import cn.taketoday.beans.factory.aot.BeanRegistrationAotProcessor;
58
import cn.taketoday.beans.factory.aot.BeanRegistrationCode;
59
import cn.taketoday.beans.factory.aot.BeanRegistrationCodeFragments;
60
import cn.taketoday.beans.factory.aot.BeanRegistrationCodeFragmentsDecorator;
61
import cn.taketoday.beans.factory.config.BeanDefinition;
62
import cn.taketoday.beans.factory.config.BeanDefinitionHolder;
63
import cn.taketoday.beans.factory.config.BeanFactoryPostProcessor;
64
import cn.taketoday.beans.factory.config.ConfigurableBeanFactory;
65
import cn.taketoday.beans.factory.config.SingletonBeanRegistry;
66
import cn.taketoday.beans.factory.parsing.FailFastProblemReporter;
67
import cn.taketoday.beans.factory.parsing.ProblemReporter;
68
import cn.taketoday.beans.factory.support.AbstractBeanDefinition;
69
import cn.taketoday.beans.factory.support.BeanDefinitionRegistry;
70
import cn.taketoday.beans.factory.support.BeanDefinitionRegistryPostProcessor;
71
import cn.taketoday.beans.factory.support.BeanNameGenerator;
72
import cn.taketoday.beans.factory.support.RegisteredBean;
73
import cn.taketoday.beans.factory.support.RootBeanDefinition;
74
import cn.taketoday.beans.factory.support.StandardBeanFactory;
75
import cn.taketoday.context.BootstrapContext;
76
import cn.taketoday.context.BootstrapContextAware;
77
import cn.taketoday.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration;
78
import cn.taketoday.context.support.StandardApplicationContext;
79
import cn.taketoday.core.Ordered;
80
import cn.taketoday.core.PriorityOrdered;
81
import cn.taketoday.core.env.ConfigurableEnvironment;
82
import cn.taketoday.core.io.ClassPathResource;
83
import cn.taketoday.core.io.PropertySourceDescriptor;
84
import cn.taketoday.core.io.PropertySourceProcessor;
85
import cn.taketoday.core.io.Resource;
86
import cn.taketoday.core.io.ResourceLoader;
87
import cn.taketoday.core.type.AnnotationMetadata;
88
import cn.taketoday.core.type.MethodMetadata;
89
import cn.taketoday.javapoet.CodeBlock;
90
import cn.taketoday.javapoet.MethodSpec;
91
import cn.taketoday.javapoet.ParameterizedTypeName;
92
import cn.taketoday.lang.Assert;
93
import cn.taketoday.lang.Nullable;
94
import cn.taketoday.logging.Logger;
95
import cn.taketoday.logging.LoggerFactory;
96
import cn.taketoday.stereotype.Component;
97
import cn.taketoday.util.ClassUtils;
98
import cn.taketoday.util.CollectionUtils;
99

100
import static cn.taketoday.context.annotation.ConfigurationClassUtils.CONFIGURATION_CLASS_LITE;
101

102
/**
103
 * {@link BeanFactoryPostProcessor} used for bootstrapping processing of
104
 * {@link Configuration @Configuration} classes.
105
 *
106
 * <p>This post processor is priority-ordered as it is important that any
107
 * {@link Component @Component} methods declared in {@code @Configuration} classes have
108
 * their corresponding bean definitions registered before any other
109
 * {@code BeanFactoryPostProcessor} executes.
110
 *
111
 * @author Chris Beams
112
 * @author Juergen Hoeller
113
 * @author Phillip Webb
114
 * @author Sam Brannen
115
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
116
 * @since 4.0 2021/12/7 21:36
117
 */
118
public class ConfigurationClassPostProcessor implements PriorityOrdered, BeanClassLoaderAware, BootstrapContextAware,
119
        BeanDefinitionRegistryPostProcessor, BeanRegistrationAotProcessor, BeanFactoryInitializationAotProcessor {
120

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

123
  private static final String IMPORT_REGISTRY_BEAN_NAME =
1✔
124
          ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
3✔
125

126
  public static final AnnotationBeanNameGenerator IMPORT_BEAN_NAME_GENERATOR =
3✔
127
          FullyQualifiedAnnotationBeanNameGenerator.INSTANCE;
128

129
  @Nullable
130
  private BootstrapContext bootstrapContext;
131

132
  private final Set<Integer> registriesPostProcessed = new HashSet<>();
10✔
133

134
  private final Set<Integer> factoriesPostProcessed = new HashSet<>();
10✔
135

136
  @Nullable
137
  private ConfigurationClassBeanDefinitionReader reader;
138

139
  private boolean localBeanNameGeneratorSet = false;
6✔
140

141
  /* Using fully qualified class names as default bean names by default. */
142
  private BeanNameGenerator importBeanNameGenerator = IMPORT_BEAN_NAME_GENERATOR;
6✔
143

144
  @Nullable
2✔
145
  private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
4✔
146

147
  @Nullable
148
  private List<PropertySourceDescriptor> propertySourceDescriptors;
149

150
  public ConfigurationClassPostProcessor() { }
3✔
151

152
  public ConfigurationClassPostProcessor(BootstrapContext bootstrapContext) {
2✔
153
    setBootstrapContext(bootstrapContext);
3✔
154
  }
1✔
155

156
  @Override
157
  public void setBootstrapContext(BootstrapContext context) {
158
    Assert.notNull(context, "BootstrapContext is required");
3✔
159
    this.bootstrapContext = context;
3✔
160
  }
1✔
161

162
  // @since 4.0
163
  protected final BootstrapContext obtainBootstrapContext() {
164
    Assert.state(bootstrapContext != null, "BootstrapContext is required");
7!
165
    return bootstrapContext;
3✔
166
  }
167

168
  @Override
169
  public int getOrder() {
170
    return Ordered.LOWEST_PRECEDENCE;  // within PriorityOrdered
2✔
171
  }
172

173
  /**
174
   * Set the {@link ProblemReporter} to use.
175
   * <p>Used to register any problems detected with {@link Configuration} or {@link Component}
176
   * declarations. For instance, an @Component method marked as {@code final} is illegal
177
   * and would be reported as a problem. Defaults to {@link FailFastProblemReporter}.
178
   */
179
  public void setProblemReporter(@Nullable ProblemReporter problemReporter) {
180
    obtainBootstrapContext().setProblemReporter(problemReporter);
×
181
  }
×
182

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

206
  @Override
207
  public void setBeanClassLoader(ClassLoader beanClassLoader) {
208
    this.beanClassLoader = beanClassLoader;
3✔
209
  }
1✔
210

211
  /**
212
   * Derive further bean definitions from the configuration classes in the registry.
213
   */
214
  @Override
215
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
216
    int registryId = System.identityHashCode(registry);
3✔
217
    if (this.registriesPostProcessed.contains(registryId)) {
6!
218
      throw new IllegalStateException(
×
219
              "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
220
    }
221
    if (this.factoriesPostProcessed.contains(registryId)) {
6!
222
      throw new IllegalStateException(
×
223
              "postProcessBeanFactory already called on this post-processor against " + registry);
224
    }
225
    this.registriesPostProcessed.add(registryId);
6✔
226

227
    processConfigBeanDefinitions(registry);
3✔
228
  }
1✔
229

230
  /**
231
   * Prepare the Configuration classes for servicing bean requests at runtime
232
   * by replacing them with CGLIB-enhanced subclasses.
233
   */
234
  @Override
235
  public void postProcessBeanFactory(ConfigurableBeanFactory beanFactory) {
236
    if (bootstrapContext == null) {
3✔
237
      bootstrapContext = BootstrapContext.from(beanFactory);
4✔
238
    }
239
    int factoryId = System.identityHashCode(beanFactory);
3✔
240
    if (this.factoriesPostProcessed.contains(factoryId)) {
6✔
241
      throw new IllegalStateException(
6✔
242
              "postProcessBeanFactory already called on this post-processor against " + beanFactory);
243
    }
244
    this.factoriesPostProcessed.add(factoryId);
6✔
245
    if (!this.registriesPostProcessed.contains(factoryId)) {
6✔
246
      // BeanDefinitionRegistryPostProcessor hook apparently not supported...
247
      // Simply call processConfigurationClasses lazily at this point then.
248
      processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
4✔
249
    }
250

251
    enhanceConfigurationClasses(beanFactory);
3✔
252
    beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
6✔
253
  }
1✔
254

255
  @Nullable
256
  @Override
257
  public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
258
    RootBeanDefinition mergedBeanDefinition = registeredBean.getMergedBeanDefinition();
3✔
259
    Object configClassAttr = mergedBeanDefinition
2✔
260
            .getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
2✔
261
    if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
4✔
262
      Class<?> proxyClass = registeredBean.getBeanType().toClass();
4✔
263
      return BeanRegistrationAotContribution.withCustomCodeFragments(codeFragments ->
4✔
264
              new ConfigurationClassProxyBeanRegistrationCodeFragments(codeFragments, proxyClass));
6✔
265
    }
266
    return null;
2✔
267
  }
268

269
  @Override
270
  @Nullable
271
  public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableBeanFactory beanFactory) {
272
    boolean hasPropertySourceDescriptors = CollectionUtils.isNotEmpty(this.propertySourceDescriptors);
4✔
273
    boolean hasImportRegistry = beanFactory.containsBean(IMPORT_REGISTRY_BEAN_NAME);
4✔
274
    if (hasPropertySourceDescriptors || hasImportRegistry) {
4✔
275
      return (generationContext, code) -> {
6✔
276
        if (hasPropertySourceDescriptors) {
2✔
277
          new PropertySourcesAotContribution(this.propertySourceDescriptors, this::resolvePropertySourceLocation)
9✔
278
                  .applyTo(generationContext, code);
1✔
279
        }
280
        if (hasImportRegistry) {
2!
281
          new ImportAwareAotContribution(beanFactory).applyTo(generationContext, code);
7✔
282
        }
283
      };
1✔
284
    }
285
    return null;
2✔
286
  }
287

288
  @Nullable
289
  private Resource resolvePropertySourceLocation(String location) {
290
    BootstrapContext bootstrapContext = obtainBootstrapContext();
3✔
291
    try {
292
      String resolvedLocation = bootstrapContext.getEnvironment().resolveRequiredPlaceholders(location);
5✔
293
      return bootstrapContext.getResource(resolvedLocation);
4✔
294
    }
295
    catch (Exception ex) {
×
296
      return null;
×
297
    }
298
  }
299

300
  /**
301
   * Build and validate a configuration model based on the registry of
302
   * {@link Configuration} classes.
303
   */
304
  public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
305
    ArrayList<BeanDefinitionHolder> configCandidates = new ArrayList<>();
4✔
306
    String[] candidateNames = registry.getBeanDefinitionNames();
3✔
307
    BootstrapContext bootstrapContext = obtainBootstrapContext();
3✔
308
    for (String beanName : candidateNames) {
16✔
309
      BeanDefinition beanDef = registry.getBeanDefinition(beanName);
4✔
310
      if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
4✔
311
        if (log.isDebugEnabled()) {
3!
312
          log.debug("Bean definition has already been processed as a configuration class: {}", beanDef);
5✔
313
        }
314
      }
315
      else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, bootstrapContext)) {
4✔
316
        configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
8✔
317
      }
318
    }
319

320
    // Return immediately if no @Configuration classes were found
321
    if (configCandidates.isEmpty()) {
3✔
322
      return;
1✔
323
    }
324

325
    // Sort by previously determined @Order value, if applicable
326
    configCandidates.sort((bd1, bd2) -> {
3✔
327
      int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
4✔
328
      int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
4✔
329
      return Integer.compare(i1, i2);
4✔
330
    });
331

332
    // Detect any custom bean name generation strategy supplied through the enclosing application context
333
    SingletonBeanRegistry sbr = null;
2✔
334
    if (registry instanceof SingletonBeanRegistry) {
3!
335
      sbr = (SingletonBeanRegistry) registry;
3✔
336
      if (!this.localBeanNameGeneratorSet) {
3!
337
        BeanNameGenerator populator = (BeanNameGenerator) sbr.getSingleton(
5✔
338
                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
339
        if (populator != null) {
2✔
340
          this.importBeanNameGenerator = populator;
3✔
341
        }
342
      }
343
    }
344

345
    // Parse each @Configuration class
346
    var parser = new ConfigurationClassParser(bootstrapContext);
5✔
347

348
    LinkedHashSet<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
5✔
349
    HashSet<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
6✔
350
    do {
351
      parser.parse(candidates);
3✔
352
      parser.validate();
2✔
353

354
      Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
6✔
355
      configClasses.removeAll(alreadyParsed);
4✔
356

357
      // Read the model and create bean definitions based on its content
358
      if (reader == null) {
3✔
359
        this.reader = new ConfigurationClassBeanDefinitionReader(
7✔
360
                bootstrapContext, importBeanNameGenerator, parser.getImportRegistry());
3✔
361
      }
362
      reader.loadBeanDefinitions(configClasses);
4✔
363
      alreadyParsed.addAll(configClasses);
4✔
364

365
      candidates.clear();
2✔
366
      if (registry.getBeanDefinitionCount() > candidateNames.length) {
5✔
367
        String[] newCandidateNames = registry.getBeanDefinitionNames();
3✔
368
        HashSet<String> oldCandidateNames = CollectionUtils.newHashSet(candidateNames);
3✔
369
        HashSet<String> alreadyParsedClasses = new HashSet<>();
4✔
370
        for (ConfigurationClass configurationClass : alreadyParsed) {
10✔
371
          alreadyParsedClasses.add(configurationClass.metadata.getClassName());
6✔
372
        }
1✔
373
        for (String candidateName : newCandidateNames) {
16✔
374
          if (!oldCandidateNames.contains(candidateName)) {
4✔
375
            BeanDefinition bd = registry.getBeanDefinition(candidateName);
4✔
376
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, bootstrapContext)
6✔
377
                    && !alreadyParsedClasses.contains(bd.getBeanClassName())) {
3✔
378
              candidates.add(new BeanDefinitionHolder(bd, candidateName));
8✔
379
            }
380
          }
381
        }
382
        candidateNames = newCandidateNames;
2✔
383
      }
384
    }
385
    while (!candidates.isEmpty());
3✔
386

387
    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
388
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
6!
389
      sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
5✔
390
    }
391

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

395
    bootstrapContext.clearCache();
2✔
396
  }
1✔
397

398
  /**
399
   * Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
400
   * any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
401
   * Candidate status is determined by BeanDefinition attribute metadata.
402
   *
403
   * @see ConfigurationClassEnhancer
404
   */
405
  public void enhanceConfigurationClasses(ConfigurableBeanFactory beanFactory) {
406
    LinkedHashMap<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
4✔
407
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
17✔
408
      BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
4✔
409
      Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
4✔
410
      AnnotationMetadata annotationMetadata = null;
2✔
411
      MethodMetadata methodMetadata = null;
2✔
412
      if (beanDef instanceof AnnotatedBeanDefinition annotatedBeanDefinition) {
6✔
413
        annotationMetadata = annotatedBeanDefinition.getMetadata();
3✔
414
        methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
3✔
415
      }
416
      if ((configClassAttr != null || methodMetadata != null)
7!
417
              && (beanDef instanceof AbstractBeanDefinition abd) && !abd.hasBeanClass()) {
6✔
418
        // Configuration class (full or lite) or a configuration-derived @Bean method
419
        // -> eagerly resolve bean class at this point, unless it's a 'lite' configuration
420
        // or component class without @Bean methods.
421
        boolean liteConfigurationCandidateWithoutBeanMethods = CONFIGURATION_CLASS_LITE.equals(configClassAttr)
7✔
422
                && (annotationMetadata != null) && !ConfigurationClassUtils.hasComponentMethods(annotationMetadata);
6✔
423
        if (!liteConfigurationCandidateWithoutBeanMethods) {
2✔
424
          try {
425
            abd.resolveBeanClass(this.beanClassLoader);
5✔
426
          }
427
          catch (Throwable ex) {
×
428
            throw new IllegalStateException(
×
429
                    "Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
×
430
          }
1✔
431
        }
432
      }
433
      if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
4✔
434
        if (!(beanDef instanceof AbstractBeanDefinition abd)) {
7!
435
          throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
×
436
                  beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
437
        }
438
        else if (log.isWarnEnabled() && beanFactory.containsSingleton(beanName)) {
7!
439
          log.warn("Cannot enhance @Configuration bean definition '{}' " +
×
440
                  "since its singleton instance has been created too early. The typical cause " +
441
                  "is a non-static @Component method with a BeanDefinitionRegistryPostProcessor " +
442
                  "return type: Consider declaring such methods as 'static'.", beanName);
443
        }
444
        configBeanDefs.put(beanName, abd);
5✔
445
      }
446
    }
447
    if (configBeanDefs.isEmpty()) {
3✔
448
      // nothing to enhance -> return immediately
449
      return;
1✔
450
    }
451

452
    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
4✔
453
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
11✔
454
      AbstractBeanDefinition beanDef = entry.getValue();
4✔
455
      // If a @Configuration class gets proxied, always proxy the target class
456
      beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
4✔
457
      // Set enhanced subclass of the user-specified bean class
458
      Class<?> configClass = beanDef.getBeanClass();
3✔
459
      Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
6✔
460
      if (configClass != enhancedClass) {
3✔
461
        if (log.isTraceEnabled()) {
3!
462
          log.trace("Replacing bean definition '{}' existing class '{}' with " +
×
463
                  "enhanced class '{}'", entry.getKey(), configClass.getName(), enhancedClass.getName());
×
464
        }
465
        beanDef.setBeanClass(enhancedClass);
3✔
466
      }
467
    }
1✔
468
  }
1✔
469

470
  private record ImportAwareBeanPostProcessor(BeanFactory beanFactory)
6✔
471
          implements DependenciesBeanPostProcessor, InitializationBeanPostProcessor, Ordered {
472

473
    @Override
474
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
475
      if (bean instanceof ImportAware importAware) {
6✔
476
        ImportRegistry registry = beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
7✔
477
        AnnotationMetadata importingClass = registry.getImportingClassFor(ClassUtils.getUserClass(bean).getName());
6✔
478
        if (importingClass != null) {
2!
479
          importAware.setImportMetadata(importingClass);
3✔
480
        }
481
      }
482
      return bean;
2✔
483
    }
484

485
    @Override
486
    public PropertyValues processDependencies(
487
            @Nullable PropertyValues propertyValues, Object bean, String beanName) {
488
      // postProcessDependencies method attempts to autowire other configuration beans.
489
      if (bean instanceof EnhancedConfiguration enhancedConfiguration) {
6✔
490
        // FIXME
491
        enhancedConfiguration.setBeanFactory(this.beanFactory);
4✔
492
      }
493
      return propertyValues;
2✔
494
    }
495

496
    @Override
497
    public int getOrder() {
498
      return HIGHEST_PRECEDENCE;
×
499
    }
500
  }
501

502
  private static class ImportAwareAotContribution implements BeanFactoryInitializationAotContribution {
503

504
    private static final String BEAN_FACTORY_VARIABLE = BeanFactoryInitializationCode.BEAN_FACTORY_VARIABLE;
505

506
    private static final ParameterizedTypeName STRING_STRING_MAP =
12✔
507
            ParameterizedTypeName.get(Map.class, String.class, String.class);
2✔
508

509
    private static final String MAPPINGS_VARIABLE = "mappings";
510

511
    private static final String BEAN_DEFINITION_VARIABLE = "beanDefinition";
512

513
    private static final String BEAN_NAME = "cn.taketoday.context.annotation.internalImportAwareAotProcessor";
514

515
    private final ConfigurableBeanFactory beanFactory;
516

517
    public ImportAwareAotContribution(ConfigurableBeanFactory beanFactory) {
2✔
518
      this.beanFactory = beanFactory;
3✔
519
    }
1✔
520

521
    @Override
522
    public void applyTo(GenerationContext generationContext,
523
            BeanFactoryInitializationCode beanFactoryInitializationCode) {
524

525
      Map<String, String> mappings = buildImportAwareMappings();
3✔
526
      if (!mappings.isEmpty()) {
3✔
527
        GeneratedMethod generatedMethod = beanFactoryInitializationCode
1✔
528
                .getMethods()
5✔
529
                .add("addImportAwareBeanPostProcessors", method ->
2✔
530
                        generateAddPostProcessorMethod(method, mappings));
5✔
531
        beanFactoryInitializationCode
2✔
532
                .addInitializer(generatedMethod.toMethodReference());
2✔
533
        ResourceHints hints = generationContext.getRuntimeHints().resources();
4✔
534
        mappings.forEach(
4✔
535
                (target, from) -> hints.registerType(TypeReference.of(from)));
6✔
536
      }
537
    }
1✔
538

539
    private void generateAddPostProcessorMethod(MethodSpec.Builder method, Map<String, String> mappings) {
540
      method.addJavadoc("Add ImportAwareBeanPostProcessor to support ImportAware beans.");
6✔
541
      method.addModifiers(Modifier.PRIVATE);
9✔
542
      method.addParameter(StandardBeanFactory.class, BEAN_FACTORY_VARIABLE);
7✔
543
      method.addCode(generateAddPostProcessorCode(mappings));
6✔
544
    }
1✔
545

546
    private CodeBlock generateAddPostProcessorCode(Map<String, String> mappings) {
547
      CodeBlock.Builder code = CodeBlock.builder();
2✔
548
      code.addStatement("$T $L = new $T<>()", STRING_STRING_MAP, MAPPINGS_VARIABLE, HashMap.class);
18✔
549
      mappings.forEach((type, from) -> code.addStatement("$L.put($S, $S)", MAPPINGS_VARIABLE, type, from));
23✔
550
      code.addStatement("$T $L = new $T($T.class)", RootBeanDefinition.class, BEAN_DEFINITION_VARIABLE,
22✔
551
              RootBeanDefinition.class, ImportAwareAotBeanPostProcessor.class);
552
      code.addStatement("$L.setRole($T.ROLE_INFRASTRUCTURE)", BEAN_DEFINITION_VARIABLE, BeanDefinition.class);
14✔
553
      code.addStatement("$L.setInstanceSupplier(() -> new $T($L))", BEAN_DEFINITION_VARIABLE, ImportAwareAotBeanPostProcessor.class, MAPPINGS_VARIABLE);
18✔
554
      code.addStatement("$L.registerBeanDefinition($S, $L)", BEAN_FACTORY_VARIABLE, BEAN_NAME, BEAN_DEFINITION_VARIABLE);
18✔
555
      return code.build();
3✔
556
    }
557

558
    private Map<String, String> buildImportAwareMappings() {
559
      ImportRegistry importRegistry = this.beanFactory
4✔
560
              .getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
3✔
561
      Map<String, String> mappings = new LinkedHashMap<>();
4✔
562
      for (String name : this.beanFactory.getBeanDefinitionNames()) {
18✔
563
        Class<?> beanType = this.beanFactory.getType(name);
5✔
564
        if (beanType != null && ImportAware.class.isAssignableFrom(beanType)) {
6!
565
          String target = ClassUtils.getUserClass(beanType).getName();
4✔
566
          AnnotationMetadata from = importRegistry.getImportingClassFor(target);
4✔
567
          if (from != null) {
2!
568
            mappings.put(target, from.getClassName());
6✔
569
          }
570
        }
571
      }
572
      return mappings;
2✔
573
    }
574

575
  }
576

577
  private static class PropertySourcesAotContribution implements BeanFactoryInitializationAotContribution {
578

579
    private static final String ENVIRONMENT_VARIABLE = "environment";
580

581
    private static final String RESOURCE_LOADER_VARIABLE = "resourceLoader";
582

583
    private final List<PropertySourceDescriptor> descriptors;
584

585
    private final Function<String, Resource> resourceResolver;
586

587
    PropertySourcesAotContribution(List<PropertySourceDescriptor> descriptors,
588
            Function<String, Resource> resourceResolver) {
2✔
589
      this.descriptors = descriptors;
3✔
590
      this.resourceResolver = resourceResolver;
3✔
591
    }
1✔
592

593
    @Override
594
    public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
595
      registerRuntimeHints(generationContext.getRuntimeHints());
4✔
596
      GeneratedMethod generatedMethod = beanFactoryInitializationCode
1✔
597
              .getMethods()
4✔
598
              .add("processPropertySources", this::generateAddPropertySourceProcessorMethod);
2✔
599
      beanFactoryInitializationCode
2✔
600
              .addInitializer(generatedMethod.toMethodReference());
2✔
601
    }
1✔
602

603
    private void registerRuntimeHints(RuntimeHints hints) {
604
      for (PropertySourceDescriptor descriptor : this.descriptors) {
11✔
605
        Class<?> factory = descriptor.propertySourceFactory();
3✔
606
        if (factory != null) {
2✔
607
          hints.reflection().registerType(factory, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
11✔
608
        }
609
        for (String location : descriptor.locations()) {
11✔
610
          Resource resource = this.resourceResolver.apply(location);
6✔
611
          if (resource != null && resource.exists() && resource instanceof ClassPathResource classpathResource) {
11!
612
            hints.resources().registerPattern(classpathResource.getPath());
6✔
613
          }
614
        }
1✔
615
      }
1✔
616
    }
1✔
617

618
    private void generateAddPropertySourceProcessorMethod(MethodSpec.Builder method) {
619
      method.addJavadoc("Apply known @PropertySources to the environment.");
6✔
620
      method.addModifiers(Modifier.PRIVATE);
9✔
621
      method.addParameter(ConfigurableEnvironment.class, ENVIRONMENT_VARIABLE);
7✔
622
      method.addParameter(ResourceLoader.class, RESOURCE_LOADER_VARIABLE);
7✔
623
      method.addCode(generateAddPropertySourceProcessorCode());
5✔
624
    }
1✔
625

626
    private CodeBlock generateAddPropertySourceProcessorCode() {
627
      CodeBlock.Builder code = CodeBlock.builder();
2✔
628
      String processorVariable = "processor";
2✔
629
      code.addStatement("$T $L = new $T($L, $L)", PropertySourceProcessor.class,
26✔
630
              processorVariable, PropertySourceProcessor.class, ENVIRONMENT_VARIABLE,
631
              RESOURCE_LOADER_VARIABLE);
632
      code.beginControlFlow("try");
6✔
633
      for (PropertySourceDescriptor descriptor : this.descriptors) {
11✔
634
        code.addStatement("$L.processPropertySource($L)", processorVariable,
14✔
635
                generatePropertySourceDescriptorCode(descriptor));
2✔
636
      }
1✔
637
      code.nextControlFlow("catch ($T ex)", IOException.class);
10✔
638
      code.addStatement("throw new $T(ex)", UncheckedIOException.class);
10✔
639
      code.endControlFlow();
3✔
640
      return code.build();
3✔
641
    }
642

643
    private CodeBlock generatePropertySourceDescriptorCode(PropertySourceDescriptor descriptor) {
644
      CodeBlock.Builder code = CodeBlock.builder();
2✔
645
      code.add("new $T(", PropertySourceDescriptor.class);
10✔
646
      CodeBlock values = descriptor.locations().stream()
4✔
647
              .map(value -> CodeBlock.of("$S", value)).collect(CodeBlock.joining(", "));
15✔
648
      if (descriptor.name() == null && descriptor.propertySourceFactory() == null
7✔
649
              && descriptor.encoding() == null && !descriptor.ignoreResourceNotFound()) {
5!
650
        code.add("$L)", values);
11✔
651
      }
652
      else {
653
        List<CodeBlock> arguments = new ArrayList<>();
4✔
654
        arguments.add(CodeBlock.of("$T.of($L)", List.class, values));
15✔
655
        arguments.add(CodeBlock.of("$L", descriptor.ignoreResourceNotFound()));
13✔
656
        arguments.add(handleNull(descriptor.name(), () -> CodeBlock.of("$S", descriptor.name())));
19✔
657
        arguments.add(handleNull(descriptor.propertySourceFactory(),
9✔
658
                () -> CodeBlock.of("$T.class", descriptor.propertySourceFactory())));
10✔
659
        arguments.add(handleNull(descriptor.encoding(),
9✔
660
                () -> CodeBlock.of("$S", descriptor.encoding())));
×
661
        code.add(CodeBlock.join(arguments, ", "));
6✔
662
        code.add(")");
6✔
663
      }
664
      return code.build();
3✔
665
    }
666

667
    private CodeBlock handleNull(@Nullable Object value, Supplier<CodeBlock> nonNull) {
668
      if (value == null) {
2✔
669
        return CodeBlock.of("null");
5✔
670
      }
671
      else {
672
        return nonNull.get();
4✔
673
      }
674
    }
675

676
  }
677

678
  private static class ConfigurationClassProxyBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
679

680
    private final Class<?> proxyClass;
681

682
    public ConfigurationClassProxyBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments,
683
            Class<?> proxyClass) {
684
      super(codeFragments);
3✔
685
      this.proxyClass = proxyClass;
3✔
686
    }
1✔
687

688
    @Override
689
    public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext,
690
            BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) {
691
      CodeBlock.Builder code = CodeBlock.builder();
2✔
692
      code.add(super.generateSetBeanDefinitionPropertiesCode(generationContext,
9✔
693
              beanRegistrationCode, beanDefinition, attributeFilter));
694
      code.addStatement("$T.initializeConfigurationClass($T.class)",
14✔
695
              ConfigurationClassUtils.class, ClassUtils.getUserClass(this.proxyClass));
2✔
696
      return code.build();
3✔
697
    }
698

699
    @Override
700
    public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
701
            BeanRegistrationCode beanRegistrationCode, Executable constructorOrFactoryMethod,
702
            boolean allowDirectSupplierShortcut) {
703
      Executable executableToUse = proxyExecutable(generationContext.getRuntimeHints(), constructorOrFactoryMethod);
6✔
704
      return super.generateInstanceSupplierCode(generationContext, beanRegistrationCode,
7✔
705
              executableToUse, allowDirectSupplierShortcut);
706
    }
707

708
    private Executable proxyExecutable(RuntimeHints runtimeHints, Executable userExecutable) {
709
      if (userExecutable instanceof Constructor<?> userConstructor) {
6!
710
        try {
711
          runtimeHints.reflection().registerConstructor(userConstructor, ExecutableMode.INTROSPECT);
6✔
712
          return this.proxyClass.getConstructor(userExecutable.getParameterTypes());
6✔
713
        }
714
        catch (NoSuchMethodException ex) {
×
715
          throw new IllegalStateException("No matching constructor found on proxy " + this.proxyClass, ex);
×
716
        }
717
      }
718
      return userExecutable;
×
719
    }
720

721
  }
722

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

© 2025 Coveralls, Inc