巧用Spring中的@Order進行排序
Spring @Order進行排序
直接上代碼
public class OrderAnnotationTest { ?? ?public static void main(String[] args) { ?? ??? ?A a = new A(); ?? ??? ?B b = new B(); ?? ??? ?C c = new C(); ?? ??? ?List<Object> orderList = new ArrayList<>(3); ?? ??? ?orderList.add(a); ?? ??? ?orderList.add(b); ?? ??? ?orderList.add(c); ?? ??? ?orderList.sort(AnnotationAwareOrderComparator.INSTANCE); ?? ??? ?System.out.println(orderList); ?? ?} ?? ?@Order(0) ?? ?static class A { ?? ??? ?@Override ?? ??? ?public String toString() { ?? ??? ??? ?return "A"; ?? ??? ?} ?? ?} ?? ?@Order(-1) ?? ?static class B { ?? ??? ?@Override ?? ??? ?public String toString() { ?? ??? ??? ?return "B"; ?? ??? ?} ?? ?} ?? ?@Order(2) ?? ?static class C { ?? ??? ?@Override ?? ??? ?public String toString() { ?? ??? ??? ?return "C"; ?? ??? ?} ?? ?} }
結(jié)果如下:
[B, A, C]
原理解析:
AnnotationAwareOrderComparator繼承自O(shè)rderComparator
實際比較的方法如下
private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) { ?? ?boolean p1 = (o1 instanceof PriorityOrdered); ?? ?boolean p2 = (o2 instanceof PriorityOrdered); ?? ?if (p1 && !p2) { ?? ??? ?return -1; ?? ?} ?? ?else if (p2 && !p1) { ?? ??? ?return 1; ?? ?} ?? ?int i1 = getOrder(o1, sourceProvider); ?? ?int i2 = getOrder(o2, sourceProvider); ?? ?return Integer.compare(i1, i2); }
Spring中關(guān)于Order的那點事
本文閱讀源碼版本為spring5.3.1
為啥要用Order
spring是一個大量使用策略設(shè)計模式的框架,這意味著有很多相同接口的實現(xiàn)類,如果不手動指定順序的話,那么使用時肯定會有問題。而Order給我們提供了一種編碼設(shè)置順序的可能。
關(guān)于Order
spring中提供了多種方式來設(shè)置優(yōu)先級,有Ordered,PriorityOrdered接口,有Order注解,除此之外,spring4.1以后,還可以使用Priority注解。下面我將針對這幾種用法從源碼的角度來進行分析。
Ordered,PriorityOrdered接口
public interface Ordered { ?? ?/** ?? ? * 最高優(yōu)先值 ?? ? */ ?? ?int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; ?? ?/** ?? ? * 最低優(yōu)先值 ?? ? */ ?? ?int LOWEST_PRECEDENCE = Integer.MAX_VALUE; ?? ?int getOrder(); }
PriorityOrdered繼承了Ordered,但并未提供任何方法,這是一個標(biāo)記了優(yōu)先級的接口,和Ordered相比,PriorityOrdered就是高人一等,spring中提供了比較器OrderComparator,可以通過構(gòu)建一個OrderComparator,調(diào)用其compare方法,不過OrderComparator提供了一個靜態(tài)sort方法,我們無需自己構(gòu)建OrderComparator了,排序的結(jié)果按照order值從小到大排序。
demo
public class OrderDemo{ ?? ?private final OrderComparator comparator = new OrderComparator(); ?? ?@Test ? ? void comparePriorityOrderedInstanceToStandardOrderedInstanceWithSamePriority() { ? ? ? ? assertThatPriorityOrderedAlwaysWins(new StubPriorityOrdered(100), new StubOrdered(100)); ? ? } ? ? @Test ? ? void comparePriorityOrderedInstanceToStandardOrderedInstanceWithLowerPriority() { ? ? ? ? assertThatPriorityOrderedAlwaysWins(new StubPriorityOrdered(100), new StubOrdered(200)); ? ? } ?? ? ?? ?@Test ? ? void compareOrderedInstancesBefore() { ? ? ? ? assertThat(this.comparator.compare(new StubOrdered(100), new StubOrdered(2000))).isEqualTo(-1); ? ? } ? ? @Test ? ? void compareOrderedInstancesNullFirst() { ? ? ? ? assertThat(this.comparator.compare(null, new StubOrdered(100))).isEqualTo(1); ? ? } ? ? @Test ? ? void compareOrderedInstancesNullLast() { ? ? ? ? assertThat(this.comparator.compare(new StubOrdered(100), null)).isEqualTo(-1); ? ? } ?? ?@Test ? ? void test1() { ? ? ? ? assertThat(this.comparator.compare(new Object (), new StubOrdered(2000))).isEqualTo(1); ? ? } ?? ? private static class StubOrdered implements Ordered { ? ? ? ? private final int order; ? ? ? ? StubOrdered(int order) { ? ? ? ? ? ? this.order = order; ? ? ? ? } ? ? ? ? @Override ? ? ? ? public int getOrder() { ? ? ? ? ? ? return this.order; ? ? ? ? } ? ? } ? ? private static class StubPriorityOrdered implements PriorityOrdered { ? ? ? ? private final int order; ? ? ? ? StubPriorityOrdered(int order) { ? ? ? ? ? ? this.order = order; ? ? ? ? } ? ? ? ? @Override ? ? ? ? public int getOrder() { ? ? ? ? ? ? return this.order; ? ? ? ? } ? ? } }
小結(jié)
- PriorityOrdered優(yōu)先級比Ordered高,與設(shè)置的order值無關(guān)。
- 若兩個對象都實現(xiàn)了Ordered或PriorityOrdered接口,那么設(shè)置的order值越小,優(yōu)先值越高。
- 若沒有實現(xiàn)Ordered或PriorityOrdered接口,默認是最低的優(yōu)先級。
OrderComparator#compare解讀
在看compare之前,我覺得將OrderSourceProvider這個函數(shù)式接口放在前面講解一下,閱讀源碼時會更清晰一點。
?? ?@FunctionalInterface ?? ?public interface OrderSourceProvider { ?? ??? ?/** ?? ??? ? * 對給定對象校驗并返回一個新的對象 ?? ??? ? */ ?? ??? ?@Nullable ?? ??? ?Object getOrderSource(Object obj); ?? ?}
demo
public class OrderDemo{ ?? ?private final OrderComparator comparator = new OrderComparator(); ?? ?private static class TestSourceProvider implements OrderComparator.OrderSourceProvider { ?? ??? ?private final Object target; ?? ??? ?private final Object orderSource; ?? ??? ?TestSourceProvider(Object target, Object orderSource) { ?? ??? ??? ?this.target = target; ?? ??? ??? ?this.orderSource = orderSource; ?? ??? ?} ?? ??? ?@Override ?? ??? ?public Object getOrderSource(Object obj) { ?? ??? ??? ?if (target.equals(obj)) { ?? ??? ??? ??? ?return orderSource; ?? ??? ??? ?} ?? ??? ??? ?return null; ?? ??? ?} ?? ?} ?? ?@Test ?? ?void compareWithSourceProviderArray() { ?? ??? ?Comparator<Object> customComparator = this.comparator.withSourceProvider( ?? ??? ??? ??? ?new TestSourceProvider(5L, new Object[] {new StubOrdered(10), new StubOrdered(-25)})); ?? ??? ?assertThat(customComparator.compare(5L, new Object())).isEqualTo(-1); ?? ?} ?? ?@Test ?? ?void compareWithSourceProviderArrayNoMatch() { ?? ??? ?Comparator<Object> customComparator = this.comparator.withSourceProvider( ?? ??? ??? ??? ?new TestSourceProvider(5L, new Object[] {new Object(), new Object()})); ?? ??? ?assertThat(customComparator.compare(new Object(), 5L)).isEqualTo(0); ?? ?} ?? ?@Test ?? ?void compareWithSourceProviderEmpty() { ?? ??? ?Comparator<Object> customComparator = this.comparator.withSourceProvider( ?? ??? ??? ??? ?new TestSourceProvider(50L, new Object())); ?? ??? ?assertThat(customComparator.compare(new Object(), 5L)).isEqualTo(0); ?? ?} }
接下來我們來閱讀compare源碼。
?? ?public int compare(@Nullable Object o1, @Nullable Object o2) { ?? ??? ?return doCompare(o1, o2, null); ?? ?} ?? ?private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) { ?? ??? ?// 這里會判斷是否實現(xiàn)了PriorityOrdered接口 ?? ??? ?boolean p1 = (o1 instanceof PriorityOrdered); ?? ??? ?boolean p2 = (o2 instanceof PriorityOrdered); ?? ??? ?// 這里會看到根本沒有比較order的值,只要實現(xiàn)PriorityOrdered接口,就會排在前面 ?? ??? ?if (p1 && !p2) { ?? ??? ??? ?return -1; ?? ??? ?}else if (p2 && !p1) { ?? ??? ??? ?return 1; ?? ??? ?} ?? ??? ?// 獲取對象設(shè)置的order值 ?? ??? ?int i1 = getOrder(o1, sourceProvider); ?? ??? ?int i2 = getOrder(o2, sourceProvider); ?? ??? ?return Integer.compare(i1, i2); ?? ?} ?? ? ?? ?private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) { ?? ??? ?Integer order = null; ?? ??? ?if (obj != null && sourceProvider != null) { ?? ??? ??? ?Object orderSource = sourceProvider.getOrderSource(obj); ?? ??? ??? ?if (orderSource != null) { ?? ??? ??? ??? ?// 如果返回的是數(shù)組 ?? ??? ??? ??? ?if (orderSource.getClass().isArray()) { ?? ??? ??? ??? ??? ?for (Object source : ObjectUtils.toObjectArray(orderSource)) { ?? ??? ??? ??? ??? ??? ?// 只要找到對象設(shè)置的order值,就跳出 ?? ??? ??? ??? ??? ??? ?order = findOrder(source); ?? ??? ??? ??? ??? ??? ?if (order != null) { ?? ??? ??? ??? ??? ??? ??? ?break; ?? ??? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ?}else { ?? ??? ??? ??? ??? ?order = findOrder(orderSource); ?? ??? ??? ??? ?} ?? ??? ??? ?} ?? ??? ?} ?? ??? ?// 如果我們沒有提供OrderSourceProvider? ?? ??? ?return (order != null ? order : getOrder(obj)); ?? ?} ?? ?protected int getOrder(@Nullable Object obj) { ?? ??? ?if (obj != null) { ?? ??? ??? ?Integer order = findOrder(obj); ?? ??? ??? ?if (order != null) { ?? ??? ??? ??? ?return order; ?? ??? ??? ?} ?? ??? ?} ?? ??? ?// object為null時,返回值最大 ?? ??? ?return Ordered.LOWEST_PRECEDENCE; ?? ?} ?? ?protected Integer findOrder(Object obj) { ?? ??? ?// 沒有實現(xiàn)Ordered接口將返回null ?? ??? ?return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null); ?? ?}
@Order與@Priority
spring中提供了對@Order與@Priority支持的比較器AnnotationAwareOrderComparator,該類繼承OrderComparator,并覆蓋了findOrder方法,我們來一起看下源碼。
?? ?protected Integer findOrder(Object obj) { ?? ??? ?Integer order = super.findOrder(obj); ?? ??? ?if (order != null) { ?? ??? ??? ?return order; ?? ??? ?} ?? ??? ?// 調(diào)用父類的findOrder方法無法找到設(shè)定的order值時 ?? ??? ?return findOrderFromAnnotation(obj); ?? ?} ?? ? ?? ?private Integer findOrderFromAnnotation(Object obj) { ?? ??? ?AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass()); ?? ??? ?// 對整個類型層次結(jié)構(gòu)執(zhí)行完整搜索,包括父類和接口 ?? ??? ?MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY); ?? ??? ?// 獲取注解中設(shè)置的order值 ?? ??? ?Integer order = OrderUtils.getOrderFromAnnotations(element, annotations); ?? ??? ?if (order == null && obj instanceof DecoratingProxy) { ?? ??? ??? ?return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass()); ?? ??? ?} ?? ??? ?return order; ?? ?} ?? ? ?? ?static Integer getOrderFromAnnotations(AnnotatedElement element, MergedAnnotations annotations) { ?? ??? ?if (!(element instanceof Class)) { ?? ??? ??? ?return findOrder(annotations); ?? ??? ?} ?? ??? ?// 加入緩存中 ?? ??? ?Object cached = orderCache.get(element); ?? ??? ?if (cached != null) { ?? ??? ??? ?return (cached instanceof Integer ? (Integer) cached : null); ?? ??? ?} ?? ??? ?Integer result = findOrder(annotations); ?? ??? ?orderCache.put(element, result != null ? result : NOT_ANNOTATED); ?? ??? ?return result; ?? ?} ?? ? ?? ?// 沒有找到Order注解后才去尋找@Priority注解 ?? ?private static Integer findOrder(MergedAnnotations annotations) { ?? ??? ?MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class); ?? ??? ?if (orderAnnotation.isPresent()) { ?? ??? ??? ?return orderAnnotation.getInt(MergedAnnotation.VALUE); ?? ??? ?} ?? ??? ?MergedAnnotation<?> priorityAnnotation = annotations.get(JAVAX_PRIORITY_ANNOTATION); ?? ??? ?if (priorityAnnotation.isPresent()) { ?? ??? ??? ?return priorityAnnotation.getInt(MergedAnnotation.VALUE); ?? ??? ?} ?? ??? ?return null; ?? ?}
demo
public class AnnotationAwareOrderComparatorTests { ?? ?@Test ?? ?void sortInstancesWithSubclass() { ?? ??? ?List<Object> list = new ArrayList<>(); ?? ??? ?list.add(new B()); ?? ??? ?list.add(new C()); ?? ??? ?AnnotationAwareOrderComparator.sort(list); ?? ??? ?assertThat(list.get(0) instanceof C).isTrue(); ?? ??? ?assertThat(list.get(1) instanceof B).isTrue(); ?? ?} ?? ?@Test ?? ?void sortInstancesWithOrderAndPriority() { ?? ??? ?List<Object> list = new ArrayList<>(); ?? ??? ?list.add(new B()); ?? ??? ?list.add(new A2()); ?? ??? ?AnnotationAwareOrderComparator.sort(list); ?? ??? ?assertThat(list.get(0) instanceof A2).isTrue(); ?? ??? ?assertThat(list.get(1) instanceof B).isTrue(); ?? ?} ?? ? ?? ?@Order(1) ?? ?private static class A { ?? ?} ?? ?@Order(2) ?? ?private static class B { ?? ?} ?? ? ?? ?private static class C extends A { ?? ?} ?? ?@Priority(1) ?? ?private static class A2 { ?? ?} }
小結(jié)
@Order與@Priority注解放置在類,接口或參數(shù)上,可以被繼承;它們之間是可以互相替換的關(guān)系。
應(yīng)用
spring源碼中有很多地方都顯式的調(diào)用AnnotationAwareOrderComparator的sort方法,也有一些地方調(diào)用的OrderComparator的sort方法,大家自己可以找找看。
我這里發(fā)現(xiàn)了一點有意思的地方,我們?nèi)绻x多個ControllerAdvice的bean,分別通過實現(xiàn)Ordered,PriorityOrdered接口來定義執(zhí)行時的順序,會發(fā)現(xiàn)上面我們總結(jié)的 PriorityOrdered優(yōu)先級就是比Ordered高 這一點不成立,其實只是spring將ControllerAdvice相關(guān)信息封裝了一下欺騙了我們。我看的源碼的版本是5.3.1,低于5.2版本的不會發(fā)生這樣的事情。這里我們就來看看5.2版本前后源碼有哪些變化,導(dǎo)致了這個現(xiàn)象的發(fā)生。
這里就拿RequestMappingHandlerAdapter初始化去尋找ControllerAdvice注解的代碼來舉例
?? ?private void initControllerAdviceCache() { ?? ??? ?if (getApplicationContext() == null) { ?? ??? ??? ?return; ?? ??? ?} ?? ??? ?List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); ?? ??? ?// 5.2版本前使用下面注釋的這行代碼,5.2之后這行代碼就去掉了,而是在上面findAnnotatedBeans ?? ??? ?// 方法中使用OrderComparator.sort(adviceBeans) ?? ??? ?//AnnotationAwareOrderComparator.sort(adviceBeans); ?? ??? ?... ?? ?}
我們知道OrderComparator適用范圍是比AnnotationAwareOrderComparator要窄一點的,它不支持注解,那么上面這樣的改動是不是就意味著我們定義ControllerAdvice時,就不能使用@Order與@Pri-ority呢?
其實它是支持的,ControllerAdviceBean#findAnnotatedBeans方法中會將我們定義的Con-trollerAdvice類包裝成ControllerAdviceBean,而ControllerAdviceBean是實現(xiàn)了Ordered接口的,那么OrderComparator#sort方法要想支持使用注解,ControllerAdviceBean的getOrder方法中就必須干點啥,分析了挺多,我們還是看源碼實現(xiàn)吧。
?? ?// 5.2版本后 ?? ?public int getOrder() { ?? ??? ?if (this.order == null) { ?? ??? ??? ?String beanName = null; ?? ??? ??? ?Object resolvedBean = null; ?? ??? ??? ?// 這里根據(jù)beanName獲取bean ?? ??? ??? ?if (this.beanFactory != null && this.beanOrName instanceof String) { ?? ??? ??? ??? ?beanName = (String) this.beanOrName; ?? ??? ??? ??? ?String targetBeanName = ScopedProxyUtils.getTargetBeanName(beanName); ?? ??? ??? ??? ?boolean isScopedProxy = this.beanFactory.containsBean(targetBeanName); ?? ??? ??? ??? ?if (!isScopedProxy && !ScopedProxyUtils.isScopedTarget(beanName)) { ?? ??? ??? ??? ??? ?resolvedBean = resolveBean(); ?? ??? ??? ??? ?} ?? ??? ??? ?}else { ?? ??? ??? ??? ?resolvedBean = resolveBean(); ?? ??? ??? ?} ?? ??? ??? ?// 這里只判斷了是否實現(xiàn)了Ordered接口,并沒有對實現(xiàn)PriorityOrdered作特殊處理 ?? ??? ??? ?// 這里優(yōu)先判斷是否實現(xiàn)了Ordered接口,如果同時使用注解的話將被忽略 ?? ??? ??? ?if (resolvedBean instanceof Ordered) { ?? ??? ??? ??? ?this.order = ((Ordered) resolvedBean).getOrder(); ?? ??? ??? ?}else { ?? ??? ??? ??? ?if (beanName != null && this.beanFactory instanceof ConfigurableBeanFactory) { ?? ??? ??? ??? ??? ?ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) this.beanFactory; ?? ??? ??? ??? ??? ?try { ?? ??? ??? ??? ??? ??? ?BeanDefinition bd = cbf.getMergedBeanDefinition(beanName); ?? ??? ??? ??? ??? ??? ?if (bd instanceof RootBeanDefinition) { ?? ??? ??? ??? ??? ??? ??? ?Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod(); ?? ??? ??? ??? ??? ??? ??? ?if (factoryMethod != null) { ?? ??? ??? ??? ??? ??? ??? ??? ?// 這里將會從注解@Order與@Priority中獲取order值 ?? ??? ??? ??? ??? ??? ??? ??? ?this.order = OrderUtils.getOrder(factoryMethod); ?? ??? ??? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?}catch (NoSuchBeanDefinitionException ex) { ?? ??? ??? ??? ??? ??? ?// ignore -> probably a manually registered singleton ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ?} ?? ??? ??? ??? ?if (this.order == null) { ?? ??? ??? ??? ??? ?if (this.beanType != null) { ?? ??? ??? ??? ??? ??? ?this.order = OrderUtils.getOrder(this.beanType, Ordered.LOWEST_PRECEDENCE); ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?else { ?? ??? ??? ??? ??? ??? ?this.order = Ordered.LOWEST_PRECEDENCE; ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ?} ?? ??? ??? ?} ?? ??? ?} ?? ??? ?return this.order; ?? ?}
源碼分析后,我們來看一段測試demo
public class ControllerAdviceBeanTests { ?? ?@ControllerAdvice ?? ?@Order(100) ?? ?@Priority(200) ?? ?static class OrderedControllerAdvice implements Ordered { ?? ??? ?@Override ?? ??? ?public int getOrder() { ?? ??? ??? ?return 42; ?? ??? ?} ?? ?} ?? ?@ControllerAdvice ?? ?// Order和@Priority由于Order的實現(xiàn)應(yīng)該被忽略 ?? ?@Order(100) ?? ?@Priority(200) ?? ?static class PriorityOrderedControllerAdvice implements PriorityOrdered { ?? ??? ?@Override ?? ??? ?public int getOrder() { ?? ??? ??? ?return 55; ?? ??? ?} ?? ?} ?? ?@Configuration(proxyBeanMethods = false) ?? ?static class Config { ?? ??? ?@Bean ?? ??? ?OrderedControllerAdvice orderedControllerAdvice() { ?? ??? ??? ?return new OrderedControllerAdvice(); ?? ??? ?} ?? ??? ?@Bean ?? ??? ?PriorityOrderedControllerAdvice priorityOrderedControllerAdvice() { ?? ??? ??? ?return new PriorityOrderedControllerAdvice(); ?? ??? ?} ?? ?} ?? ?@Test ?? ?@SuppressWarnings({"rawtypes", "unchecked"}) ?? ?public void findAnnotatedBeansSortsBeans() { ?? ??? ?AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); ?? ??? ?List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(context); ?? ??? ?// 輸出順序并不是 55 42,而是42,55 ?? ??? ?for (ControllerAdviceBean adviceBean : adviceBeans) { ?? ??? ??? ?System.out.println (adviceBean.getOrder ()); ?? ??? ?} ?? ?} }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
spring聲明式事務(wù) @Transactional 不回滾的多種情況以及解決方案
本文主要介紹了spring聲明式事務(wù) @Transactional 不回滾的多種情況以及解決方案,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11SpringBoot?使用定時任務(wù)(SpringTask)的詳細步驟
Cron?表達式非常靈活,可以滿足各種定時任務(wù)的需求,但需要注意的是,Cron?表達式只能表示固定的時間點,無法處理復(fù)雜的時間邏輯,本文給大家介紹SpringBoot?使用定時任務(wù)(SpringTask)的詳細步驟,感興趣的朋友一起看看吧2024-02-02Java實現(xiàn)數(shù)據(jù)庫連接的最詳細教程分享
JDBC,Java?Database?Connectivity,即Java數(shù)據(jù)庫連接,是?Java?中的一套和數(shù)據(jù)庫進行交互的API,本文就來講講Java如何利用JDBC實現(xiàn)數(shù)據(jù)庫的連接吧2023-05-05MyBatis動態(tài)SQL中的trim標(biāo)簽的使用方法
這篇文章主要介紹了MyBatis動態(tài)SQL中的trim標(biāo)簽的使用方法,需要的朋友可以參考下2017-05-05servlet的url-pattern匹配規(guī)則詳細描述(小結(jié))
在利用servlet或Filter進行url請求的匹配時,很關(guān)鍵的一點就是匹配規(guī)則。這篇文章主要介紹了servlet的url-pattern匹配規(guī)則詳細描述(小結(jié)),非常具有實用價值,需要的朋友可以參考下2018-07-07解決idea爆紅 cant resolve symbol String的問題解析
連著出差幾個禮拜沒有使用idea開發(fā)工具,突然一天打開電腦發(fā)現(xiàn)idea里的代碼全部爆紅,懵逼不如所措,很多朋友建議我按住Alt+回車設(shè)置jdk就能解決,但是仍然報錯,經(jīng)過幾個小時的倒騰最終解決,遇到此問題的朋友參考下本文吧2021-06-06