聊一聊Java反射
這次提到的Java反射涉及的代碼比較多。因為工作中經(jīng)常用到反射,對代碼做了很多抽象以及過濾器。雖然代碼量很多,但是簡單易用,過濾插件也易修改。
下面介紹下工作中哪些地方比較容易用到反射。比如插件或者過濾器,如果抽象的子類比較少,配置成XML等結(jié)構(gòu)也是可以達(dá)到同樣的效果。如果希望靈活一些,添加了插件或者過濾器代碼子類后希望可以直接使用??赡芊瓷鋾容^好點,通過掃描所有class或者jar文件,得到所有繼承的子類。如果每次調(diào)用都掃描所有的文件會比較影響性能。所以在實現(xiàn)里面加入反射緩存,對所要獲取反射子類時涉及的所有參數(shù)作為一個key緩存所有的反射結(jié)果。下次如果是同樣的key,就不在重新掃描。
代碼示例如下:
public static void main(String[] args) {
//設(shè)置掃描范圍,可以是class文件所在位置例如bin下或者是mysql開頭或者mysql結(jié)尾的jar,
//設(shè)置為""為全部都掃描,這種比較耗時
ReflectUtils.createSharedReflections("classes", "bin", "mysql");
try {
//調(diào)試階段可以設(shè)置每次都全掃描
//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;
//通過該方式,可以統(tǒng)一做操作,
impl.print();
}
} catch (Exception e) {
e.printStackTrace();
}
}
代碼執(zhí)行結(jié)果:
//緩存文件,避免每次調(diào)用反射都重新掃描 //如果刪除該文件,再次調(diào)用反射時,會重新掃描,一般會在代碼里面有添加子類的時候會刪除該文件 XmlUtils.readXml failure:.\configuration.REF (系統(tǒng)找不到指定的文件。) net.simple.reflect.test.B net.simple.reflect.test.B net.simple.reflect.test.D net.simple.reflect.test.V
具體的類里面如何實現(xiàn)的大家就看下源碼吧,這里貼出兩個核心類的代碼。源碼地址: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
//首先過濾出當(dāng)前允許掃描的路徑
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;
}
/**
* 調(diào)用此方法之前必須先設(shè)置共享的類型遍列工具,參考:{@link #setSharedReflections(Reflections)},
* 此方法主要使更方便的遍列給定類的實現(xiàn),
*/
public static final Collection<String> listSubClass(final Class<?> baseType, final String... typeNames) {//
if (null == sharedReflections)
return EMP_COLL;
//調(diào)用階段由于可能增加新的子類實現(xiàn),需要每次都重新掃描,只有在發(fā)布的產(chǎn)品時使用保存記錄的方法以提高啟動速度.
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;
}
}
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot項目設(shè)置斷點debug調(diào)試無效忽略web.xml問題的解決
這篇文章主要介紹了SpringBoot項目設(shè)置斷點debug調(diào)試無效忽略web.xml問題的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
淺談@RequestBody和@RequestParam可以同時使用
這篇文章主要介紹了@RequestBody和@RequestParam可以同時使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
SpringBoot獲取前臺參數(shù)的六種方式以及統(tǒng)一響應(yīng)
本文主要介紹了SpringBoot獲取前臺參數(shù)的六種方式以及統(tǒng)一響應(yīng),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
springboot1.X和2.X中如何解決Bean名字相同時覆蓋
這篇文章主要介紹了springboot1.X和2.X中如何解決Bean名字相同時覆蓋,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
Mybatis-plus foreach拼接字符串查詢無數(shù)據(jù)返回問題
這篇文章主要介紹了Mybatis-plus foreach拼接字符串查詢無數(shù)據(jù)返回問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
Spring Web項目spring配置文件隨服務(wù)器啟動時自動加載
這篇文章主要介紹了Spring Web項目spring配置文件隨服務(wù)器啟動時自動加載,加載spring的配置文件,并且只加載一次,從而提高程序效率。具體內(nèi)容詳情大家通過本文一起學(xué)習(xí)吧2018-01-01

