先来看一段代码
public interface WrapIgnoreList {
List<String> KNIFE4J = Arrays.asList(
"/doc.html",
"/swagger-resources",
"/swagger-resources/configuration",
"/v3/api-docs",
"/v2/api-docs",
"/webjars/**");
List<String> ALL = new ArrayList<>(KNIFE4J);
}
这段代码定义了一个名为 WrapIgnoreList 的接口,其中包含两个 List 类型的静态常量 KNIFE4J 和 ALL。
这个接口的作用很简单,就是通过接口名直接访问这两个静态常量
// 引用 KNIFE4J 中的路径
List<String> knife4jPaths = WrapIgnoreList.KNIFE4J;
// 引用 ALL 中的路径(包含了 KNIFE4J 的所有路径)
List<String> allPaths = WrapIgnoreList.ALL;
(静态常量,因为这是接口内的定义,而我们并没有主动去加上权限修饰,出于接口出现的意义,那在 Java 接口内定义的字段默认会有:默认静态,默认常量,默认公共权限。public static final 方便全局共享数据,避免实例化)
通过KNIFE4J变量就能实现对特定的url列表不进行全局响应中的包装。
那为什么还需要创建一个新的列表(ArrayList),而不是直接将 KNIFE4J 作为 ALL 的内容。比如,如果 ALL 只需要包含 KNIFE4J 的内容,直接引用 KNIFE4J 更简洁。这是出于扩展性考虑的,如果以后你需要添加更多路径,就可以在 ALL 的初始化过程中做扩展。
List<String> ALL = new ArrayList<>(KNIFE4J);
ALL.add("/new-path"); // 在 ALL 中添加新的路径
可能不太熟悉列表相关函数的小伙伴可能会问:KNIFE4J不能直接调用add吗?
不行,KNIFE4J 不能直接调用 add() 方法的原因是它是一个List 接口类型的静态常量,具体来说,它是通过 Arrays.asList() 方法创建的一个固定大小的列表。这种列表的大小是不可变的,意味着你不能对它进行修改,如添加或删除元素。
Arrays.asList() 创建的列表特性:
当使用 Arrays.asList() 方法时,返回的是一个固定大小的列表,这个列表不能改变大小,但它的内容(元素)是可以修改的。也就是说:
可以修改元素,例如使用 set() 方法来更新列表中的某个元素。
不能增加或删除元素,例如不能使用 add()、remove() 或 clear() 方法。
举个例子:
List<String> list = Arrays.asList("one", "two", "three");
// 可以修改元素
list.set(0, "changed");
// 不能添加或删除元素
list.add("four"); // 抛出 UnsupportedOperationException
调用 add() 方法会抛出 UnsupportedOperationException,因为 add() 方法试图改变列表的大小。
如何解决这个问题?
方法一:
如果你想在 KNIFE4J 中添加更多的元素,可以使用 ArrayList 来创建一个可变的列表,像这样:
List<String> KNIFE4J = new ArrayList<>(Arrays.asList(
"/doc.html",
"/swagger-resources",
"/swagger-resources/configuration",
"/v3/api-docs",
"/v2/api-docs",
"/webjars/**"));
// 现在可以添加新元素了
KNIFE4J.add("/new-path");
通过使用 new ArrayList<>(Arrays.asList(…)),你创建了一个新的 ArrayList,它可以自由地修改和调整大小。
这里可能有人会有疑惑,接口内定义的不是静态变量吗?怎么可以调用add()呢?
因为此时虽然KNIFE4J是在接口中定义的静态常量,但它是一个可变对象(ArrayList),这就是它可以调用 add() 的原因。
在 Java 中,final 修饰符只能防止引用的重新赋值,但它不能阻止引用对象内部的内容发生变化。也就是说,你不能改变 KNIFE4J 本身指向的 ArrayList 对象,但你可以改变该 ArrayList 内部的数据。
具体也与ALL列表的情况一样:
public interface WrapIgnoreList {
List<String> KNIFE4J = Arrays.asList(
"/doc.html",
"/swagger-resources",
"/swagger-resources/configuration",
"/v3/api-docs",
"/v2/api-docs",
"/webjars/**");
List<String> ALL = new ArrayList<>(KNIFE4J);
}
ALL 本身是 final,这意味着你不能把 ALL 重新赋值为另一个列表。
但是,ALL 是一个可变的 ArrayList,它本身的引用不能变,但这个 ArrayList 可以修改其内容,因此你可以对它调用 add() 等修改操作。
ALL.add("/new-doc"); // 这是合法的,因为它修改了 ArrayList 的内容,而没有改变 ALL 引用本身
ALL = new ArrayList<>(); // 这是非法的,因为 ALL 是 final,不能重新指向一个新的对象
方法二:
让 KNIFE4J 成为实例变量,在实现了该接口的类中定义实例变量,实例化后就可以用add()方法了。
例如:
public interface WrapIgnoreList {
// 接口中定义静态常量
List<String> KNIFE4J = Arrays.asList(
"/doc.html",
"/swagger-resources",
"/swagger-resources/configuration",
"/v3/api-docs",
"/v2/api-docs",
"/webjars/**");
}
// 类中定义实例变量
public class WrapIgnoreListImpl implements WrapIgnoreList {
List<String> KNIFE4J = new ArrayList<>(Arrays.asList(
"/doc.html",
"/swagger-resources",
"/swagger-resources/configuration",
"/v3/api-docs",
"/v2/api-docs",
"/webjars/**"));
public void addPath(String path) {
KNIFE4J.add(path);
}
//实例化
public class Main {
public static void main(String[] args) {
WrapIgnoreListImpl ignoreList = new WrapIgnoreListImpl();
ignoreList.addPath("/new-path"); // 可以成功调用 add() 方法
}
}
}
推荐使用方法一。