• 技术文章 >Java >java教程

    工作中常用到的Java反射

    阿神阿神2016-11-07 17:25:46原创1114

    这次提到的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 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 pathUrls;
        private final Collection pathURLfilters;
        private final Collection typeFilters;
        private ISubTypeFilter subTypeFilter;
    
        public Reflections() {
            typeFilters = new ArrayList();
            pathURLfilters = new ArrayList();
            this.pathUrls = ClasspathHelper.getUrlsForCurrentClasspath();
        }
    
        public Reflections(final Collection pathUrls) {
            this.pathUrls = pathUrls;
            typeFilters = new ArrayList();
            pathURLfilters = new ArrayList();
        }
    
        /**
         * @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 getSubTypesFast(final Class baseType) {//, final String... typeNames
            //首先过滤出当前允许扫描的路径
            final StringBuilder bufPathsId = new StringBuilder(32);
            final Map fileUrls = new LinkedHashMap(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 valueEles = W3cUtils.childElementList(refEle, "F");
                final Collection result = new ArrayList(valueEles.size());
                for (final Element valueEle : valueEles) {
                    result.add(new String(Base64.decodeFast(valueEle.getAttribute("id"))));
                }
                return result;
            }
            final ThreadPool pool = new ThreadPool();
            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 result = new ArrayList();
            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 getSubTypes(final Class baseType, final String... typeNames) {//
            final ThreadPool pool = new ThreadPool();
            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 result = new ArrayList();
            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 result = new ArrayList(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 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 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 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 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 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 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> listClassOfPackage(final Class cType, final String extenion) {
            final List> result = new ArrayList>();
            final List 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 listClassCanonicalNameOfPackage(final Class clazz, final String extenion) {
            return ReflectUtils.listNameOfPackage(clazz, extenion, true);
        }
    
        public static List listClassNameOfPackage(final Class clazz, final String extenion) {
            return ReflectUtils.listNameOfPackage(clazz, extenion, false);
        }
    
        public static List listNameOfPackage(final Class clazz, final String extenion, final boolean fullPkgName) {
            return ReflectUtils.listNameOfPackage(clazz.getName().replace('.', '/') + ".class", extenion, fullPkgName);
        }
    
        public static List listNameOfPackage(final String clazzPkg, final String extenion, final boolean fullPkgName) {
            final List result = new ArrayList();
    
            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("//m.sbmmt.com/m/"), 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 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 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 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 initClass(final String implClass, final Class tType) {
            return ReflectUtils.initClass(implClass, tType, true);
        }
    
        public static final  T initClass(final String implClass, final Class 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核实处理。
    专题推荐:Java 反射
    上一篇:java与php通过socket通信的实例教程 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • Java 线程的状态与常用方法分析及示例• 如何在Java中实现HashMap与String字符串之间的转换?• Java中如何定义数据类型• Java中类型自动转换机制的示例及分析• 使用Java SpringBoot集成POI实现Word文档导出
    1/1

    PHP中文网