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

   项目中需要用到包扫描的情况是很多的,一般是在项目初始化的时候,根据一些条件来对某个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文件

转载于:https://my.oschina.net/u/2315939/blog/548096

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值