聊一聊Java反射
发布时间:2020-05-27 11:30:58 所属栏目:Java 来源:互联网
导读:这次提到的Java反射涉及的代码比较多。因为工作中经常用到反射,对代码做了很多抽象以及过滤器。虽然代码量很多,但是简单易用,过滤插件也易修改。
|
这次提到的Java反射涉及的代码比较多。因为工作中经常用到反射,对代码做了很多抽象以及过滤器。虽然代码量很多,但是简单易用,过滤插件也易修改。 下面介绍下工作中哪些地方比较容易用到反射。比如插件或者过滤器,如果抽象的子类比较少,配置成XML等结构也是可以达到同样的效果。如果希望灵活一些,添加了插件或者过滤器代码子类后希望可以直接使用。可能反射会比较好点,通过扫描所有class或者jar文件,得到所有继承的子类。如果每次调用都扫描所有的文件会比较影响性能。所以在实现里面加入反射缓存,对所要获取反射子类时涉及的所有参数作为一个key缓存所有的反射结果。下次如果是同样的key,就不在重新扫描。 代码示例如下:
public static void main(String[] args) {
//设置扫描范围,可以是class文件所在位置例如bin下或者是mysql开头或者mysql结尾的jar,//设置为""为全部都扫描,这种比较耗时
ReflectUtils.createSharedReflections("classes","bin","mysql");
try {
//调试阶段可以设置每次都全扫描
//Beans.setDesignTime(true);
final Collection<String> subTypes = ReflectUtils.listSubClass(IA.class);//
for (final String subType : subTypes) {
//这里获取的是所有继承IA的子类
System.out.println(subType);
final IA impl = ReflectUtils.initClass(subType,IA.class);
if (null == impl)
continue;
//通过该方式,可以统一做操作,
impl.print();
}
} catch (Exception e) {
e.printStackTrace();
}
}
代码执行结果: //缓存文件,避免每次调用反射都重新扫描 //如果删除该文件,再次调用反射时,会重新扫描,一般会在代码里面有添加子类的时候会删除该文件 XmlUtils.readXml failure:.configuration.REF (系统找不到指定的文件。) net.simple.reflect.test.B net.simple.reflect.test.B net.simple.reflect.test.D net.simple.reflect.test.V 具体的类里面如何实现的大家就看下源码吧,这里贴出两个核心类的代码。源码地址:https://git.oschina.net/eliyanfei/api_tools
package net.simple.reflect;
import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import net.simple.reflect.filter.IPathURLFilter;
import net.simple.reflect.filter.ISubTypeFilter;
import net.simple.reflect.filter.ITypeFilter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
*
* @author 李岩飞
* @email eliyanfei@126.com
* 2016年11月2日 下午3:23:49
*
*/
public final class Reflections {
private final Collection<URL> pathUrls;
private final Collection<IPathURLFilter> pathURLfilters;
private final Collection<ITypeFilter> typeFilters;
private ISubTypeFilter subTypeFilter;
public Reflections() {
typeFilters = new ArrayList<ITypeFilter>();
pathURLfilters = new ArrayList<IPathURLFilter>();
this.pathUrls = ClasspathHelper.getUrlsForCurrentClasspath();
}
public Reflections(final Collection<URL> pathUrls) {
this.pathUrls = pathUrls;
typeFilters = new ArrayList<ITypeFilter>();
pathURLfilters = new ArrayList<IPathURLFilter>();
}
/**
* @param subTypeFilter
* the subTypeFilter to set
*/
public void setSubTypeFilter(final ISubTypeFilter subTypeFilter) {
this.subTypeFilter = subTypeFilter;
}
/**
* @return the subTypeFilter
*/
public ISubTypeFilter getSubTypeFilter() {
return subTypeFilter;
}
public Reflections addPathURLFilter(final IPathURLFilter pathURLFilter) {
if (null == pathURLFilter)
return this;
if (!this.pathURLfilters.contains(pathURLFilter))
this.pathURLfilters.add(pathURLFilter);
return this;
}
public Reflections addTypeFilter(final ITypeFilter typeFilter) {
if (null == typeFilter)
return this;
if (!this.typeFilters.contains(typeFilter))
this.typeFilters.add(typeFilter);
return this;
}
private static final String histFile = "./configuration.REF";
private Document histDom;
public Collection<String> getSubTypesFast(final Class<?> baseType) {//,final String... typeNames
//首先过滤出当前允许扫描的路径
final StringBuilder bufPathsId = new StringBuilder(32);
final Map<File,URL> fileUrls = new LinkedHashMap<File,URL>(8);
for (final URL pathUrl : pathUrls) {
if (!acceptPathUrl(pathUrl))
continue;
File file = null;
try {
file = new File(URLDecoder.decode(pathUrl.getFile(),"UTF-8"));
} catch (final Exception e) {
file = new File(pathUrl.getFile());
}
fileUrls.put(file,pathUrl);
if (!file.exists())//is url file?ignore
continue;
bufPathsId.append(file.getName()).append(file.lastModified());
}
final String domId = MD5.getHashString(bufPathsId.toString());
if (null == histDom)
histDom = W3cUtils.readXml(histFile);
if (null == histDom)
histDom = W3cUtils.newDom("R");
Element rootEle = histDom.getDocumentElement();
if (null == rootEle)
histDom.appendChild(rootEle = histDom.createElement("R"));
if (!domId.equals(rootEle.getAttribute("id"))) {
rootEle.getParentNode().removeChild(rootEle);
histDom.appendChild(rootEle = histDom.createElement("R"));
rootEle.setAttribute("id",domId);
}
final String baseTypeId = MD5.getHashString(baseType.getName());
Element refEle = W3cUtils.firstChildElement(rootEle,"E","id",baseTypeId);
if (null != refEle) {
final List<Element> valueEles = W3cUtils.childElementList(refEle,"F");
final Collection<String> result = new ArrayList<String>(valueEles.size());
for (final Element valueEle : valueEles) {
result.add(new String(Base64.decodeFast(valueEle.getAttribute("id"))));
}
return result;
}
final ThreadPool<ListSubTypes> pool = new ThreadPool<ListSubTypes>();
for (final File fileKey : fileUrls.keySet()) {
pool.execute(new ListSubTypes(baseType,fileKey,fileUrls.get(fileKey)));
}
try {
pool.shutdown(3,TimeUnit.MINUTES);
} catch (final InterruptedException e) {
e.printStackTrace();//for debug
}
final Collection<String> result = new ArrayList<String>();
for (final ListSubTypes task : pool.getThreadRunables()) {
result.addAll(task.result);
}
refEle = W3cUtils.addEle(rootEle,"E");
refEle.setAttribute("id",baseTypeId);
for (final String itm : result) {
W3cUtils.addEle(refEle,"F").setAttribute("id",Base64.encodeToString(itm.getBytes(),false));
}
try {
W3cUtils.writeXmlDocument(histFile,histDom);
} catch (final Exception e) {
}
return result;
}
/**
* @see {@link ReflectUtils#createSharedReflections(String...)}
* @see {@link ReflectUtils#setSharedReflections(Reflections)}
* @see {@link ReflectUtils#listSubClass(Class)}
* @param baseType
* @return
*/
public Collection<String> getSubTypes(final Class<?> baseType,final String... typeNames) {//
final ThreadPool<ListSubTypes> pool = new ThreadPool<ListSubTypes>();
for (final URL pathUrl : pathUrls) {
if (!acceptPathUrl(pathUrl))
continue;
File file = null;
try {
file = new File(URLDecoder.decode(pathUrl.getFile(),"UTF-8"));
} catch (final Exception e) {
file = new File(pathUrl.getFile());
}
pool.execute(new ListSubTypes(baseType,file,pathUrl,typeNames));
}
try {
pool.shutdown(3,TimeUnit.MINUTES);
} catch (final InterruptedException e) {
e.printStackTrace();//for debug
}
final Collection<String> result = new ArrayList<String>();
for (final ListSubTypes task : pool.getThreadRunables()) {
result.addAll(task.result);
}
return result;
}
class ListSubTypes implements Runnable {
final File file;
final Class<?> baseType;
final URL pathUrl;
final String[] typeNames;
public ListSubTypes(final Class<?> baseType,final File file,final URL pathUrl,final String... typeNames) {
this.baseType = baseType;
this.file = file;
this.pathUrl = pathUrl;
this.typeNames = typeNames;
}
Collection<String> result = new ArrayList<String>(4);
@Override
public void run() {
if (file.isDirectory()) {
listSubTypesFromDirectory(file,baseType,result,typeNames);
} else
listSubTypesFromJar(baseType,typeNames);
}
}
/**
* @param baseType
* @param pathUrl
* @param result
*/
public void listSubTypesFromDirectory(final File baseDirectory,final Class<?> baseType,final File directory,final Collection<String> result,final String... typeNames) {
File[] files = directory.listFiles();
if (null == files)
files = new File[] {};
String clazzPath;
final int baseDirLen = baseDirectory.getAbsolutePath().length() + 1;
for (final File file : files) {
if (file.isDirectory()) {
listSubTypesFromDirectory(baseDirectory,typeNames);
} else {
clazzPath = file.getAbsolutePath().substring(baseDirLen);
clazzPath = clazzPath.replace('','/');
doTypesFilter(baseType,clazzPath,typeNames);
}
}
}
/**
* @param baseType
* @param pathUrl
* @param result
*/
public void listSubTypesFromJar(final Class<?> baseType,URL pathUrl,final String... typeNames) {
try {
// It does not work with the filesystem: we must
// be in the case of a package contained in a jar file.
JarFile jarFile = null;
try {
if ("file".equals(pathUrl.getProtocol()))
pathUrl = new URL("jar:" + pathUrl.toExternalForm() + "!/");
jarFile = ((JarURLConnection) pathUrl.openConnection()).getJarFile();
} catch (final Exception e) {
final String filePath = pathUrl.getFile();
// if on win platform
if (filePath.indexOf(':') != -1) {
if (pathUrl.getFile().charAt(0) == '/')
jarFile = new JarFile(filePath.substring(1));
}
if (null == jarFile)
jarFile = new JarFile(filePath);
}
final Enumeration<JarEntry> e = jarFile.entries();
ZipEntry entry;
while (e.hasMoreElements()) {
entry = e.nextElement();
doTypesFilter(baseType,entry.getName(),typeNames);
}
} catch (final IOException ioex) {
}
}
private void doTypesFilter(final Class<?> baseType,final String clazzPath,final String... typeNames) {
if (!clazzPath.endsWith(".class"))
return;
final int lastDotIdx = clazzPath.lastIndexOf('.');
if (-1 == lastDotIdx)
return;
final String typeDef = clazzPath.substring(0,lastDotIdx).replace('/','.');
if (null != typeNames && typeNames.length > 0) {
final int lastDot = typeDef.lastIndexOf('.');
if (lastDot == -1)
return;
final String typeName = typeDef.substring(lastDot + 1);
boolean withLiked = false;
for (final String tmpTypeName : typeNames) {
if (!typeName.contains(tmpTypeName))
continue;
withLiked = true;
break;
}
if (withLiked == false)
return;
}
if (this.typeFilters.isEmpty()) {
if (null == this.subTypeFilter || this.subTypeFilter.accept(baseType,clazzPath))
result.add(typeDef);
} else {
for (final ITypeFilter typeFilter : this.typeFilters) {
if (!typeFilter.accept(clazzPath))
continue;
if (null == this.subTypeFilter || this.subTypeFilter.accept(baseType,clazzPath))
result.add(typeDef);
}
}
}
/**
* @param pathUrl
* @return
*/
private boolean acceptPathUrl(final URL pathUrl) {
if (this.pathURLfilters.isEmpty())
return true;
for (final IPathURLFilter pathURLFilter : this.pathURLfilters) {
if (pathURLFilter.accept(pathUrl))
return true;
}
return false;
}
}
(编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
