1. 需求描述
在 springboot前后端分離后權限原理淺談 基礎上,在涉及到介面權限的時候,必然需要獲取Controller層的介面串列,作為資料庫中權限表的記錄,比如,在權限表中,存盤如下資訊:

考慮到介面測驗的方便,大多數springboot會集成Swagger2或者Swagger3進行介面測驗和生成介面檔案,如下圖所示的注解,就是使用了Swagger2,需要添加的@ApiOperation注解,

假設Controller類中,都添加了如上圖所示的兩個注解,即都統一用post請求,那么,如何獲取com.supger.xxxproject.Controller中的所有介面串列呢,其中,上圖中的 value="新增車輛"為權限名稱,而對應的方法名insertCar為權限編碼, 不難發現,現在需要做的,就是獲取Controller包下的所有類,然后,獲取每一個類的有對應注解@PostMapping的方法名和@ApiOperation注解的value值和對應的類名,存盤在list串列里面,然后插入資料庫,
那么,如何獲取Controller包下的所有類以及對應的方法注解資訊呢?
2. 獲取指定包下的所有類
參考 java讀取指定package下的所有class 文章,現對應的類直接拿來用,類整合如下:
public class FindClassesByPackage {
/**
* 從包package中獲取所有的Class
*
* @param pack
* @return
*/
public static Set<Class<?>> getClasses(String pack) {
// 第一個class類的集合
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
// 是否回圈迭代
boolean recursive = true;
// 獲取包的名字 并進行替換
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
// 定義一個列舉的集合 并進行回圈來處理這個目錄下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(
packageDirName);
// 回圈迭代下去
while (dirs.hasMoreElements()) {
// 獲取下一個元素
URL url = dirs.nextElement();
// 得到協議的名稱
String protocol = url.getProtocol();
// 如果是以檔案的形式保存在服務器上
if ("file".equals(protocol)) {
System.err.println("file型別的掃描");
// 獲取包的物理路徑
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以檔案的方式掃描整個包下的檔案 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath,
recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包檔案
// 定義一個JarFile
System.err.println("jar型別的掃描");
JarFile jar;
try {
// 獲取jar
jar = ((JarURLConnection) url.openConnection())
.getJarFile();
// 從此jar包 得到一個列舉類
Enumeration<JarEntry> entries = jar.entries();
// 同樣的進行回圈迭代
while (entries.hasMoreElements()) {
// 獲取jar里的一個物體 可以是目錄 和一些jar包里的其他檔案 如META-INF等檔案
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/開頭的
if (name.charAt(0) == '/') {
// 獲取后面的字串
name = name.substring(1);
}
// 如果前半部分和定義的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"結尾 是一個包
if (idx != -1) {
// 獲取包名 把"/"替換成"."
packageName = name.substring(0, idx)
.replace('/', '.');
}
// 如果可以迭代下去 并且是一個包
if ((idx != -1) || recursive) {
// 如果是一個.class檔案 而且不是目錄
if (name.endsWith(".class")
&& !entry.isDirectory()) {
// 去掉后面的".class" 獲取真正的類名
String className = name.substring(
packageName.length() + 1, name
.length() - 6);
try {
// 添加到classes
classes.add(Class
.forName(packageName + '.'
+ className));
} catch (ClassNotFoundException e) {
// log
// .error("添加用戶自定義視圖類錯誤 找不到此類的.class檔案");
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
// log.error("在掃描用戶定義視圖時從jar包獲取檔案出錯");
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
/**
* 以檔案的形式來獲取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName,
String packagePath, final boolean recursive, Set<Class<?>> classes) {
// 獲取此包的目錄 建立一個File
File dir = new File(packagePath);
// 如果不存在或者 也不是目錄就直接回傳
if (!dir.exists() || !dir.isDirectory()) {
// log.warn("用戶定義包名 " + packageName + " 下沒有任何檔案");
return;
}
// 如果存在 就獲取包下的所有檔案 包括目錄
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定義過濾規則 如果可以回圈(包含子目錄) 或則是以.class結尾的檔案(編譯好的java類檔案)
@Override
public boolean accept(File file) {
return (recursive && file.isDirectory())
|| (file.getName().endsWith(".class"));
}
});
// 回圈所有檔案
for (File file : dirfiles) {
// 如果是目錄 則繼續掃描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "."
+ file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java類檔案 去掉后面的.class 只留下類名
String className = file.getName().substring(0,
file.getName().length() - 6);
try {
// 添加到集合中去
//classes.add(Class.forName(packageName + '.' + className));
//經過回復同學的提醒,這里用forName有一些不好,會觸發static方法,沒有使用classLoader的load干凈
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// log.error("添加用戶自定義視圖類錯誤 找不到此類的.class檔案");
e.printStackTrace();
}
}
}
}
}
經過測驗,上述的代碼能夠獲取到指定包名下的所有類,在此基礎上,僅需要遍歷類,通過反射的方式,獲取每一個類的所有方法,再對方法進行遍歷,獲取到該方法對應的注解value和方法名稱,整合成list后,存入資料庫,即完成了需求的描述,
下面僅對獲取的資料整合成list,并在控制臺輸出展示,或是轉成json轉,輸出展示,并未存入資料庫,實際專案中,此處,添加存入資料庫的代碼即可,在此不再贅述,
// 獲取所有的介面串列
@Test
public void test3(){
Set<Class<?>> aClass = FindClassesByPackage.getClasses("com.supger.xxxproject.controller");
List<Map<String,Object>> list = new LinkedList<>();
aClass.forEach(e ->{
Method[] methods = e.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(PostMapping.class)) {
// 假設每個方法上只有一個@ApiOperation注解
ApiOperation[] annotations = method.getDeclaredAnnotationsByType(ApiOperation.class);
Map<String,Object> map = new LinkedHashMap<>();
map.put("perm_name",annotations[0].value());
map.put("perm_code",method.getName());
map.put("remark",method.getDeclaringClass().getSimpleName());
list.add(map);
}
}
});
System.out.println(list);
String s = JSON.toJSONString(list);
System.out.println(s);
}
}
3. 參考資源
(1)springboot前后端分離后權限原理淺談
(2) java讀取指定package下的所有class
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/264471.html
標籤:java
上一篇:【死磕JVM】五年 整整五年了 該知道JVM加載機制了!
下一篇:面向物件---繼承
