Talk about the Java reflection

  • 2020-05-16 06:56:13
  • OfStack

The Java reflection mentioned this time involves more code. Because reflection is often used in work, a lot of abstractions and filters are made to the code. Although the code is a lot, but easy to use, filter plugins are also easy to modify.

Here's where reflection is easier to use at work. For example, plug-ins or filters, if there are fewer subclasses of abstraction, you can achieve the same effect by configuring a structure such as XML. If you want to be flexible, you may want to use it directly when you add a plug-in or filter code subclass. Maybe reflection would be better, by scanning all the class or jar files and getting all the inherited subclasses. If all files are scanned every time you make a call, performance will suffer. So add the reflection cache to the implementation and cache all the reflection results as 1 key for all the parameters involved in getting the reflection subclass. Next time if it's the same key, don't rescan it.

The code example is as follows:


public static void main(String[] args) {
 // Set the scan range, it could be class Where the file is located, for example bin Or is under mysql At the beginning or mysql At the end of the jar,
 // Set to "" Scan for all , This comparison takes time 
 ReflectUtils.createSharedReflections("classes", "bin", "mysql");
 try {
  // The debug phase can be set to do a full scan each time 
  //Beans.setDesignTime(true);
  final Collection<String> subTypes = ReflectUtils.listSubClass(IA.class);//
  for (final String subType : subTypes) {
  // You get all the inheritance here IA A subclass of 
  System.out.println(subType);
  final IA impl = ReflectUtils.initClass(subType, IA.class);
  if (null == impl)
   continue;
  // Through this way, can unify 1 Do the operation, 
  impl.print();
  }
 } catch (Exception e) {
  e.printStackTrace();
 }
 }

Code execution results:


// Cache files to avoid rescanning every time reflection is called 
// If you delete the file, when reflection is called again, it will be scanned again, 1 This file is usually deleted when subclassing is added to the code 
XmlUtils.readXml failure:.\configuration.REF ( The system could not find the specified file. )
net.simple.reflect.test.B
net.simple.reflect.test.B
net.simple.reflect.test.D
net.simple.reflect.test.V

The concrete class inside how to realize you look at the source code, here posted two core classes of the code. Source address: 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  Li Yanfei 
 * @email eliyanfei@126.com 
 * 2016 years 11 month 2 day   In the afternoon 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
 // First, filter out the current allowed scan path 
 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  Li Yanfei 
 * @email eliyanfei@126.com 
 * 2016 years 11 month 2 day   In the afternoon 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);
 }

 /**
 *  This method is used for binding 1 Common Shared type traversal tools .
 * @param sharedReflections
 */
 public static final void setSharedReflections(final Reflections sharedReflections) {
 ReflectUtils.sharedReflections = sharedReflections;
 }

 /**
 *  The Shared type traversal tool must be set before calling this method , reference :{@link #setSharedReflections(Reflections)},
 *  This approach mainly makes it easier to traverse the implementation of a given class ,
 */
 public static final Collection<String> listSubClass(final Class<?> baseType, final String... typeNames) {//
 if (null == sharedReflections)
  return EMP_COLL;
 // The call phase is due to the possibility of adding a new subclass implementation , It needs to be scanned every time , Use record keeping only for published products to improve startup speed .
 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;
 }
 }
}

Related articles: