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;
2
3
import java.lang.annotation.ElementType;
4
import java.lang.annotation.Retention;
5
import java.lang.annotation.RetentionPolicy;
6
import java.lang.annotation.Target;
7
8
@Retention(RetentionPolicy.RUNTIME)
9
@Target(
{ElementType.TYPE})
10
public @interface AnnotationFaker
{
11
}
12
2.ClassFaker: 被AnnotationFaker标注的class
1
package com.oocl.isdc.sha.frm.test.config;
2
3
@AnnotationFaker
4
public class ClassFaker
{
5
public void hello()
{
6
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;
2
3
import java.util.ArrayList;
4
import java.util.List;
5
6
import org.objectweb.asm.AnnotationVisitor;
7
import org.objectweb.asm.Attribute;
8
import org.objectweb.asm.ClassVisitor;
9
import org.objectweb.asm.FieldVisitor;
10
import org.objectweb.asm.MethodVisitor;
11
import org.objectweb.asm.tree.AnnotationNode;
12
13
public 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,我们就初始化它。
1
package com.oocl.isdc.sha.frm.test.config;
2
3
import java.io.File;
4
import java.io.IOException;
5
import java.net.URISyntaxException;
6
import java.net.URL;
7
import java.net.URLClassLoader;
8
import java.net.URLDecoder;
9
import java.util.Enumeration;
10
import java.util.HashSet;
11
import java.util.Iterator;
12
import java.util.List;
13
import java.util.Set;
14
import java.util.zip.ZipEntry;
15
import java.util.zip.ZipException;
16
import java.util.zip.ZipFile;
17
18
import org.objectweb.asm.ClassReader;
19
import org.objectweb.asm.Type;
20
import org.objectweb.asm.tree.AnnotationNode;
21
22
public 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