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

TAKETODAY / today-infrastructure / 18154768944

01 Oct 2025 07:26AM UTC coverage: 81.882% (-0.005%) from 81.887%
18154768944

push

github

web-flow
Merge pull request #290 from TAKETODAY/dev/jspecify

jspecify

59788 of 78013 branches covered (76.64%)

Branch coverage included in aggregate %.

141239 of 167496 relevant lines covered (84.32%)

3.6 hits per line

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

58.57
today-context/src/main/java/infra/context/annotation/ClassPathScanningComponentProvider.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.context.annotation;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.io.FileNotFoundException;
23
import java.io.IOException;
24

25
import infra.beans.factory.BeanDefinitionStoreException;
26
import infra.bytecode.ClassReader;
27
import infra.context.ResourceLoaderAware;
28
import infra.context.index.CandidateComponentsIndex;
29
import infra.core.io.PathMatchingPatternResourceLoader;
30
import infra.core.io.PatternResourceLoader;
31
import infra.core.io.ResourceLoader;
32
import infra.core.type.classreading.CachingMetadataReaderFactory;
33
import infra.core.type.classreading.ClassFormatException;
34
import infra.core.type.classreading.MetadataReader;
35
import infra.core.type.classreading.MetadataReaderFactory;
36
import infra.lang.Assert;
37
import infra.lang.TodayStrategies;
38
import infra.logging.Logger;
39
import infra.logging.LoggerFactory;
40
import infra.util.ClassUtils;
41

42
/**
43
 * A component provider that provides components from a base package. Can
44
 * use {@link CandidateComponentsIndex the index} if it is available of scans the
45
 * classpath otherwise.
46
 *
47
 * <p>This implementation is based on framework 's {@link MetadataReader MetadataReader}
48
 * facility, backed by an ASM {@link ClassReader ClassReader}.
49
 *
50
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
51
 * @since 4.0 2021/12/18 13:50
52
 */
53
public class ClassPathScanningComponentProvider implements ResourceLoaderAware {
54

55
  public static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
56

57
  /**
58
   * System property that instructs Spring to ignore class format exceptions during
59
   * classpath scanning, in particular for unsupported class file versions.
60
   * By default, such a class format mismatch leads to a classpath scanning failure.
61
   *
62
   * @see ClassFormatException
63
   */
64
  public static final String IGNORE_CLASSFORMAT_PROPERTY_NAME = "infra.classformat.ignore";
65

66
  private static final boolean shouldIgnoreClassFormatException = TodayStrategies.getFlag(IGNORE_CLASSFORMAT_PROPERTY_NAME);
4✔
67

68
  protected final Logger logger = LoggerFactory.getLogger(getClass());
5✔
69

70
  private String resourcePattern = DEFAULT_RESOURCE_PATTERN;
3✔
71

72
  private String resourcePrefix = PatternResourceLoader.CLASSPATH_ALL_URL_PREFIX;
3✔
73

74
  @Nullable
75
  protected PatternResourceLoader resourcePatternResolver;
76

77
  @Nullable
78
  private MetadataReaderFactory metadataReaderFactory;
79

80
  public ClassPathScanningComponentProvider() { }
3✔
81

82
  /**
83
   * Set the resource pattern to use when scanning the classpath.
84
   * This value will be appended to each base package name.
85
   *
86
   * @see #DEFAULT_RESOURCE_PATTERN
87
   */
88
  public void setResourcePattern(String resourcePattern) {
89
    Assert.notNull(resourcePattern, "'resourcePattern' is required");
3✔
90
    this.resourcePattern = resourcePattern;
3✔
91
  }
1✔
92

93
  /**
94
   * Set the resource prefix to use when scanning the resources.
95
   * This value will be appended to each base package name.
96
   *
97
   * @see PatternResourceLoader#CLASSPATH_ALL_URL_PREFIX
98
   */
99
  public void setResourcePrefix(String resourcePrefix) {
100
    Assert.notNull(resourcePrefix, "'resourcePrefix' is required");
×
101
    this.resourcePrefix = resourcePrefix;
×
102
  }
×
103

104
  public String getResourcePrefix() {
105
    return resourcePrefix;
×
106
  }
107

108
  /**
109
   * Set the {@link ResourceLoader} to use for resource locations.
110
   * This will typically be a {@link PatternResourceLoader} implementation.
111
   * <p>Default is a {@code PathMatchingPatternResourceLoader}, also capable of
112
   * resource pattern resolving through the {@code PatternResourceLoader} interface.
113
   *
114
   * @see PatternResourceLoader
115
   * @see PathMatchingPatternResourceLoader
116
   */
117
  @Override
118
  public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
119
    this.resourcePatternResolver = PatternResourceLoader.fromResourceLoader(resourceLoader);
4✔
120
    this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
6✔
121
  }
