聊一聊Java反射
程序员文章站
2024-03-12 15:32:50
这次提到的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
具体的类里面如何实现的大家就看下源码吧,这里贴出两个核心类的代码。源码地址:
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; } } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。