每日一博 | 工作中常用到的 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, pathUrl, file, result, typeNames);

} else

listSubTypesFromJar(baseType, pathUrl, result, typeNames);

}

}

/**

* @param baseType

* @param pathUrl

* @param result

*/

public void listSubTypesFromDirectory(final File baseDirectory, final Class<?> baseType, final URL pathUrl, 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, baseType, pathUrl, file, result, typeNames);

} else {

clazzPath = file.getAbsolutePath().substring(baseDirLen);

clazzPath = clazzPath.replace('//', '/');

doTypesFilter(baseType, pathUrl, result, clazzPath, typeNames);

}

}

}

/**

* @param baseType

* @param pathUrl

* @param result

*/

public void listSubTypesFromJar(final Class<?> baseType, URL pathUrl, final Collection<String> result, 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, pathUrl, result, entry.getName(), typeNames);

}

} catch (final IOException ioex) {

}

}

private void doTypesFilter(final Class<?> baseType, final URL pathUrl, final Collection<String> result, 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, pathUrl, clazzPath))

result.add(typeDef);

} else {

for (final ITypeFilter typeFilter : this.typeFilters) {

if (!typeFilter.accept(clazzPath))

continue;

if (null == this.subTypeFilter || this.subTypeFilter.accept(baseType, pathUrl, 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;

}

}

package net.simple.reflect;

import java.beans.Beans;

import java.io.File;

import java.io.IOException;

import java.io.UnsupportedEncodingException;

import java.net.JarURLConnection;

import java.net.URL;

import java.net.URLDecoder;

import java.util.ArrayList;

import java.util.Collection;

import java.util.Collections;

import java.util.Enumeration;

import java.util.List;

import java.util.jar.JarEntry;

import java.util.jar.JarFile;

import java.util.zip.ZipEntry;

import net.simple.reflect.filter.PathURLFilter;

import net.simple.reflect.filter.SampleSubInstanceFilter;

import net.simple.reflect.filter.TypeFilter;

/**

*

* @author 李岩飞

* @email eliyanfei@126.com

* 2016年11月2日 下午3:24:02

*

*/

public final class ReflectUtils {

public static final String VAR_START_FLAG = "${";

public static final String VAR_END_FLAG = "}";

private static Reflections sharedReflections;

static final Collection<String> EMP_COLL = Collections.emptyList();

public static final void createSharedReflections(final String... filterExts) {

final Reflections refs = new Reflections();

refs.addPathURLFilter(new PathURLFilter(filterExts));//

refs.addTypeFilter(TypeFilter.DEFAULT);

refs.setSubTypeFilter(SampleSubInstanceFilter.DEFAULT);

ReflectUtils.setSharedReflections(refs);

}

/**

* 此方法用于绑定一个通用的共享类型遍列工具.

* @param sharedReflections

*/

public static final void setSharedReflections(final Reflections sharedReflections) {

ReflectUtils.sharedReflections = sharedReflections;

}

/**

* 调用此方法之前必须先设置共享的类型遍列工具,参考:{@link #setSharedReflections(Reflections)},

* 此方法主要使更方便的遍列给定类的实现,

*/

public static final Collection<String> listSubClass(final Class<?> baseType, final String... typeNames) {//

if (null == sharedReflections)

return EMP_COLL;

//调用阶段由于可能增加新的子类实现,需要每次都重新扫描,只有在发布的产品时使用保存记录的方法以提高启动速度.

return Beans.isDesignTime() ? sharedReflections.getSubTypes(baseType, typeNames) : sharedReflections.getSubTypesFast(baseType);

}

public static List<Class<?>> listClassOfPackage(final Class<?> cType, final String extenion) {

final List<Class<?>> result = new ArrayList<Class<?>>();

final List<String> cPath = ReflectUtils.listClassCanonicalNameOfPackage(cType, extenion);

for (final String path : cPath) {

try {

result.add(Class.forName(path, false, Thread.currentThread().getContextClassLoader()));

} catch (final Exception e) {

// ignore

}

}

return result;

}

public static List<String> listClassCanonicalNameOfPackage(final Class<?> clazz, final String extenion) {

return ReflectUtils.listNameOfPackage(clazz, extenion, true);

}

public static List<String> listClassNameOfPackage(final Class<?> clazz, final String extenion) {

return ReflectUtils.listNameOfPackage(clazz, extenion, false);

}

public static List<String> listNameOfPackage(final Class<?> clazz, final String extenion, final boolean fullPkgName) {

return ReflectUtils.listNameOfPackage(clazz.getName().replace('.', '/') + ".class", extenion, fullPkgName);

}

public static List<String> listNameOfPackage(final String clazzPkg, final String extenion, final boolean fullPkgName) {

final List<String> result = new ArrayList<String>();

final StringBuffer pkgBuf = new StringBuffer(clazzPkg);

if (pkgBuf.charAt(0) != '/')

pkgBuf.insert(0, '/');

final URL urlPath = ReflectUtils.class.getResource(pkgBuf.toString());

if (null == urlPath)

return result;

String checkedExtenion = extenion;

if (!extenion.endsWith(".class"))

checkedExtenion = extenion + ".class";

if (pkgBuf.toString().endsWith(".class"))

pkgBuf.delete(pkgBuf.lastIndexOf("/"), pkgBuf.length());

pkgBuf.deleteCharAt(0);

final StringBuffer fileUrl = new StringBuffer();

try {

fileUrl.append(URLDecoder.decode(urlPath.toExternalForm(), "UTF-8"));

} catch (final UnsupportedEncodingException e1) {

fileUrl.append(urlPath.toExternalForm());

}

if (fileUrl.toString().startsWith("file:")) {

fileUrl.delete(0, 5);// delete file: flag

if (fileUrl.indexOf(":") != -1)

fileUrl.deleteCharAt(0);// delete flag

final String baseDir = fileUrl.substring(0, fileUrl.lastIndexOf("classes") + 8);

ReflectUtils.doListNameOfPackageInDirectory(new File(baseDir), new File(baseDir), result, pkgBuf.toString(), checkedExtenion, fullPkgName);

} else {

ReflectUtils.doListNameOfPackageInJar(urlPath, urlPath, result, pkgBuf.toString(), checkedExtenion, fullPkgName);

}

return result;

}

/**

*/

private static void doListNameOfPackageInJar(final URL baseUrl, final URL urlPath, final List<String> result, final String clazzPkg, final String extenion, final boolean fullPkgName) {

try {

// It does not work with the filesystem: we must

// be in the case of a package contained in a jar file.

final JarURLConnection conn = (JarURLConnection) urlPath.openConnection();

final JarFile jfile = conn.getJarFile();

final Enumeration<JarEntry> e = jfile.entries();

ZipEntry entry;

String entryname;

while (e.hasMoreElements()) {

entry = e.nextElement();

entryname = entry.getName();

if (entryname.startsWith(clazzPkg) && entryname.endsWith(extenion)) {

if (fullPkgName)

result.add(entryname.substring(0, entryname.lastIndexOf('.')).replace('/', '.'));

else

result.add(entryname.substring(entryname.lastIndexOf('/') + 1, entryname.lastIndexOf('.')));

}

}

} catch (final IOException ioex) {

}

}

private static void doListNameOfPackageInDirectory(final File baseDirectory, final File directory, final List<String> result, final String clazzPkg, final String extenion,

final boolean fullPkgName) {

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()) {

ReflectUtils.doListNameOfPackageInDirectory(baseDirectory, file, result, clazzPkg, extenion, fullPkgName);

} else {

if (!file.getName().endsWith(extenion))

continue;

if (fullPkgName) {

clazzPath = file.getAbsolutePath().substring(baseDirLen);

clazzPath = clazzPath.substring(0, clazzPath.length() - 6);

result.add(clazzPath.replace(File.separatorChar, '.'));

} else {

result.add(file.getName().substring(0, file.getName().length() - 6));

}

}

}

}

public static final <T> T initClass(final String implClass, final Class<T> tType) {

return ReflectUtils.initClass(implClass, tType, true);

}

public static final <T> T initClass(final String implClass, final Class<T> tType, final boolean doInit) {

try {

final Object object = Class.forName(implClass, doInit, Thread.currentThread().getContextClassLoader()).newInstance();

return tType.cast(object);

} catch (final Throwable e) {

return null;

}

}

}

未登录用户
全部评论0
到底啦