您的位置:首页 > 编程语言 > Java开发

利用spring,实现package下的类扫描

2015-12-20 00:00 232 查看
项目中需要用到包扫描的情况是很多的,一般是在项目初始化的时候,根据一些条件来对某个package下的类进行特殊处理。现在想实现的功能是,在一个filter或interceptor初始化的时候,扫描指定的一些package路径,遍历下面的每个class,找出method上使用了一个特殊注解的所有方法,然后缓存起来,当方法拦截器工作的时候,就不用再挨个判断方法是否需要拦截了

网上有很多自己编码实现scan package功能的例子,但是如果有工具已经帮你实现了,并且经受了普遍的验证,那么,自己造轮子的必要性就不大了
spring框架中有扫描包的类ClassPathBeanDefinitionScanner 里面的findCandidateComponents方法是我们进行改造的依据

/**
* Scan the class path for candidate components.
* @param basePackage the package to check for annotated classes
* @return a corresponding Set of autodetected bean definitions
*/
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + this.resourcePattern;
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}


改造如下:

方法loadCheckClassMethods的入参是逗号分隔的包路径,如com.xx

利用Spring的

ResourcePatternResolver来寻找包下面的资源Resource,因为我们的扫描pattern是.class文件,所以这里的Resource就是class文件

protected static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

/**
* 根据扫描包的配置
* 加载需要检查的方法
*/
private void loadCheckClassMethods(String scanPackages) {
String[] scanPackageArr = scanPackages.split(",");
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (String basePackage : scanPackageArr) {
if (StringUtils.isBlank(basePackage)) {
continue;
}
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage)) + "/" + DEFAULT_RESOURCE_PATTERN;
try {
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
for (Resource resource : resources) {
//检查resource,这里的resource都是class
loadClassMethod(metadataReaderFactory, resource);
}
} catch (Exception e) {
log.error("初始化SensitiveWordInterceptor失败", e);
}

}
}

/**
* 加载资源,判断里面的方法
*
* @param metadataReaderFactory spring中用来读取resource为class的工具
* @param resource              这里的资源就是一个Class
* @throws IOException
*/
private void loadClassMethod(MetadataReaderFactory metadataReaderFactory, Resource resource) throws IOException {
try {
if (resource.isReadable()) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
if (metadataReader != null) {
String className = metadataReader.getClassMetadata().getClassName();
try {
tryCacheMethod(className);
} catch (ClassNotFoundException e) {
log.error("检查" + className + "是否含有需要信息失败", e);
}
}
}
} catch (Exception e) {
log.error("判断类中的方法实现需要检测xxx失败", e);
}
}

/**
* 把action下面的所有method遍历一次,标记他们是否需要进行xxx验证
* 如果需要,放入cache中
*
* @param fullClassName
*/
private void tryCacheMethod(String fullClassName) throws ClassNotFoundException {
Class<?> clz = Class.forName(fullClassName);
Method[] methods = clz.getDeclaredMethods();
for (Method method : methods) {
if (method.getModifiers() != Modifier.PUBLIC) {
continue;
}
if (CheckXXX.class.isAssignableFrom(CHECK_ANNOTATION)) {
CheckXXX checkXXX = (CheckXXX) method.getAnnotation(CHECK_ANNOTATION);
if (checkXXX != null && checkXXX.check()) {
cache.put(fullClassName + "." + method.getName(), checkXXX);
log.info("检测到需要检查xxx的方法:" + fullClassName + "." + method.getName());
}
}
}
}


tryCacheMethod做的事就是缓存需要处理的public方法

经测试,这种方式可以取到web的class文件和jar包中的class文件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring package scan