Asm是很好的ByteCode generator 和 ByteCode reader。Asm提供了ClassVisitor来访问Class中的每个元素。当用ClassReader来读取Class的字节码时,每read一个元素,ASM会调用指定的ClassVisitor来访问这个元素。这就是访问者模式。利用这个特点,当ClassVisitor访问Class的Annotation元素时,我们会把annotation的信息记录下来。这样就可以在将来使用这个Annotation。
举一个例子吧,我们用AnnotationFaker来标注一个Class,也就是Annotation的target是Type级别。当我们发现某个class是用AnnotationFaker标注的,我们就load这个class到jvm中,并初始化,否则免谈。
1.AnnotationFaker: annnotation用来标注需要初始化的class
 1 package com.oocl.isdc.sha.frm.test.config;
package com.oocl.isdc.sha.frm.test.config;
 2
 3 import java.lang.annotation.ElementType;
import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
import java.lang.annotation.Target;
 7
 8 @Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
 9
 @Target(
@Target( {ElementType.TYPE})
{ElementType.TYPE})
10
 public @interface AnnotationFaker
public @interface AnnotationFaker  {
{
11 }
}
12
 
2.ClassFaker: 被AnnotationFaker标注的class
1 package com.oocl.isdc.sha.frm.test.config;
package com.oocl.isdc.sha.frm.test.config;
2
3 @AnnotationFaker
@AnnotationFaker
4
 public class ClassFaker
public class ClassFaker  {
{
5
 public void hello()
    public void hello()  {
{
6 System.out.println("hello world, load me success!");
        System.out.println("hello world, load me success!");
7 }
    }
8 }
}
9
 
3.
ClassVisitorFaker:ClassVisitor的一个实现,用来得到class上的Annotation
 1 package com.oocl.isdc.sha.frm.test.config;
package com.oocl.isdc.sha.frm.test.config;
 2
 3 import java.util.ArrayList;
import java.util.ArrayList;
 4 import java.util.List;
import java.util.List;
 5
 6 import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.AnnotationVisitor;
 7 import org.objectweb.asm.Attribute;
import org.objectweb.asm.Attribute;
 8 import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassVisitor;
 9 import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.FieldVisitor;
10 import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.MethodVisitor;
11 import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.AnnotationNode;
12
13
 public class ClassVisitorFaker implements ClassVisitor
public class ClassVisitorFaker implements ClassVisitor {
{
14
15 public List<AnnotationNode> visibleAnnotations;
    public List<AnnotationNode> visibleAnnotations;
16
17 public List<AnnotationNode> invisibleAnnotations;
    public List<AnnotationNode> invisibleAnnotations;
18 
    
19
 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)  {
{
20 
      
21 }
    }
22
23
 public AnnotationVisitor visitAnnotation(String desc, boolean visible)
    public AnnotationVisitor visitAnnotation(String desc, boolean visible)  {
{
24 AnnotationNode an = new AnnotationNode(desc);
        AnnotationNode an = new AnnotationNode(desc);
25
 if (visible)
        if (visible)  {
{
26
 if (visibleAnnotations == null)
            if (visibleAnnotations == null)  {
{
27 visibleAnnotations = new ArrayList<AnnotationNode> (1);
                visibleAnnotations = new ArrayList<AnnotationNode> (1);
28 }
            }
29 visibleAnnotations.add(an);
            visibleAnnotations.add(an);
30
 } else
        } else  {
{
31
 if (invisibleAnnotations == null)
            if (invisibleAnnotations == null)  {
{
32 invisibleAnnotations = new ArrayList<AnnotationNode> (1);
                invisibleAnnotations = new ArrayList<AnnotationNode> (1);
33 }
            }
34 invisibleAnnotations.add(an);
            invisibleAnnotations.add(an);
35 }
        }
36 return an;
        return an;
37 }
    }
38
39
 public void visitAttribute(Attribute attr)
    public void visitAttribute(Attribute attr)  {
{
40 }
    }
41
42
 public void visitEnd()
    public void visitEnd()  {
{
43 }
    }
44
45
 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value)
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value)  {
{
46 return null;
        return null;
47 }
    }
48
49
 public void visitInnerClass(String name, String outerName, String innerName, int access)
    public void visitInnerClass(String name, String outerName, String innerName, int access)  {
{
50 }
    }
51
52
 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)  {
{
53 return null;
        return null;
54 }
    }
55
56
 public void visitOuterClass(String owner, String name, String desc)
    public void visitOuterClass(String owner, String name, String desc)  {
{
57 }
    }
