聊一聊Java反射

 更新时间:2016年11月06日 16:43:51   作者:李岩飞  
工作中哪些地方比较容易用到反射,这篇文章就为大家介绍了工作中常用到的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;
 }
 }
}

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

相关文章

  • Java I/O深入学习之File和RandomAccessFile

    Java I/O深入学习之File和RandomAccessFile

    这篇文章主要介绍了Java I/O深入学习之File和RandomAccessFile, I/O系统即输入/输出系统,对于一门程序语言来说,创建一个好的输入/输出系统并非易事。在充分理解Java I/O系统以便正确地运用之前,我们需要学习相当数量的类。,需要的朋友可以参考下
    2019-06-06
  • 一篇文章带你入门java算术运算符(加减乘除余,字符连接)

    一篇文章带你入门java算术运算符(加减乘除余,字符连接)

    这篇文章主要介绍了Java基本数据类型和运算符,结合实例形式详细分析了java基本数据类型、数据类型转换、算术运算符、逻辑运算符等相关原理与操作技巧,需要的朋友可以参考下
    2021-08-08
  • Netty中最简单的粘包解析方法分享

    Netty中最简单的粘包解析方法分享

    黏包 是指网络上有多条数据发送给服务端, 但是由于某种原因这些数据在被接受的时候进行了重新组合,本文分享了一种最简单的黏包解析方法, 非常适用于初初初级选手
    2023-05-05
  • Spring从@Aspect到Advisor使用演示实例

    Spring从@Aspect到Advisor使用演示实例

    这篇文章主要介绍了Spring从@Aspect到Advisor使用演示实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-02-02
  • JDBC之PreparedStatement类中预编译的综合应用解析

    JDBC之PreparedStatement类中预编译的综合应用解析

    SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句
    2013-07-07
  • 配置javaw.exe双击运行jar包方式

    配置javaw.exe双击运行jar包方式

    这篇文章主要介绍了配置javaw.exe双击运行jar包方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Java中如何比较两个数组中元素是否相同

    Java中如何比较两个数组中元素是否相同

    比较两个数组中的元素是否相同,在项目中经常会使用到,下面与大家分享个最简单的方法
    2014-06-06
  • 解决jackson反序列化失败InvalidFormatException:Can not deserialize value of type java.util.Date

    解决jackson反序列化失败InvalidFormatException:Can not dese

    这篇文章主要介绍了解决jackson反序列化失败InvalidFormatException:Can not deserialize value of type java.util.Date问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • SpringCloud使用Kafka Streams实现实时数据处理

    SpringCloud使用Kafka Streams实现实时数据处理

    使用Kafka Streams在Spring Cloud中实现实时数据处理可以帮助我们构建可扩展、高性能的实时数据处理应用,Kafka Streams是一个基于Kafka的流处理库,本文介绍了如何在SpringCloud中使用Kafka Streams实现实时数据处理,需要的朋友可以参考下
    2024-07-07
  • Java如何利用策略模式替代if/else语句

    Java如何利用策略模式替代if/else语句

    这篇文章主要介绍了Java如何利用策略模式替代if/else语句,帮助大家优化自己的代码,提高程序运行效率,感兴趣的朋友可以了解下
    2020-09-09

最新评论