当前位置: 移动技术网 > IT编程>开发语言>Java > 聊一聊Java反射

聊一聊Java反射

2019年07月22日  | 移动技术网IT编程  | 我要评论

这次提到的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;
 }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网