58
59
 public void visitSource(String source, String debug)
    public void visitSource(String source, String debug)  {
{
60 }
    }
61
62
 public List<AnnotationNode> getVisibleAnnotations()
    public List<AnnotationNode> getVisibleAnnotations()  {
{
63 return visibleAnnotations;
        return visibleAnnotations;
64 }
    }
65
66
 public List<AnnotationNode> getInvisibleAnnotations()
    public List<AnnotationNode> getInvisibleAnnotations()  {
{
67 return invisibleAnnotations;
        return invisibleAnnotations;
68 }
    }
69 }
}
70
 
4.
ClassParser: main class, 分析classpath上的class,如果是用AnnotationFaker标注的class,我们就初始化它。
  1 package com.oocl.isdc.sha.frm.test.config;
package com.oocl.isdc.sha.frm.test.config;
  2
  3 import java.io.File;
import java.io.File;
  4 import java.io.IOException;
import java.io.IOException;
  5 import java.net.URISyntaxException;
import java.net.URISyntaxException;
  6 import java.net.URL;
import java.net.URL;
  7 import java.net.URLClassLoader;
import java.net.URLClassLoader;
  8 import java.net.URLDecoder;
import java.net.URLDecoder;
  9 import java.util.Enumeration;
import java.util.Enumeration;
 10 import java.util.HashSet;
import java.util.HashSet;
 11 import java.util.Iterator;
import java.util.Iterator;
 12 import java.util.List;
import java.util.List;
 13 import java.util.Set;
import java.util.Set;
 14 import java.util.zip.ZipEntry;
import java.util.zip.ZipEntry;
 15 import java.util.zip.ZipException;
import java.util.zip.ZipException;
 16 import java.util.zip.ZipFile;
import java.util.zip.ZipFile;
 17
 18 import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassReader;
 19 import org.objectweb.asm.Type;
import org.objectweb.asm.Type;
 20 import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.AnnotationNode;
 21
 22
 public class ClassParser
