如何封装自己的扫包工具类

基于工具类实现扫包,扫类,扫注解

Posted by MasterJen on November 11, 2018

Hey ScannerUtils 扫包工具类

In doing we learn.– 实践长才干.

最近做项目时,有个特殊的需求,就是进行扫注解,但是呢,此注解并不是基于springboot的而是自定义的,那怎么才能做到类上或者方法上的注解呢?鉴于此,我就写了一个进行扫包,扫类,扫注解的工具类,方便大家使用,详细类见如下:

//扫包工具类
public class ScannerUtils {
//    得到所有的 目标类  (Controller类)
    private static Set<Class> classets = new LinkedHashSet<>();
//    存放所有关于 此注解 的类
    private static Set<PermissonBean> classPermissonBeanSet = new LinkedHashSet<>();
//    存放 含有此注解的 方法
    private static Set<PermissonBean> methodPermissonBeanSet = new LinkedHashSet<>();

    /**
     * 根据指定的包扫描下面所有的类 得到所有的类
     *
     * @param packagename   com.masterjen.project
     * @return
     */
    private static void getClassSet(String packagename) throws Exception {
//        把所有的类的 绝对路径进行转换成  com/master/projec的文件的形式
        URL resource = PermsAnnoUtils.class.getClassLoader().getResource(packagename.replace(".", "/"));
//      得到 目标文件夹  进行判断
        if (resource != null && "file".equalsIgnoreCase(resource.getProtocol())) {
            //当前项目运行环境所在的绝对路径
            String packagepath = URLDecoder.decode(resource.getFile(), "UTF-8");
//            进行扫包  packagename : com.master.project...  packagepath:com/master/project
            addClass(packagename, packagepath);
        }

    }

