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
1package com.oocl.isdc.sha.frm.test.config;
2
3import java.lang.annotation.ElementType;
4import java.lang.annotation.Retention;
5import java.lang.annotation.RetentionPolicy;
6import java.lang.annotation.Target;
7
8@Retention(RetentionPolicy.RUNTIME)
9@Target({ElementType.TYPE})
10public @interface AnnotationFaker {
11}
12
2.ClassFaker: 被AnnotationFaker标注的class
1package com.oocl.isdc.sha.frm.test.config;
2
3@AnnotationFaker
4public class ClassFaker {
5 public void hello() {
6 System.out.println("hello world, load me success!");
7 }
8}
9
3.
ClassVisitorFaker:ClassVisitor的一个实现,用来得到class上的Annotation
1package com.oocl.isdc.sha.frm.test.config;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import org.objectweb.asm.AnnotationVisitor;
7import org.objectweb.asm.Attribute;
8import org.objectweb.asm.ClassVisitor;
9import org.objectweb.asm.FieldVisitor;
10import org.objectweb.asm.MethodVisitor;
11import org.objectweb.asm.tree.AnnotationNode;
12
13public class ClassVisitorFaker implements ClassVisitor{
14
15 public List<AnnotationNode> visibleAnnotations;
16
17 public List<AnnotationNode> invisibleAnnotations;
18
19 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) {
24 AnnotationNode an = new AnnotationNode(desc);
25 if (visible) {
26 if (visibleAnnotations == null) {
27 visibleAnnotations = new ArrayList<AnnotationNode> (1);
28 }
29 visibleAnnotations.add(an);
30 } else {
31 if (invisibleAnnotations == null) {
32 invisibleAnnotations = new ArrayList<AnnotationNode> (1);
33 }
34 invisibleAnnotations.add(an);
35 }
36 return an;
37 }
38
39 public void visitAttribute(Attribute attr) {
40 }
41
42 public void visitEnd() {
43 }
44
45 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
46 return null;
47 }
48
49 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) {
53 return null;
54 }
55
56 public void visitOuterClass(String owner, String name, String desc) {
57 }
58
59 public void visitSource(String source, String debug) {
60 }
61
62 public List<AnnotationNode> getVisibleAnnotations() {
63 return visibleAnnotations;
64 }
65
66 public List<AnnotationNode> getInvisibleAnnotations() {
67 return invisibleAnnotations;
68 }
69}
70
4.
ClassParser: main class, 分析classpath上的class,如果是用AnnotationFaker标注的class,我们就初始化它。
1package com.oocl.isdc.sha.frm.test.config;
2
3import java.io.File;
4import java.io.IOException;
5import java.net.URISyntaxException;
6import java.net.URL;
7import java.net.URLClassLoader;
8import java.net.URLDecoder;
9import java.util.Enumeration;
10import java.util.HashSet;
11import java.util.Iterator;
12import java.util.List;
13import java.util.Set;
14import java.util.zip.ZipEntry;
15import java.util.zip.ZipException;
16import java.util.zip.ZipFile;
17
18import org.objectweb.asm.ClassReader;
19import org.objectweb.asm.Type;
20import org.objectweb.asm.tree.AnnotationNode;
21
22public class ClassParser {
23 @SuppressWarnings("unchecked")
24 public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException,
25 IllegalAccessException, URISyntaxException {
26 Set<String> result = new HashSet<String>();
27
28 if (getClassLoader() instanceof URLClassLoader) {
29 URL[] urls = ((URLClassLoader) getClassLoader()).getURLs();
30 for (URL u : urls) {
31 File file = new File(u.toURI());
32 if (file.isDirectory()) {
33 handleDirectory(result, file, null);
34 } else if (file.getName().toLowerCase().endsWith(".jar")) {
35 handleArchive(result, file);
36 }
37 }
38 }else{
39 Enumeration<URL> urls = getClassLoader().getResources(".");
40 while (urls.hasMoreElements()) {
41 String urlPath = urls.nextElement().getFile();
42 // System.out.println(urlPath);
43 urlPath = URLDecoder.decode(urlPath, "UTF-8");
44 if (urlPath.startsWith("file:")) {
45 urlPath = urlPath.substring(5);
46 }
47 if (urlPath.indexOf('!') > 0) {
48 urlPath = urlPath.substring(0, urlPath.indexOf('!'));
49 }
50
51 final File file = new File(urlPath);
52 if (file.isDirectory()) {
53 handleDirectory(result, file, null);
54 }
55
56 }
57 }
58
59
60
61 for (String clsRsName : result) {
62 ClassVisitorFaker cv = new ClassVisitorFaker();
63 ClassReader cr = new ClassReader(getClassLoader().getResourceAsStream(clsRsName));
64 cr.accept(cv, 0);
65 List<AnnotationNode> annotationsList = cv.getVisibleAnnotations();
66 if (null != annotationsList) {
67 for (Iterator<AnnotationNode> it = annotationsList.iterator(); it.hasNext();) {
68 AnnotationNode annotation = it.next();
69 Type t = Type.getType(annotation.desc);
70 if (AnnotationFaker.class.getName().equals(t.getClassName())) {
71 Class clazz = Class.forName(filenameToClassname(clsRsName));
72 ClassFaker faker = (ClassFaker) clazz.newInstance();
73 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)
83 throws ZipException, IOException {
84 for (final File child : file.listFiles()) {
85 final String newPath = path == null ? child.getName() : path + '/' + child.getName();
86 if (child.isDirectory()) {
87 handleDirectory(result, child, newPath);
88 } else if (child.getName().toLowerCase().endsWith(".jar")) {
89 handleArchive(result, child);
90 } else {
91 handleItem(result, newPath);
92 }
93 }
94 }
95
96 private static void handleItem(final Set<String> result, final String name) {
97 if (name.endsWith(".class")) {
98 result.add(name);
99 }
100 }
101
102 private static void handleArchive(final Set<String> result, final File file) throws ZipException, IOException {
103 final ZipFile zip = new ZipFile(file);
104 final Enumeration<? extends ZipEntry> entries = zip.entries();
105 while (entries.hasMoreElements()) {
106 final ZipEntry entry = entries.nextElement();
107 final String name = entry.getName();
108 handleItem(result, name);
109 }
110 }
111
112 private static ClassLoader getClassLoader() {
113 ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();
114 if (clsLoader == null) {
115 clsLoader = ClassLoader.getSystemClassLoader();
116 }
117 return clsLoader;
118 }
119
120 public static String filenameToClassname(final String filename) {
121 return filename.substring(0, filename.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
122 }
123
124}
125