• 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

92.78
today-context/src/main/java/cn/taketoday/context/annotation/ConfigurationClass.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.util.HashSet;
21
import java.util.LinkedHashMap;
22
import java.util.LinkedHashSet;
23

24
import cn.taketoday.beans.factory.parsing.Location;
25
import cn.taketoday.beans.factory.parsing.Problem;
26
import cn.taketoday.beans.factory.parsing.ProblemReporter;
27
import cn.taketoday.beans.factory.support.BeanDefinitionReader;
28
import cn.taketoday.core.io.DescriptiveResource;
29
import cn.taketoday.core.io.Resource;
30
import cn.taketoday.core.type.AnnotationMetadata;
31
import cn.taketoday.core.type.MethodMetadata;
32
import cn.taketoday.core.type.classreading.MetadataReader;
33
import cn.taketoday.lang.Nullable;
34
import cn.taketoday.stereotype.Component;
35
import cn.taketoday.util.ClassUtils;
36

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

52
  public final AnnotationMetadata metadata;
53

54
  public final Resource resource;
55

56
  @Nullable
57
  public String beanName;
58

59
  /**
60
   * Return the configuration classes that imported this class,
61
   * or an empty Set if this configuration was not imported.
62
   *
63
   * @see #isImported()
64
   */
65
  public final LinkedHashSet<ConfigurationClass> importedBy = new LinkedHashSet<>(1);
30✔
66

67
  public final LinkedHashSet<ComponentMethod> componentMethods = new LinkedHashSet<>();
25✔
68

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

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

73
  public final HashSet<String> skippedComponentMethods = new HashSet<>();
25✔
74

75
  /**
76
   * Create a new {@link ConfigurationClass} with the given name.
77
   *
78
   * @param metadataReader reader used to parse the underlying {@link Class}
79
   * @param beanName must not be {@code null}
80
   * @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
81
   */
82
  ConfigurationClass(MetadataReader metadataReader, @Nullable String beanName) {
2✔
83
    this.metadata = metadataReader.getAnnotationMetadata();
4✔
84
    this.resource = metadataReader.getResource();
4✔
85
    this.beanName = beanName;
3✔
86
  }
1✔
87

88
  /**
89
   * Create a new {@link ConfigurationClass} representing a class that was imported
90
   * using the {@link Import} annotation or automatically processed as a nested
91
   * configuration class (if importedBy is not {@code null}).
92
   *
93
   * @param metadataReader reader used to parse the underlying {@link Class}
94
   * @param importedBy the configuration class importing this one or {@code null}
95
   */
96
  ConfigurationClass(MetadataReader metadataReader, @Nullable ConfigurationClass importedBy) {
2✔
97
    this.metadata = metadataReader.getAnnotationMetadata();
4✔
98
    this.resource = metadataReader.getResource();
4✔
99
    this.importedBy.add(importedBy);
5✔
100
  }
1✔
101

102
  /**
103
   * Create a new {@link ConfigurationClass} with the given name.
104
   *
105
   * @param clazz the underlying {@link Class} to represent
106
   * @param beanName name of the {@code @Configuration} class bean
107
   * @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
108
   */
109
  ConfigurationClass(Class<?> clazz, @Nullable String beanName) {
2✔
110
    this.metadata = AnnotationMetadata.introspect(clazz);
4✔
111
    this.resource = new DescriptiveResource(clazz.getName());
7✔
112
    this.beanName = beanName;
3✔
113
  }
1✔
114

115
  /**
116
   * Create a new {@link ConfigurationClass} representing a class that was imported
117
   * using the {@link Import} annotation or automatically processed as a nested
118
   * configuration class (if imported is {@code true}).
119
   *
120
   * @param clazz the underlying {@link Class} to represent
121
   * @param importedBy the configuration class importing this one (or {@code null})
122
   */
123
  ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) {
2✔
124
    this.metadata = AnnotationMetadata.introspect(clazz);
4✔
125
    this.resource = new DescriptiveResource(clazz.getName());
7✔
126
    this.importedBy.add(importedBy);
5✔
127
  }
1✔
128

129
  /**
130
   * Create a new {@link ConfigurationClass} with the given name.
131
   *
132
   * @param metadata the metadata for the underlying class to represent
133
   * @param beanName name of the {@code @Configuration} class bean
134
   * @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
135
   */
