背景
遇到一个bug,同一个接口,在开发环境和其他环境,期望得到的异常请求结果居然不同,但每个环境的代码都是一致的
排查
通过走读代码 + debug,发现我们系统自定义的 异常处理类,除开发外,其余环境没有生效!!!
![](https://img.haomeiwen.com/i15595522/f93f89abf518c1c7.png)
为什么没有生效?
原因可能有以下几种:
1.没被Spring加载
没被加载又有几种因素:比如没被扫描到,初始化bean的时候报错了等
-- 这种情况不存在,因为开发环境是ok
2.还有其他类似被标注了 @ControllerAdvice 的类存在,由于类加载顺序问题,被其他 ControllerAdvice 优先执行了
-- 然而找遍工程,没有
![](https://img.haomeiwen.com/i15595522/289ef17abe9c9319.png)
现在只能怀疑是不是在依赖的jar中存在类似的处理
下一个问题,怎么从jar里找这个注解是否被使用了呢?
百度了下,貌似没有工具能做到从class里查数据的
利用ClassLoader加载指定包下面所有的Class对象
刚好我们的工程在启动类那里,指定了扫描的包,那么如果存在类似功能的Class,并且要生效,那么一定是能被扫描到的
@SpringBootApplication(scanBasePackages = {"com.xxx"})
去网上去找了一份代码,获取到了指定包下所有的Class对象,同时也输出目标结果
![](https://img.haomeiwen.com/i15595522/13054dbaae8448c0.png)
通过这份代码输出的结果,终于定位到了问题
这份从网上引用的代码放到文章最后,仅供参考,不做其他任何非法使用
解决
对自己系统的异常处理类添加优先级
@Priority(1)
示例:
![](https://img.haomeiwen.com/i15595522/55dbfab9a06138e8.png)
网上摘抄加载Class代码示例
public class Scanner {
/**
* 从包package中获取所有的Class
*
* @param packageName
* @return
*/
public Set<Class<?>> getClasses(String packageName) throws Exception {
// 第一个class类的集合
//List<Class<?>> classes = new ArrayList<Class<?>>();
Set<Class<?>> classes = new HashSet<>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
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)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
addClass(classes, filePath, packageName);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
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) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
public void addClass(Set<Class<?>> classes, String filePath, String packageName) throws Exception {
File[] files = new File(filePath).listFiles(file -> (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory());
assert files != null;
for (File file : files) {
String fileName = file.getName();
if (file.isFile()) {
String classsName = fileName.substring(0, fileName.lastIndexOf("."));
if (!packageName.isEmpty()) {
classsName = packageName + "." + classsName;
}
doAddClass(classes, classsName);
}
}
}
public void doAddClass(Set<Class<?>> classes, final String classsName) throws Exception {
ClassLoader classLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return super.loadClass(name);
}
};
classes.add(classLoader.loadClass(classsName));
}
public <A extends Annotation> Set<Class<?>> getAnnotationClasses(String packageName, Class<A> annotationClass) throws Exception {
//找用了annotationClass注解的类
Set<Class<?>> controllers = new HashSet<>();
Set<Class<?>> clsList = getClasses(packageName);
if (clsList != null && clsList.size() > 0) {
for (Class<?> cls : clsList) {
if (cls.getAnnotation(annotationClass) != null) {
controllers.add(cls);
}
}
}
return controllers;
}
public static void main(String[] args) throws Exception {
Set<Class<?>> set = new Scanner().getAnnotationClasses("com.xxx", ControllerAdvice.class);
System.out.println(set);
}
}
网友评论