巧用Spring中的@Order進(jìn)行排序
Spring @Order進(jìn)行排序
直接上代碼
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
實(shí)際比較的方法如下
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的那點(diǎn)事
本文閱讀源碼版本為spring5.3.1
為啥要用Order
spring是一個(gè)大量使用策略設(shè)計(jì)模式的框架,這意味著有很多相同接口的實(shí)現(xiàn)類,如果不手動(dòng)指定順序的話,那么使用時(shí)肯定會(huì)有問題。而Order給我們提供了一種編碼設(shè)置順序的可能。
關(guān)于Order
spring中提供了多種方式來設(shè)置優(yōu)先級(jí),有Ordered,PriorityOrdered接口,有Order注解,除此之外,spring4.1以后,還可以使用Priority注解。下面我將針對(duì)這幾種用法從源碼的角度來進(jìn)行分析。
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,但并未提供任何方法,這是一個(gè)標(biāo)記了優(yōu)先級(jí)的接口,和Ordered相比,PriorityOrdered就是高人一等,spring中提供了比較器OrderComparator,可以通過構(gòu)建一個(gè)OrderComparator,調(diào)用其compare方法,不過OrderComparator提供了一個(gè)靜態(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)先級(jí)比Ordered高,與設(shè)置的order值無關(guān)。
- 若兩個(gè)對(duì)象都實(shí)現(xiàn)了Ordered或PriorityOrdered接口,那么設(shè)置的order值越小,優(yōu)先值越高。
- 若沒有實(shí)現(xiàn)Ordered或PriorityOrdered接口,默認(rèn)是最低的優(yōu)先級(jí)。
OrderComparator#compare解讀
在看compare之前,我覺得將OrderSourceProvider這個(gè)函數(shù)式接口放在前面講解一下,閱讀源碼時(shí)會(huì)更清晰一點(diǎn)。
?? ?@FunctionalInterface ?? ?public interface OrderSourceProvider { ?? ??? ?/** ?? ??? ? * 對(duì)給定對(duì)象校驗(yàn)并返回一個(gè)新的對(duì)象 ?? ??? ? */ ?? ??? ?@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) { ?? ??? ?// 這里會(huì)判斷是否實(shí)現(xiàn)了PriorityOrdered接口 ?? ??? ?boolean p1 = (o1 instanceof PriorityOrdered); ?? ??? ?boolean p2 = (o2 instanceof PriorityOrdered); ?? ??? ?// 這里會(huì)看到根本沒有比較order的值,只要實(shí)現(xiàn)PriorityOrdered接口,就會(huì)排在前面 ?? ??? ?if (p1 && !p2) { ?? ??? ??? ?return -1; ?? ??? ?}else if (p2 && !p1) { ?? ??? ??? ?return 1; ?? ??? ?} ?? ??? ?// 獲取對(duì)象設(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)) { ?? ??? ??? ??? ??? ??? ?// 只要找到對(duì)象設(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時(shí),返回值最大 ?? ??? ?return Ordered.LOWEST_PRECEDENCE; ?? ?} ?? ?protected Integer findOrder(Object obj) { ?? ??? ?// 沒有實(shí)現(xiàn)Ordered接口將返回null ?? ??? ?return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null); ?? ?}
@Order與@Priority
spring中提供了對(duì)@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值時(shí) ?? ??? ?return findOrderFromAnnotation(obj); ?? ?} ?? ? ?? ?private Integer findOrderFromAnnotation(Object obj) { ?? ??? ?AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass()); ?? ??? ?// 對(duì)整個(gè)類型層次結(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)了一點(diǎn)有意思的地方,我們?nèi)绻x多個(gè)ControllerAdvice的bean,分別通過實(shí)現(xiàn)Ordered,PriorityOrdered接口來定義執(zhí)行時(shí)的順序,會(huì)發(fā)現(xiàn)上面我們總結(jié)的 PriorityOrdered優(yōu)先級(jí)就是比Ordered高 這一點(diǎn)不成立,其實(shí)只是spring將ControllerAdvice相關(guān)信息封裝了一下欺騙了我們。我看的源碼的版本是5.3.1,低于5.2版本的不會(huì)發(fā)生這樣的事情。這里我們就來看看5.2版本前后源碼有哪些變化,導(dǎo)致了這個(gè)現(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要窄一點(diǎn)的,它不支持注解,那么上面這樣的改動(dòng)是不是就意味著我們定義ControllerAdvice時(shí),就不能使用@Order與@Pri-ority呢?
其實(shí)它是支持的,ControllerAdviceBean#findAnnotatedBeans方法中會(huì)將我們定義的Con-trollerAdvice類包裝成ControllerAdviceBean,而ControllerAdviceBean是實(shí)現(xiàn)了Ordered接口的,那么OrderComparator#sort方法要想支持使用注解,ControllerAdviceBean的getOrder方法中就必須干點(diǎn)啥,分析了挺多,我們還是看源碼實(shí)現(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(); ?? ??? ??? ?} ?? ??? ??? ?// 這里只判斷了是否實(shí)現(xiàn)了Ordered接口,并沒有對(duì)實(shí)現(xiàn)PriorityOrdered作特殊處理 ?? ??? ??? ?// 這里優(yōu)先判斷是否實(shí)現(xiàn)了Ordered接口,如果同時(shí)使用注解的話將被忽略 ?? ??? ??? ?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) { ?? ??? ??? ??? ??? ??? ??? ??? ?// 這里將會(huì)從注解@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的實(shí)現(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 ()); ?? ??? ?} ?? ?} }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
spring聲明式事務(wù) @Transactional 不回滾的多種情況以及解決方案
本文主要介紹了spring聲明式事務(wù) @Transactional 不回滾的多種情況以及解決方案,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11SpringBoot?使用定時(shí)任務(wù)(SpringTask)的詳細(xì)步驟
Cron?表達(dá)式非常靈活,可以滿足各種定時(shí)任務(wù)的需求,但需要注意的是,Cron?表達(dá)式只能表示固定的時(shí)間點(diǎn),無法處理復(fù)雜的時(shí)間邏輯,本文給大家介紹SpringBoot?使用定時(shí)任務(wù)(SpringTask)的詳細(xì)步驟,感興趣的朋友一起看看吧2024-02-02Java實(shí)現(xiàn)數(shù)據(jù)庫連接的最詳細(xì)教程分享
JDBC,Java?Database?Connectivity,即Java數(shù)據(jù)庫連接,是?Java?中的一套和數(shù)據(jù)庫進(jìn)行交互的API,本文就來講講Java如何利用JDBC實(shí)現(xiàn)數(shù)據(jù)庫的連接吧2023-05-05MyBatis動(dòng)態(tài)SQL中的trim標(biāo)簽的使用方法
這篇文章主要介紹了MyBatis動(dòng)態(tài)SQL中的trim標(biāo)簽的使用方法,需要的朋友可以參考下2017-05-05servlet的url-pattern匹配規(guī)則詳細(xì)描述(小結(jié))
在利用servlet或Filter進(jìn)行url請求的匹配時(shí),很關(guān)鍵的一點(diǎn)就是匹配規(guī)則。這篇文章主要介紹了servlet的url-pattern匹配規(guī)則詳細(xì)描述(小結(jié)),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-07-07解決idea爆紅 cant resolve symbol String的問題解析
連著出差幾個(gè)禮拜沒有使用idea開發(fā)工具,突然一天打開電腦發(fā)現(xiàn)idea里的代碼全部爆紅,懵逼不如所措,很多朋友建議我按住Alt+回車設(shè)置jdk就能解決,但是仍然報(bào)錯(cuò),經(jīng)過幾個(gè)小時(shí)的倒騰最終解決,遇到此問題的朋友參考下本文吧2021-06-06IDEA 2021.1 操作SVN 最新超詳細(xì)教程(圖文)
本教程將通過idea從svn服務(wù)器中的任意一個(gè)分支檢出代碼(本文采用branches),然后再idea中創(chuàng)建新的分支、提交代碼、拉取代碼、合并分支等操作進(jìn)行一一記錄,暫不包含代碼合并,對(duì)idea2021.1操作svn相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)下吧2021-05-05