• 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

93.81
today-context/src/main/java/infra/context/annotation/ConfigurationClass.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.util.HashSet;
23
import java.util.LinkedHashMap;
24
import java.util.LinkedHashSet;
25

26
import infra.beans.factory.BeanRegistrar;
27
import infra.beans.factory.parsing.Location;
28
import infra.beans.factory.parsing.Problem;
29
import infra.beans.factory.parsing.ProblemReporter;
30
import infra.beans.factory.support.BeanDefinitionReader;
31
import infra.core.io.DescriptiveResource;
32
import infra.core.io.Resource;
33
import infra.core.type.AnnotationMetadata;
34
import infra.core.type.MethodMetadata;
35
import infra.core.type.classreading.MetadataReader;
36
import infra.lang.Assert;
37
import infra.stereotype.Component;
38
import infra.util.ClassUtils;
39
import infra.util.LinkedMultiValueMap;
40
import infra.util.MultiValueMap;
41

42
/**
43
 * Represents a user-defined {@link Configuration @Configuration} class.
44
 * <p>Includes a set of {@link Component} methods, including all such methods
45
 * defined in the ancestry of the class, in a 'flattened-out' manner.
46
 *
47
 * @author Chris Beams
48
 * @author Juergen Hoeller
49
 * @author Phillip Webb
50
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
51
 * @see ComponentMethod
52
 * @see ConfigurationClassParser
53
 * @since 4.0
54
 */
55
final class ConfigurationClass {
56

57
  public final AnnotationMetadata metadata;
58

59
  public final Resource resource;
60

61
  public @Nullable String beanName;
62

63
  /**
64
   * whether this configuration class has been registered through a scan.
65
   */
66
  public boolean scanned = false;
15✔
67

68
  /**
69
   * Return the configuration classes that imported this class,
70
   * or an empty Set if this configuration was not imported.
71
   *
72
   * @see #isImported()
73
   */
74
  public final LinkedHashSet<ConfigurationClass> importedBy = new LinkedHashSet<>(1);
30✔
75

76
  public final LinkedHashSet<ComponentMethod> componentMethods = new LinkedHashSet<>();
25✔
77

78
  public final LinkedHashMap<String, Class<? extends BeanDefinitionReader>> importedResources = new LinkedHashMap<>();
25✔
79

80
  public final LinkedHashMap<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>();
25✔
81

82
  public final HashSet<String> skippedComponentMethods = new HashSet<>();
25✔
83

84
  public final MultiValueMap<String, BeanRegistrar> beanRegistrars = new LinkedMultiValueMap<>();
25✔
85

86
  /**
87
   * Create a new {@link ConfigurationClass} with the given name.
88
   *
89
   * @param metadataReader reader used to parse the underlying {@link Class}
90
   * @param beanName must not be {@code null}
91
   * @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
92
   */
93
  ConfigurationClass(MetadataReader metadataReader, @Nullable String beanName) {
2✔
94
    this.metadata = metadataReader.getAnnotationMetadata();
4✔
95
    this.resource = metadataReader.getResource();
4✔
96
    this.beanName = beanName;
3✔
97
  }
1✔
98

99
  /**
100
   * Create a new {@link ConfigurationClass} representing a class that was imported
101
   * using the {@link Import} annotation or automatically processed as a nested
102
   * configuration class (if importedBy is not {@code null}).
103
   *
104
   * @param metadataReader reader used to parse the underlying {@link Class}
105
   * @param importedBy the configuration class importing this one or {@code null}
106
   */
107
  ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
2✔
108
    this.metadata = metadataReader.getAnnotationMetadata();
4✔
109
    this.resource = metadataReader.getResource();
4✔
110
    this.importedBy.add(importedBy);
5✔
111
  }
1✔
112

113
  /**
114
   * Create a new {@link ConfigurationClass} with the given name.
115
   *
116
   * @param clazz the underlying {@link Class} to represent
117
   * @param beanName name of the {@code @Configuration} class bean
118
   * @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
119
   */
120
  ConfigurationClass(Class<?> clazz, @Nullable String beanName) {
2✔
121
    this.metadata = AnnotationMetadata.introspect(clazz);
4✔
122
    this.resource = new DescriptiveResource(clazz.getName());
7✔
123
    this.beanName = beanName;
3✔
124
  }
1✔
125

126
  /**
127
   * Create a new {@link ConfigurationClass} representing a class that was imported
128
   * using the {@link Import} annotation or automatically processed as a nested
129
   * configuration class (if imported is {@code true}).
130
   *
131
   * @param clazz the underlying {@link Class} to represent
132
   * @param importedBy the configuration class importing this one (or {@code null})
133
   */
134
  ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
2✔
135
    this.metadata = AnnotationMetadata.introspect(clazz);
4✔
136
    this.resource = new DescriptiveResource(clazz.getName());
7✔
137
    this.importedBy.add(importedBy);
5✔
138
  }
1✔
139

140
  /**
141
   * Create a new {@link ConfigurationClass} with the given name.
142
   *
143
   * @param metadata the metadata for the underlying class to represent
144
   * @param beanName name of the {@code @Configuration} class bean
145
   * @param scanned whether the underlying class has been registered through a scan
146
   */
