News:三分天注定,七分靠打拼,爱拼才会赢!致力打造专业IT博客。如果你对本博客有任何意见或建议请联系作者,邮箱:blog@caokuan.cn

Java 扫描特定注解工具类

逝水无痕 435 0 条

在日常开发中我们为了处理某类特定的业务会自定义注解,然后在类上加上注解来标识这个类是我们需要处理的。那么相应的问题就来了:如何找到这些带有特定注解的类?这就用到了类扫描工具。在项目启动的时候扫描指定的包及其子包,或者在其他时候扫描指定的包及其子包,然后将扫描结果缓存下来以备使用。下面就将我参考 CXF 框架写的类扫描工具分享出来供大家使用。源码下载:ClassScanner.java

javascanner.jpg

基于 Spring 框架的注解扫描工具类

package cn.caokuan.test;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

/**
 * 说明:注解扫描工具类(基于Spring框架实现)
 * 作者:caokk
 */
public class ClassScanner {
    
    private static final String ALL_FILES = "**/*";
    private static final String ALL_CLASS_FILES = ALL_FILES + ".class";
    private static final String WILDCARD = "*";
    
    @SuppressWarnings("unchecked")
    public static Map<Class<? extends Annotation>, Collection<Class<?>>> scan(String basePackage,
            Class<? extends Annotation>... annotations) throws IOException, ClassNotFoundException {
        return findClasses(parsePackages(basePackage), Collections.unmodifiableList(Arrays.asList(annotations)), null);
    }
    
    private static Set<String> parsePackages(final String packagesAsCsv) {        
        final String[] values = StringUtils.split(packagesAsCsv, ",");
        final Set<String> basePackages = new HashSet<String>(values.length);
        for (final String value : values) {
            final String trimmed = value.trim();
            if (trimmed.equals(WILDCARD)) {
                basePackages.clear();
                basePackages.add(trimmed);
                break;
            } else if (trimmed.length() > 0) {
                basePackages.add(trimmed);
            }
        }
        return basePackages;
    }

    private static Map<Class<? extends Annotation>, Collection<Class<?>>> findClasses(Collection<String> basePackages,
            List<Class<? extends Annotation>> annotations, ClassLoader loader)
            throws IOException, ClassNotFoundException {

        ResourcePatternResolver resolver = getResolver(loader);
        MetadataReaderFactory factory = new CachingMetadataReaderFactory(resolver);

        final Map<Class<? extends Annotation>, Collection<Class<?>>> classes = new HashMap<Class<? extends Annotation>, Collection<Class<?>>>();
        final Map<Class<? extends Annotation>, Collection<String>> matchingInterfaces = new HashMap<Class<? extends Annotation>, Collection<String>>();
        final Map<String, String[]> nonMatchingClasses = new HashMap<String, String[]>();

        for (Class<? extends Annotation> annotation : annotations) {
            classes.put(annotation, new HashSet<Class<?>>());
            matchingInterfaces.put(annotation, new HashSet<String>());
        }

        if (basePackages == null || basePackages.isEmpty()) {
            return classes;
        }

        for (final String basePackage : basePackages) {
            final boolean scanAllPackages = basePackage.equals(WILDCARD);
            final String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                    + (scanAllPackages ? "" : ClassUtils.convertClassNameToResourcePath(basePackage)) + ALL_CLASS_FILES;

            final Resource[] resources = resolver.getResources(packageSearchPath);

            for (final Resource resource : resources) {
                final MetadataReader reader = factory.getMetadataReader(resource);
                final AnnotationMetadata metadata = reader.getAnnotationMetadata();

                for (Class<? extends Annotation> annotation : annotations) {
                    boolean concreteClass = !metadata.isInterface() && !metadata.isAbstract();
                    if (metadata.isAnnotated(annotation.getName())) {
                        if (concreteClass) {
                            classes.get(annotation).add(loadClass(metadata.getClassName(), loader));
                        } else {
                            matchingInterfaces.get(annotation).add(metadata.getClassName());
                        }
                    } else if (concreteClass && metadata.getInterfaceNames().length > 0) {
                        nonMatchingClasses.put(metadata.getClassName(), metadata.getInterfaceNames());
                    }
                }
            }
        }
        if (!nonMatchingClasses.isEmpty()) {
            for (Map.Entry<Class<? extends Annotation>, Collection<String>> e1 : matchingInterfaces.entrySet()) {
                for (Map.Entry<String, String[]> e2 : nonMatchingClasses.entrySet()) {
                    for (String intName : e2.getValue()) {
                        if (e1.getValue().contains(intName)) {
                            classes.get(e1.getKey()).add(loadClass(e2.getKey(), loader));
                            break;
                        }
                    }
                }
            }
        }
        for (Map.Entry<Class<? extends Annotation>, Collection<String>> e : matchingInterfaces.entrySet()) {
            if (classes.get(e.getKey()).isEmpty()) {
                for (String intName : e.getValue()) {
                    classes.get(e.getKey()).add(loadClass(intName, loader));
                }
            }
        }

        return classes;
    }

    private static ResourcePatternResolver getResolver(ClassLoader loader) {
        return loader != null ? new PathMatchingResourcePatternResolver(loader)
                : new PathMatchingResourcePatternResolver();
    }
    
    private static Class<?> loadClass(String className, ClassLoader loader) throws ClassNotFoundException {
        if (loader == null) {
            try {
                ClassLoader cl = getContextClassLoader();
                if (cl != null) {
                    return cl.loadClass(className);
                }            
            } catch (ClassNotFoundException e) {
            }
            return loadClass2(className, ClassScanner.class);
        } else {
            return loader.loadClass(className);
        }
    }
    
    private static ClassLoader getContextClassLoader() {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
                public ClassLoader run() {
                    ClassLoader loader = Thread.currentThread().getContextClassLoader();
                    return loader != null ? loader : ClassLoader.getSystemClassLoader();
                }
            });
        } 
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        return loader != null ? loader : ClassLoader.getSystemClassLoader();
    }
    
    private static Class<?> loadClass2(String className, Class<?> callingClass) throws ClassNotFoundException {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException ex) {
            final ClassLoader loader = getClassLoader(callingClass);
            if (loader != null) {
                return loader.loadClass(className);
            } else {
                throw ex;
            }
        }
    }
    
    private static ClassLoader getClassLoader(final Class<?> clazz) {
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
                public ClassLoader run() {
                    return clazz.getClassLoader();
                }
            });
        }
        return clazz.getClassLoader();
    }
}

工具参考

org.apache.cxf.common.util.ClasspathScanner
org.apache.cxf.common.util.SpringClasspathScanner

应用示例

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IOException {
        Map<?, ?> scan = ClassScanner.scan("cn.caokuan", TestScan.class);
        System.out.println(scan);
    }
}

输出结果为 cn.caokuan 包中有 @TestScan 注解的类

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, IOException {
        Map<?, ?> scan = ClassScanner.scan("cn.caokuan.", TestScan.class);
        System.out.println(scan);
    }
}

输出结果为 cn.caokuan 包及其子包中有 @TestScan 注解的类
发表我的评论
icon_mrgreen.gificon_neutral.gificon_twisted.gificon_arrow.gificon_eek.gificon_smile.gificon_confused.gificon_cool.gificon_evil.gificon_biggrin.gificon_idea.gificon_redface.gificon_razz.gificon_rolleyes.gificon_wink.gificon_cry.gificon_surprised.gificon_lol.gificon_mad.gificon_sad.gificon_exclaim.gificon_question.gif

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址