136
  ConfigurationClass(AnnotationMetadata metadata, @Nullable String beanName) {
2✔
137
    this.metadata = metadata;
3✔
138
    this.resource = new DescriptiveResource(metadata.getClassName());
7✔
139
    this.beanName = beanName;
3✔
140
  }
1✔
141

142
  String getSimpleName() {
143
    return ClassUtils.getShortName(metadata.getClassName());
5✔
144
  }
145

146
  void setBeanName(@Nullable String beanName) {
147
    this.beanName = beanName;
3✔
148
  }
1✔
149

150
  /**
151
   * Return whether this configuration class was registered via @{@link Import} or
152
   * automatically registered due to being nested within another configuration class.
153
   *
154
   * @see #importedBy
155
   */
156
  public boolean isImported() {
157
    return !this.importedBy.isEmpty();
8✔
158
  }
159

160
  /**
161
   * Merge the imported-by declarations from the given configuration class into this one.
162
   */
163
  void mergeImportedBy(ConfigurationClass otherConfigClass) {
164
    this.importedBy.addAll(otherConfigClass.importedBy);
6✔
165
  }
1✔
166

167
  void addMethod(ComponentMethod method) {
168
    this.componentMethods.add(method);
5✔
169
  }
1✔
170

171
  void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
172
    this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
6✔
173
  }
1✔
174

175
  void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
176
    this.importedResources.put(importedResource, readerClass);
6✔
177
  }
1✔
178

179
  void validate(ProblemReporter problemReporter) {
180
    // A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
181
    var annotation = metadata.getAnnotation(Configuration.class);
5✔
182
    if (annotation.isPresent()
6✔
183
            && annotation.getValue("proxyBeanMethods", boolean.class).orElse(true)) {
7✔
184
      if (metadata.isFinal()) {
4✔
185
        problemReporter.error(new FinalConfigurationProblem());
×
186
      }
187
      for (ComponentMethod componentMethod : componentMethods) {
11✔
188
        componentMethod.validate(problemReporter);
3✔
189
      }
1✔
190
    }
191

192
    // A configuration class may not contain overloaded bean methods unless it declares enforceUniqueMethods=false
193
    if (annotation.isPresent() && annotation.getBoolean("enforceUniqueMethods")) {
7✔
194
      LinkedHashMap<String, MethodMetadata> beanMethodsByName = new LinkedHashMap<>();
4✔
195
      for (ComponentMethod beanMethod : componentMethods) {
11✔
196
        MethodMetadata current = beanMethod.metadata;
3✔
197
        MethodMetadata existing = beanMethodsByName.put(current.getMethodName(), current);
7✔
198
        if (existing != null && existing.getDeclaringClassName().equals(current.getDeclaringClassName())) {
8!
199
          problemReporter.error(new BeanMethodOverloadingProblem(existing.getMethodName()));
×
200
        }
201
      }
1✔
202
    }
203
  }
1✔
204

205
  @Override
206
  public boolean equals(@Nullable Object other) {
207
    return (this == other || (other instanceof ConfigurationClass &&
9✔
208
            metadata.getClassName().equals(((ConfigurationClass) other).metadata.getClassName())));
10✔
209
  }
210

211
  @Override
212
  public int hashCode() {
213
    return metadata.getClassName().hashCode();
5✔
214
  }
215

216
  @Override
217
  public String toString() {
218
    return "ConfigurationClass: beanName '" + this.beanName + "', " + this.resource;
6✔
219
  }
220

221
  /**
222
   * Configuration classes must be non-final to accommodate CGLIB subclassing.
223
   */
224
  private class FinalConfigurationProblem extends Problem {
225

226
    FinalConfigurationProblem() {
3✔
227
      super(String.format("@Configuration class '%s' may not be final, when proxyBeanMethods is enabled. Remove the final modifier to continue.",
16✔
228
              getSimpleName()), new Location(resource, metadata));
2✔
229
    }
1✔
230
  }
231

232
  /**
233
   * Configuration classes are not allowed to contain overloaded bean methods
234
   * by default
235
   */
236
  private class BeanMethodOverloadingProblem extends Problem {
237

238
    BeanMethodOverloadingProblem(String methodName) {
×
239
      super(String.format("@Configuration class '%s' contains overloaded @Bean methods with name '%s'. Use " +
×
240
                      "unique method names for separate bean definitions (with individual conditions etc) " +
241
                      "or switch '@Configuration.enforceUniqueMethods' to 'false'.",
242
              getSimpleName(), methodName), new Location(resource, metadata));
×
243
    }
×
244
  }
245

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