/*
* Copyright 2008-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.hasor.core.utils;
import net.hasor.core.classcode.asm.AnnotationVisitor;
import net.hasor.core.classcode.asm.ClassReader;
import net.hasor.core.classcode.asm.ClassVisitor;
import net.hasor.core.classcode.asm.Opcodes;
import net.hasor.core.utils.ResourcesUtils.ScanEvent;
import net.hasor.core.utils.ResourcesUtils.ScanItem;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* @version : 2013-8-13
* @author 赵永春 (zyc@hasor.net)
*/
public class ScanClassPath {
private ClassLoader classLoader = null;
private String[] scanPackages = null;
private Map<Class<?>, Set<Class<?>>> cacheMap = new WeakHashMap<Class<?>, Set<Class<?>>>();
//
private ScanClassPath(final String[] scanPackages) {
this(scanPackages, null);
}
private ScanClassPath(final String[] scanPackages, final ClassLoader classLoader) {
this.scanPackages = scanPackages;
this.classLoader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
}
//
public static ScanClassPath newInstance(final String[] scanPackages) {
return new ScanClassPath(scanPackages) {
};
}
public static ScanClassPath newInstance(final String scanPackages) {
return new ScanClassPath(new String[] { scanPackages }) {
};
}
/**
* 扫描jar包中凡是匹配compareType参数的类均被返回。(对执行结果不缓存)
* @param packagePath 要扫描的包名。
* @param compareType 要查找的特征。
* @return 返回扫描结果。
*/
public static Set<Class<?>> getClassSet(final String packagePath, final Class<?> compareType) {
return ScanClassPath.getClassSet(new String[] { packagePath }, compareType);
}
/**
* 扫描jar包中凡是匹配compareType参数的类均被返回。(对执行结果不缓存)
* @param loadPackages 要扫描的包名。
* @param featureType 要查找的特征。
* @return 返回扫描结果。
*/
public static Set<Class<?>> getClassSet(final String[] loadPackages, final Class<?> featureType) {
return ScanClassPath.newInstance(loadPackages).getClassSet(featureType);
}
/**
* 扫描jar包中凡是匹配compareType参数的类均被返回。(对执行结果不缓存)
* @param compareType 要查找的特征。
* @return 返回扫描结果。
*/
public Set<Class<?>> getClassSet(final Class<?> compareType) {
//0.尝试从缓存中获取
Set<Class<?>> returnData = this.cacheMap.get(compareType);
if (returnData != null) {
return Collections.unmodifiableSet(returnData);
}
//1.准备参数
final String compareTypeStr = compareType.getName();//要匹配的类型
final Set<String> classStrSet = new HashSet<String>();//符合条件的Class
//2.扫描
for (String tiem : this.scanPackages) {
if (StringUtils.isBlank(tiem)) {
continue;
}
try {
ResourcesUtils.scan(tiem.replace(".", "/") + "*.class", new ScanItem() {
@Override
public void found(final ScanEvent event, final boolean isInJar) throws IOException {
String name = event.getName();
if (name.endsWith(".class") == false) {
return;
}
//1.取得类名
name = name.substring(0, name.length() - ".class".length());
name = name.replace("/", ".");
//2.装载类
InputStream inStream = event.getStream();
ClassInfo info = ScanClassPath.this.loadClassInfo(name, inStream, ScanClassPath.this.classLoader);
//3.测试目标类是否匹配
for (String castType : info.castType) {
if (castType.equals(compareTypeStr)) {
classStrSet.add(name);
return;
}
}
for (String face : info.annos) {
if (face.equals(compareTypeStr)) {
classStrSet.add(name);
return;
}
}
}
});
} catch (Throwable e) {
e.printStackTrace();
}
}
//3.缓存
returnData = new HashSet<Class<?>>();
for (String atClass : classStrSet) {
try {
Class<?> clazz = Class.forName(atClass, false, this.classLoader);
returnData.add(clazz);
} catch (Throwable e) { /**/}
}
this.cacheMap.put(compareType, returnData);
return returnData;
}
//
private Map<String, ClassInfo> classInfoMap = new ConcurrentHashMap<String, ClassInfo>();
/**分析类的字节码,分析过程中会递归解析父类和实现的接口*/
private ClassInfo loadClassInfo(String className, final InputStream inStream, final ClassLoader loader) throws IOException {
/*一、检查类是否已经被加载过,避免重复扫描同一个类*/
if (this.classInfoMap.containsKey(className) == true) {
return this.classInfoMap.get(className);
}
/*二、使用 ClassReader 读取类的基本信息*/
ClassReader classReader = null;
try {
classReader = new ClassReader(inStream);
} catch (Exception e) {
if (e instanceof IOException) {
throw (IOException) e;
}
throw new IOException(e);
}
//className = classReader.getClassName().replace('/', '.');
/*三、读取类的(名称、父类、接口、注解)信息*/
final ClassInfo info = new ClassInfo();
classReader.accept(new ClassVisitor(Opcodes.ASM4) {
@Override
public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
//1.读取基本信息
info.className = name.replace('/', '.');
if (superName != null) {
info.superName = superName.replace('/', '.');
}
//2.读取接口
info.interFaces = interfaces;
for (int i = 0; i < info.interFaces.length; i++) {
info.interFaces[i] = info.interFaces[i].replace('/', '.');
}
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
//3.扫描类信息,获取标记的注解
/**将一个Ljava/lang/Object;形式的字符串转化为java/lang/Object形式。*/
String[] annoArrays = info.annos == null ? new String[0] : info.annos;
//
String[] newAnnoArrays = new String[annoArrays.length + 1];
System.arraycopy(annoArrays, 0, newAnnoArrays, 0, annoArrays.length);
//
String annnoType = desc.substring(1, desc.length() - 1);
newAnnoArrays[newAnnoArrays.length - 1] = annnoType.replace('/', '.');
//
info.annos = newAnnoArrays;
return super.visitAnnotation(desc, visible);
}
}, ClassReader.SKIP_CODE);
//四、递归解析父类
if (info.superName != null) {
InputStream superStream = loader.getResourceAsStream(info.superName.replace('.', '/') + ".class");
if (superStream != null) {
this.loadClassInfo(info.superName, superStream, loader);//加载父类
}
}
//五、递归解析接口
for (String faces : info.interFaces) {
InputStream superStream = loader.getResourceAsStream(faces.replace('.', '/') + ".class");
if (superStream != null) {
this.loadClassInfo(faces, superStream, loader);//加载父类
}
}
//六、类型链
Set<String> castTypeList = new TreeSet<String>();/*可转换的类型*/
String superName = info.superName;
addCastTypeList(info, castTypeList);//this
//
if (superName != null) {
while (true) {
if (superName == null || this.classInfoMap.containsKey(superName) == false) {
break;
}
ClassInfo superInfo = this.classInfoMap.get(superName);
addCastTypeList(superInfo, castTypeList);//super
superName = superInfo.superName;
}
}
info.castType = castTypeList.toArray(new String[castTypeList.size()]);
//
this.classInfoMap.put(info.className, info);
return info;
}
private void addCastTypeList(final ClassInfo info, final Set<String> addTo) {
if (info == null) {
return;
}
addTo.add(info.className);
if (info.superName != null) {
addTo.add(info.superName);
}
if (info.interFaces != null) {
for (String atFaces : info.interFaces) {
addTo.add(atFaces);
this.addCastTypeList(this.classInfoMap.get(atFaces), addTo);
}
}
}
//
/**类信息结构*/
private static class ClassInfo {
/*类名*/
public String className = null;
/*继承的父类*/
public String superName = null;
/*直接实现的接口*/
public String[] interFaces = new String[0];
/*可以转换的类型*/
public String[] castType = new String[0];
/*标记的注解*/
public String[] annos = new String[0];
}
}