spring扫描classpath下特定package,并加载具有特定注解的接口。
在框架平台的开发中,通常有很多的情况通过spring配置方式来实现某些功能会使得框架平台难以使用和扩展,我们通常的做法是让业务用户实现特定的接口或者打上特定的注解标记,框架平台根据事先约定的注解或者接口对业务类进行处理以达到预期的目标。
借助于spring提供的工具类,我们可以通过如下方式实现(以扫描标注了@ServiceModule注解的接口为例):
private static ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
static final Logger logger = LoggerFactory.getLogger(RpcClassScanner.class);
private static final String RESOURCE_PATTERN = "/**/*.class";
private static Set<Class<?>> classSet = new HashSet<Class<?>>();
private static TypeFilter typeFilter = new AnnotationTypeFilter(ServiceModule.class, false,true);
/**
* 将符合条件的Bean以Class集合的形式返回
*
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Set<Class<?>> getClassSet(List<String> packagesToScan) throws IOException, ClassNotFoundException {
classSet.clear();
if (!packagesToScan.isEmpty()) {
for (String pkg : packagesToScan) {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(pkg)
+ RESOURCE_PATTERN;
Resource[] resources = resourcePatternResolver.getResources(pattern);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader reader = readerFactory.getMetadataReader(resource);
String className = reader.getClassMetadata().getClassName();
if (matchesEntityTypeFilter(reader, readerFactory)) {
classSet.add(Class.forName(className));
logger.info("找到自动发布或代理接口:" + className);
}
}
}
}
}
return classSet;
}
private static boolean matchesEntityTypeFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
if (typeFilter.match(reader, readerFactory)) {
return true;
}
return false;
}