    /**
     * 进行扫包
     * @param packagename  com.masterjen.project
     * @param packagepath  com/masterjen/project
     * @throws Exception
     */
    private static void addClass(String packagename, String packagepath) throws Exception {
        //创建一个文件数组, 数组里面的内容都是class文件  添加一个过滤器 进行过滤得到 以class结尾的文件
        File[] files = new File(packagepath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathfile) {
                //返回值代表是否被添加
                return (pathfile.isFile() && pathfile.getName().endsWith("class")) || pathfile.isDirectory();
            }
        });
        //遍历files
        for (File file : files) {
            //com/masterjen/project/..../controller/UserController
            String fileName = file.getName();
            if (file.isFile()) {
                //代表 是一个文件也就是class结尾的文件
                //如果它不是controller 就跳过
                //如何区分是不是controller,那我们定义了所有的controller都在controller包中
                if (!fileName.contains("Controller")) {
                    //不是controller
                    continue;
                }
//                如果是 Controller文件  那么得到 UserController 类 方便反射进行读取
                String classname = fileName.substring(0, fileName.lastIndexOf("."));
                //class.forname(com.masterjer.project)  得到全限定名称
                classname = packagename + "." + classname;
//                进行反射该类  com.masterjen.project..controller.UserController
                doAddClass(classname);
            } else {
//                说明是文件夹  在此进行扫包  但是需要加上此文件夹的名称
                String subPackagepath = fileName;
                if (!StringUtils.IsEmpty(packagepath)) {
                    subPackagepath = packagepath + "/" + subPackagepath;
                }
//                包的名称也要加上名字
                String subPackagename = fileName;
                if (!StringUtils.IsEmpty(packagename)) {
                    subPackagename = packagename + "." + subPackagename;
                }
//                再次进行扫包 操作
                addClass(subPackagename, subPackagepath);
            }
        }
    }

    /**
     * 根据全限定名称进行  反射操作.
     * @param classname  权限定名称
     * @throws Exception
     */
    private static void doAddClass(String classname) throws Exception {
        Class<?> aClass = Class.forName(classname);//加载类
        classets.add(aClass);//添加到集合中
    }

    /**
     *  进行扫描类上的注解操作
     * @param packgename
     * @throws Exception
     */
    public static void inject(String packgename) throws Exception {
//        首先把上面所有的内容清空,保持唯一性
        classets.clear();
        classPermissonBeanSet.clear();
        methodPermissonBeanSet.clear();
        //当这个方法执行完成后,set中就有所有的类了
        getClassSet(packgename);
        for (Class clazz : classets) {
            //获取我们遍历到的每一个controller class
            //Annotation a 得到类上面的注解
            PermsAnno classAnnotation = (PermsAnno) clazz.getAnnotation(PermsAnno.class);
//            注解的内容 
            String classComment = null;
            String classParent = null;
            boolean classIsMenu = false;
            String classValue = null;
//            如果存在注解  那么进行对类操作 
            if (classAnnotation != null) {
                //描述
                classComment = classAnnotation.comment();
                //父是谁,和父中的comment一个值
                classParent = classAnnotation.parentConment();
                //是不是菜单
                classIsMenu = classAnnotation.isMenu();
                //权限的值
                classValue = classAnnotation.permsValue();
//               根据自定义注解 实现的实体类 
                PermissonBean permissonBean = new PermissonBean();
                permissonBean.setIsMenu(classIsMenu ? 1 : 0);
                permissonBean.setParentComment(classParent);
                permissonBean.setPermsValue(classValue);
                permissonBean.setComment(classComment);
//                把类 放到set集合中 保持唯一性
                classPermissonBeanSet.add(permissonBean);
            }
//            对类上的方法  进行扫描注解
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                PermsAnno methodAnnotation = method.getAnnotation(PermsAnno.class);
                if (methodAnnotation != null) {
                    //描述
                    String comment = methodAnnotation.comment();
                    //父是谁,和父中的comment一个值
                    String parent = methodAnnotation.parentConment();
                    //是不是菜单
                    boolean menu = methodAnnotation.isMenu();
                    //权限的值
                    String value = methodAnnotation.permsValue();
                    // /user/getuserlistbypage   此路径应该是  类上的加上方法上的 路径 
                    String permission = classValue + value;
                    PermissonBean permissonBean = new PermissonBean();
                    permissonBean.setIsMenu(menu ? 1 : 0);
                    permissonBean.setParentComment(parent);
                    permissonBean.setPermsValue(permission);
                    permissonBean.setComment(comment);
//                    放到 set集合中
                    methodPermissonBeanSet.add(permissonBean);
                }
            }
        }
    }


    public static void main(String[] args) throws Exception {
//        测试 完成后 工具类里面 就得到了 两个目标set 集合 就可以进行操作了
        inject("com.masterjen.project");
        System.out.println(classPermissonBeanSet);
        System.out.println(methodPermissonBeanSet);
    }
    public static Set<PermissonBean> getClassPermissonBeanSet() {
        return classPermissonBeanSet;
    }
    public static Set<PermissonBean> getMethodPermissonBeanSet() {
        return methodPermissonBeanSet;
    }

}

需要扫描的 自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD,ElementType.TYPE})
public @interface PermsAnno {
//    权限的值
    String permsValue();
//    权限的描述
    String comment();
//    权限的 父类描述
    String parentConment();
// 是否是菜单
    boolean isMenu() default false;
} 

带有此注解的实体类以及方法

@RestController
@RequestMapping("/user")
@PermsAnno(comment = "用户管理", parentConment = "", permsValue = "/user")
public class UserController {     
    @PermsAnno(permsValue = "/getalluser", comment = "得到所有的用户列表", parentConment = "用户管理", isMenu = true)
    @RequestMapping(value = "/getalluser", method = {RequestMethod.GET, RequestMethod.POST})
    public ResultBean getAllUser(Integer pageNum,Integer pageSize) {
        pageNum = pageNum==null?1:pageNum;
        pageSize = pageSize==null?3:pageSize;
        List<User> list = userService.findAllUser(pageNum,pageSize);
        return ResultBean.setOk(0, "OK", list);
    }  
 }

如此便可以通过工具类的getSet()方法得到带有此注解的类和方法了,里面存放的是类和方法的路径名 ,接下类就可以根据自己的需求进行操作了.