工作中常用到的Java反射

来源:互联网 发布:java api接口调用方法 编辑:程序博客网 时间:2024/06/05 12:08

这次提到的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.Bnet.simple.reflect.test.Bnet.simple.reflect.test.Dnet.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?ignorecontinue;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);@Overridepublic void run() {if (file.isDirectory()) {listSubTypesFromDirectory(file, baseType, pathUrl, file, result, typeNames);} elselistSubTypesFromJar(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 platformif (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: flagif (fileUrl.indexOf(":") != -1)fileUrl.deleteCharAt(0);// delete flagfinal 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('/', '.'));elseresult.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 0