Escaneando anotaciones con Spring

Hola Amigos, acá comparto un poco de código que escribí esta semana.

El Problema

Esta semana estaba trabajando con AMF, mi problema era listar las classes que estaban anotadas con la anotación @RemotingDestination, para luego poder listarlas en una consola. (Con el objetivode mostrar los servicion AMF disponibles en el servidor)

Aca un ejemplo de una clase anotada con @RemotingDestination

package some.good.packague.to.work; // 🙂

import org.springframework.flex.remoting.RemotingDestination;
import org.springframework.stereotype.Service;

@Service("userService")
@RemotingDestination
public class UserService{

    public String getUserName(String userId) {
        .....
        return "The best name in the world :)";
    }

}

La Solución

Primeramente estaba viendo Javassist, pero al final me quede con una solución con Spring.

El ejemplo lo encontré el blog de Java Chimaera, pero le hice algunas modificaciones para que ahora soporte buscar clases tanto en linux como en windows.

El Código

package yep.this.is.a.very.good.package;

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.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;

import java.util.Set;
import java.util.HashSet;
import java.io.IOException;

/**
 * This class is a adaptation of:
 *
 *    http://java-chimaera.blogspot.com/2008/10/scanning-classpath-annotated-classes.html
 */
/**
 * @author Renan Huanca
 * @since Apr 16, 2010 9:52:12 PM
 */
public class AnnotatedClassFinder {
    private String basePackage;
    private ResourcePatternResolver resourceResolver = null;
    private MetadataReaderFactory metadataReaderFactory = null;
    private TypeFilter annotationFilter = null;
    
    public AnnotatedClassFinder(String basePackage) {
        this.basePackage = basePackage;
        this.resourceResolver = new PathMatchingResourcePatternResolver(
                Thread.currentThread().getContextClassLoader());
        this.metadataReaderFactory = new SimpleMetadataReaderFactory();
    }

    public static AnnotatedClassFinder getInstance(String basePackage){
        return new AnnotatedClassFinder(basePackage);
    }

    public Set findByAnnotation(Class annotation) {
        this.annotationFilter = new AnnotationTypeFilter(annotation);
        Set annotatedClasses = new HashSet();
        /*
        * First of all we load all resources that are under a specific package by using a ResourcePatternResolver.
        * By doing so we will use class files as simple resources without passing
        * by the ClassLoader which can for example cause the execution of a static initialization block.
        * It resolves also transparently resources in jars.
        */
        String candidateClassesLocationPattern = "classpath*:" + basePackage + "/**/*.class";
        Resource[] resources = null;
        try {
            resources = resourceResolver.getResources(candidateClassesLocationPattern);
        } catch (IOException e) {
            throw new RuntimeException(
                    "An I/O problem occurs when trying to resolve ressources matching the pattern : "
                            + candidateClassesLocationPattern, e);
        }

        /*
        * then we proceed resource by resource, using a MetadataReaderFactory to create
        * MetadataReader wich hides the ASM related interface and complexity.
        *
        */
        for (Resource resource : resources) {
            MetadataReader metadataReader = null;
            try {
                metadataReader = this.metadataReaderFactory.getMetadataReader(resource);

                if (this.annotationFilter.match(metadataReader, metadataReaderFactory)) {

                    /*
                    * the AnnotationMetadata is a simple abstaction of the informations
                    * that holds the annotation
                    */
                    String className = convertResourceToClassName(resource, basePackage);
                    try {
                        annotatedClasses.add(ClassUtils.forName(className));
                    } catch (Exception e) {
                        throw new RuntimeException("problems occurs when trying to load the annotated class : " + className, e);
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException("An I/O problem occurs when trying to process resource : " + resource, e);
            }
        }

        return annotatedClasses;
    }

    static String convertResourceToClassName(Resource resource, String basePackage) throws IOException {
        String path = resource.getFile().getPath();
        String pathWithoutSuffix = path.substring(0, path.length() - ClassUtils.CLASS_FILE_SUFFIX.length());
        String relativePathWithoutSuffix = "";
        if(System.getProperty("file.separator").equals("\\")) {
            relativePathWithoutSuffix = pathWithoutSuffix.substring(pathWithoutSuffix.indexOf(basePackage.replace('/', '\\')));
            relativePathWithoutSuffix = relativePathWithoutSuffix.replace('\\', '/');
        } else if(System.getProperty("file.separator").equals("//")) {
            relativePathWithoutSuffix = pathWithoutSuffix.substring(pathWithoutSuffix.indexOf(basePackage));
        } else {
            throw new RuntimeException("File separator is not recognized");
        }

        // taking out extra \ or /
        relativePathWithoutSuffix = relativePathWithoutSuffix.substring(1);

        return relativePathWithoutSuffix.replace('/', '.');
    }
}

Como llamarlo?

AnnotatedClassFinder finder = AnnotatedClassFinder.getInstance("/the/package/you/want/to/search");
Set classes = finder.findByAnnotation(RemotingDestination.class);

2 thoughts on “Escaneando anotaciones con Spring

Leave a Reply

Your email address will not be published. Required fields are marked *