public class ClassParser  {
{
 23 @SuppressWarnings("unchecked")
    @SuppressWarnings("unchecked")
 24 public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException,
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException,
 25
 IllegalAccessException, URISyntaxException
            IllegalAccessException, URISyntaxException  {
{
 26 Set<String> result = new HashSet<String>();
        Set<String> result = new HashSet<String>();
 27
 28
 if (getClassLoader() instanceof URLClassLoader)
        if (getClassLoader() instanceof URLClassLoader)  {
{
 29 URL[] urls = ((URLClassLoader) getClassLoader()).getURLs();
            URL[] urls = ((URLClassLoader) getClassLoader()).getURLs();
 30
 for (URL u : urls)
            for (URL u : urls)  {
{
 31 File file = new File(u.toURI());
                File file = new File(u.toURI());
 32
 if (file.isDirectory())
                if (file.isDirectory())  {
{
 33 handleDirectory(result, file, null);
                    handleDirectory(result, file, null);
 34
 } else if (file.getName().toLowerCase().endsWith(".jar"))
                } else if (file.getName().toLowerCase().endsWith(".jar"))  {
{
 35 handleArchive(result, file);
                    handleArchive(result, file);
 36 }
                }
 37 }
            }
 38
 }else
        }else {
{
 39 Enumeration<URL> urls = getClassLoader().getResources(".");
            Enumeration<URL> urls = getClassLoader().getResources(".");
 40
 while (urls.hasMoreElements())
            while (urls.hasMoreElements())  {
{
 41 String urlPath = urls.nextElement().getFile();
                String urlPath = urls.nextElement().getFile();
 42 // System.out.println(urlPath);
                // System.out.println(urlPath);
 43 urlPath = URLDecoder.decode(urlPath, "UTF-8");
                urlPath = URLDecoder.decode(urlPath, "UTF-8");
 44
 if (urlPath.startsWith("file:"))
                if (urlPath.startsWith("file:"))  {
{
 45 urlPath = urlPath.substring(5);
                    urlPath = urlPath.substring(5);
 46 }
                }
 47
 if (urlPath.indexOf('!') > 0)
                if (urlPath.indexOf('!') > 0)  {
{
 48 urlPath = urlPath.substring(0, urlPath.indexOf('!'));
                    urlPath = urlPath.substring(0, urlPath.indexOf('!'));
 49 }
                }
 50
 51 final File file = new File(urlPath);
                final File file = new File(urlPath);
 52
 if (file.isDirectory())
                if (file.isDirectory())  {
{
 53 handleDirectory(result, file, null);
                    handleDirectory(result, file, null);
 54 }
                }
 55
 56 }
            }
 57 }
        }
 58
 59 
     
 60
 61
 for (String clsRsName : result)
        for (String clsRsName : result)  {
{
 62 ClassVisitorFaker cv = new ClassVisitorFaker();
            ClassVisitorFaker cv = new ClassVisitorFaker();
 63 ClassReader cr = new ClassReader(getClassLoader().getResourceAsStream(clsRsName));
            ClassReader cr = new ClassReader(getClassLoader().getResourceAsStream(clsRsName));
 64 cr.accept(cv, 0);
            cr.accept(cv, 0);
 65 List<AnnotationNode> annotationsList = cv.getVisibleAnnotations();
            List<AnnotationNode> annotationsList = cv.getVisibleAnnotations();
 66
 if (null != annotationsList)
            if (null != annotationsList)  {
{
 67
 for (Iterator<AnnotationNode> it = annotationsList.iterator(); it.hasNext();)
                for (Iterator<AnnotationNode> it = annotationsList.iterator(); it.hasNext();)  {
{
 68 AnnotationNode annotation = it.next();
                    AnnotationNode annotation = it.next();
 69 Type t = Type.getType(annotation.desc);
                    Type t = Type.getType(annotation.desc);
 70
 if (AnnotationFaker.class.getName().equals(t.getClassName()))
                    if (AnnotationFaker.class.getName().equals(t.getClassName()))  {
{
 71 Class clazz = Class.forName(filenameToClassname(clsRsName));
                        Class clazz = Class.forName(filenameToClassname(clsRsName));
 72 ClassFaker faker = (ClassFaker) clazz.newInstance();
                        ClassFaker faker = (ClassFaker) clazz.newInstance();
 73 faker.hello();
                        faker.hello();
 74 }
                    }
 75 }
                }
 76 }
            }
 77
 78 }
        }
 79
 80 }
    }
 81
 82 private static void handleDirectory(final Set<String> result, final File file, final String path)
    private static void handleDirectory(final Set<String> result, final File file, final String path)
 83
 throws ZipException, IOException
            throws ZipException, IOException  {
{
 84
 for (final File child : file.listFiles())
        for (final File child : file.listFiles())  {
{
 85 final String newPath = path == null ? child.getName() : path + '/' + child.getName();
            final String newPath = path == null ? child.getName() : path + '/' + child.getName();
 86
 if (child.isDirectory())
            if (child.isDirectory())  {
{
 87 handleDirectory(result, child, newPath);
                handleDirectory(result, child, newPath);
 88
 } else if (child.getName().toLowerCase().endsWith(".jar"))
            } else if (child.getName().toLowerCase().endsWith(".jar"))  {
{
 89 handleArchive(result, child);
                handleArchive(result, child);
 90
 } else
            } else  {
{
 91 handleItem(result, newPath);
                handleItem(result, newPath);
 92 }
            }
 93 }
        }
 94 }
    }
 95
 96
 private static void handleItem(final Set<String> result, final String name)
    private static void handleItem(final Set<String> result, final String name)  {
{
 97
 if (name.endsWith(".class"))
        if (name.endsWith(".class"))  {
{
 98 result.add(name);
            result.add(name);
 99 }
        }
100 }
    }
101
102
 private static void handleArchive(final Set<String> result, final File file) throws ZipException, IOException
    private static void handleArchive(final Set<String> result, final File file) throws ZipException, IOException  {
{
103 final ZipFile zip = new ZipFile(file);
        final ZipFile zip = new ZipFile(file);
104 final Enumeration<? extends ZipEntry> entries = zip.entries();
        final Enumeration<? extends ZipEntry> entries = zip.entries();
105
 while (entries.hasMoreElements())
        while (entries.hasMoreElements())  {
{
106 final ZipEntry entry = entries.nextElement();
            final ZipEntry entry = entries.nextElement();
107 final String name = entry.getName();
            final String name = entry.getName();
108 handleItem(result, name);
            handleItem(result, name);
109 }
        }
110 }
    }
111
112
 private static ClassLoader getClassLoader()
    private static ClassLoader getClassLoader()  {
{
113 ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();
        ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();
114
 if (clsLoader == null)
        if (clsLoader == null)  {
{
115 clsLoader = ClassLoader.getSystemClassLoader();
            clsLoader = ClassLoader.getSystemClassLoader();
116 }
        }
117 return clsLoader;
        return clsLoader;
118 }
    }
119
120
 public static String filenameToClassname(final String filename)
    public static String filenameToClassname(final String filename)  {
{
121 return filename.substring(0, filename.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
        return filename.substring(0, filename.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
122 }
    }
123
124 }
}
125