1✔
122

123
  /**
124
   * Return the ResourceLoader that this component provider uses.
125
   */
126
  public final PatternResourceLoader getResourceLoader() {
127
    PatternResourceLoader resourceLoader = this.resourcePatternResolver;
3✔
128
    if (resourceLoader == null) {
2!
129
      resourceLoader = new PathMatchingPatternResourceLoader();
×
130
      this.resourcePatternResolver = resourceLoader;
×
131
    }
132
    return resourceLoader;
2✔
133
  }
134

135
  /**
136
   * Set the {@link MetadataReaderFactory} to use.
137
   * <p>Default is a {@link CachingMetadataReaderFactory} for the specified
138
   * {@linkplain #setResourceLoader resource loader}.
139
   * <p>Call this setter method <i>after</i> {@link #setResourceLoader} in order
140
   * for the given MetadataReaderFactory to override the default factory.
141
   */
142
  public void setMetadataReaderFactory(@Nullable MetadataReaderFactory metadataReaderFactory) {
143
    this.metadataReaderFactory = metadataReaderFactory;
3✔
144
  }
1✔
145

146
  /**
147
   * Return the MetadataReaderFactory used by this component provider.
148
   */
149
  public final MetadataReaderFactory getMetadataReaderFactory() {
150
    MetadataReaderFactory metadataReaderFactory = this.metadataReaderFactory;
3✔
151
    if (metadataReaderFactory == null) {
2!
152
      metadataReaderFactory = new CachingMetadataReaderFactory(getResourceLoader());
×
153
      this.metadataReaderFactory = metadataReaderFactory;
×
154
    }
155
    return metadataReaderFactory;
2✔
156
  }
157

158
  /**
159
   * Scan the class path for candidate components.
160
   *
161
   * @param basePackage the package to check for annotated classes
162
   * @throws IOException sneaky throw from {@link PatternResourceLoader#getResources(String)}
163
   */
164
  public void scan(String basePackage, MetadataReaderConsumer metadataReaderConsumer) throws IOException {
165
    boolean traceEnabled = logger.isTraceEnabled();
4✔
166
    String packageSearchPath = getPatternLocation(basePackage);
4✔
167
    MetadataReaderFactory factory = getMetadataReaderFactory();
3✔
168

169
    getResourceLoader().scan(packageSearchPath, resource -> {
9✔
170
      String filename = resource.getName();
3✔
171
      if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
6!
172
        // Ignore CGLIB-generated classes in the classpath
173
        return;
×
174
      }
175
      if (traceEnabled) {
2!
176
        logger.trace("Scanning {}", resource);
×
177
      }
178
      try {
179
        MetadataReader metadataReader = factory.getMetadataReader(resource);
4✔
180
        metadataReaderConsumer.accept(metadataReader, factory);
4✔
181
      }
182
      catch (FileNotFoundException ex) {
×
183
        if (traceEnabled) {
×
184
          logger.trace("Ignored non-readable {}: {}", resource, ex.getMessage());
×
185
        }
186
      }
187
      catch (ClassFormatException ex) {
×
188
        if (shouldIgnoreClassFormatException) {
×
189
          logger.debug("Ignored incompatible class format in {}: {}", resource, ex.getMessage());
×
190
        }
191
        else {
192
          throw new BeanDefinitionStoreException(
×
193
                  "Incompatible class format in %s: set system property 'infra.classformat.ignore' to 'true' if you mean to ignore such files during classpath scanning"
194
                          .formatted(resource), ex);
×
195
        }
196
      }
1✔
197
    });
1✔
198
  }
1✔
199

200
  protected String getPatternLocation(String input) {
201
    return resourcePrefix + resolveBasePackage(input) + '/' + this.resourcePattern;
9✔
202
  }
203

204
  /**
205
   * Resolve the specified base package into a pattern specification for
206
   * the package search path.
207
   * <p>converts a "."-based package path to a "/"-based resource path.
208
   *
209
   * @param basePackage the base package as specified by the user
210
   * @return the pattern specification to be used for package searching
211
   */
212
  protected String resolveBasePackage(String basePackage) {
213
    return ClassUtils.convertClassNameToResourcePath(basePackage);
×
214
  }
215

216
  /**
217
   * Clear the local metadata cache, if any, removing all cached class metadata.
218
   */
219
  public void clearCache() {
220
    if (metadataReaderFactory instanceof CachingMetadataReaderFactory caching) {
9!
221
      // Clear cache in externally provided MetadataReaderFactory; this is a no-op
222
      // for a shared cache since it'll be cleared by the ApplicationContext.
223
      caching.clearCache();
2✔
224
    }
225
  }
1✔
226

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