>  기사  >  Java  >  업무에 일반적으로 사용되는 Java 리플렉션

업무에 일반적으로 사용되는 Java 리플렉션

阿神
阿神원래의
2016-11-07 17:25:461805검색

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

//缓存文件,避免每次调用反射都重新扫描
//如果删除该文件,再次调用反射时,会重新扫描,一般会在代码里面有添加子类的时候会删除该文件
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;

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(&#39;\\&#39;, &#39;/&#39;);
                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(&#39;:&#39;) != -1) {
                    if (pathUrl.getFile().charAt(0) == &#39;/&#39;)
                        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(&#39;.&#39;);
        if (-1 == lastDotIdx)
            return;
        final String typeDef = clazzPath.substring(0, lastDotIdx).replace(&#39;/&#39;, &#39;.&#39;);
        if (null != typeNames && typeNames.length > 0) {
            final int lastDot = typeDef.lastIndexOf(&#39;.&#39;);
            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(&#39;.&#39;, &#39;/&#39;) + ".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) != &#39;/&#39;)
            pkgBuf.insert(0, &#39;/&#39;);

        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(&#39;.&#39;)).replace(&#39;/&#39;, &#39;.&#39;));
                    else
                        result.add(entryname.substring(entryname.lastIndexOf(&#39;/&#39;) + 1, entryname.lastIndexOf(&#39;.&#39;)));
                }
            }
        } 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, &#39;.&#39;));
                } 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;
        }
    }
}


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.