I happened to remember that the newly released Spring 2.5 integrates a similar behavior. I peeked into the code and found that ClassPathScanningCandidateComponentProvider was responsible for that part. The key method is findCandidateComponents, which returns a set of bean definitions.
Here's a method based on ClassPathScanningCandidateComponentProvider that loads and returns the classes it finds:
public Set<Class<?>> scanClassPath(String basePackage, TypeFilter includeFilter, TypeFilter excludeFilter) throws Exception { Set<Class<?>> result = new HashSet<Class<?>>(); ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; if (basePackage != null) { packageSearchPath += ClassUtils .convertClassNameToResourcePath(basePackage); } packageSearchPath += "/**/*.class"; MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory( resourcePatternResolver); Resource[] resources = resourcePatternResolver .getResources(packageSearchPath); for (Resource resource : resources) { MetadataReader metadataReader = metadataReaderFactory .getMetadataReader(resource); ClassMetadata classMetadata = metadataReader.getClassMetadata(); if (includeFilter != null && !includeFilter.match(metadataReader, metadataReaderFactory)) { continue; } if (excludeFilter != null && excludeFilter.match(metadataReader, metadataReaderFactory)) { continue; } Class<?> class1 = Class. forName(classMetadata.getClassName()); result.add(class1); } return result; }Advantages:
- Class files are treated as plain files, no loading occurs before a class is needed.
- MetadataReaders provide useful information about the scanned classes without loading them, including the name of the class, whether it is concrete or abstract, the name of its super class and the implemented interfaces and even information about its annotations.
- TypeFilters can be used to include or exclude classes. Spring 2.5 provides a couple of implementations.
result = new ClassPathScanningCandidateComponentProvider(false){ { addIncludeFilter( new AssignableTypeFilter(A.class)); addExcludeFilter( new AssignableTypeFilter(B.class)); } Set<Class<?>> scan(String basePackage) { Set<Class<?>> classes = new HashSet<Class<?>>(); Set<BeanDefinition> beanDefinitions = findCandidateComponents(basePackage); for (BeanDefinition def : beanDefinitions) { try { classes.add( Class.forName(def.getBeanClassName())); } catch (ClassNotFoundException e) {} } return classes; } }.scan("my.base.package");