147
  ConfigurationClass(AnnotationMetadata metadata, String beanName, boolean scanned) {
2✔
148
    Assert.notNull(beanName, "Bean name is required");
3✔
149
    this.metadata = metadata;
3✔
150
    this.resource = new DescriptiveResource(metadata.getClassName());
7✔
151
    this.beanName = beanName;
3✔
152
    this.scanned = scanned;
3✔
153
  }
1✔
154

155
  String getSimpleName() {
156
    return ClassUtils.getShortName(metadata.getClassName());
5✔
157
  }
158

159
  void setBeanName(@Nullable String beanName) {
160
    this.beanName = beanName;
3✔
161
  }
1✔
162

163
  /**
164
   * Return whether this configuration class was registered via @{@link Import} or
165
   * automatically registered due to being nested within another configuration class.
166
   *
167
   * @see #importedBy
168
   */
169
  public boolean isImported() {
170
    return !this.importedBy.isEmpty();
8✔
171
  }
172

173
  boolean hasNonStaticComponentMethods() {
174
    for (var beanMethod : this.componentMethods) {
11✔
175
      if (!beanMethod.metadata.isStatic()) {
4✔
176
        return true;
2✔
177
      }
178
    }
1✔
179
    return false;
2✔
180
  }
181

182
  /**
183
   * Merge the imported-by declarations from the given configuration class into this one.
184
   */
185
  void mergeImportedBy(ConfigurationClass otherConfigClass) {
186
    this.importedBy.addAll(otherConfigClass.importedBy);
6✔
187
  }
1✔
188

189
  void addMethod(ComponentMethod method) {
190
    this.componentMethods.add(method);
5✔
191
  }
1✔
192

193
  void addBeanRegistrar(String sourceClassName, BeanRegistrar beanRegistrar) {
194
    this.beanRegistrars.add(sourceClassName, beanRegistrar);
5✔
195
  }
1✔
196

197
  void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
198
    this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
6✔
199
  }
1✔
200

201
  void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
202
    this.importedResources.put(importedResource, readerClass);
6✔
203
  }
1✔
204

205
  void validate(ProblemReporter problemReporter) {
206
    // A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
207
    var annotation = metadata.getAnnotation(Configuration.class);
5✔
208
    if (metadata.isFinal() && annotation.isPresent() && hasNonStaticComponentMethods()
12✔
209
            && annotation.getBoolean("proxyBeanMethods")) {
2✔
210
      problemReporter.error(new FinalConfigurationProblem());
×
211
    }
212

213
    for (ComponentMethod componentMethod : componentMethods) {
11✔
214
      componentMethod.validate(problemReporter);
3✔
215
    }
1✔
216

217
    // A configuration class may not contain overloaded bean methods unless it declares enforceUniqueMethods=false
218
    if (annotation.isPresent() && annotation.getBoolean("enforceUniqueMethods")) {
7✔
219
      LinkedHashMap<String, MethodMetadata> beanMethodsByName = new LinkedHashMap<>();
4✔
220
      for (ComponentMethod beanMethod : componentMethods) {
11✔
221
        MethodMetadata current = beanMethod.metadata;
3✔
222
        MethodMetadata existing = beanMethodsByName.put(current.getMethodName(), current);
7✔
223
        if (existing != null && existing.getDeclaringClassName().equals(current.getDeclaringClassName())) {
8!
224
          problemReporter.error(new BeanMethodOverloadingProblem(existing.getMethodName()));
×
225
        }
226
      }
1✔
227
    }
228
  }
1✔
229

230
  @Override
231
  public boolean equals(@Nullable Object other) {
232
    return (this == other || (other instanceof ConfigurationClass &&
9✔
233
            metadata.getClassName().equals(((ConfigurationClass) other).metadata.getClassName())));
10✔
234
  }
235

236
  @Override
237
  public int hashCode() {
238
    return metadata.getClassName().hashCode();
5✔
239
  }
240

241
  @Override
242
  public String toString() {
243
    return "ConfigurationClass: beanName '%s', %s".formatted(this.beanName, this.resource);
15✔
244
  }
245

246
  /**
247
   * Configuration classes must be non-final to accommodate CGLIB subclassing.
248
   */
249
  private class FinalConfigurationProblem extends Problem {
250

251
    FinalConfigurationProblem() {
3✔
252
      super(String.format("@Configuration class '%s' may not be final, when proxyBeanMethods is enabled. Remove the final modifier to continue.",
16✔
253
              getSimpleName()), new Location(resource, metadata));
2✔
254
    }
1✔
255
  }
256

257
  /**
258
   * Configuration classes are not allowed to contain overloaded bean methods
259
   * by default
260
   */
261
  private class BeanMethodOverloadingProblem extends Problem {
262

263
    BeanMethodOverloadingProblem(String methodName) {
×
264
      super(String.format("@Configuration class '%s' contains overloaded @Bean methods with name '%s'. Use " +
×
265
                      "unique method names for separate bean definitions (with individual conditions etc) " +
266
                      "or switch '@Configuration.enforceUniqueMethods' to 'false'.",
267
              getSimpleName(), methodName), new Location(resource, metadata));
×
268
    }
×
269
  }
270

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