欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解@Autowired是如何注入變量的

 更新時間:2023年07月20日 09:58:33   作者:江南一點雨  
在?Spring?容器中,當我們想給某一個屬性注入值的時候,有多種不同的方式,例如使用?@Autowired、@Inject等注解,下面小編就來和小伙伴們聊一聊,@Autowired?到底是如何把數(shù)據(jù)注入進來的

在 Spring 容器中,當我們想給某一個屬性注入值的時候,有多種不同的方式,例如可以通過構(gòu)造器注入、可以通過 set 方法注入,也可以使用 @Autowired、@Inject、@Resource 等注解注入。

今天松哥就來和小伙伴們聊一聊,@Autowired 到底是如何把數(shù)據(jù)注入進來的。

@Service
public?class?AService?{
????@Autowired
????BService?bService;
}

1. Bean 的創(chuàng)建

這個問題我們就得從 Bean 的創(chuàng)建開始了,本文主要是和小伙伴們聊 @Autowired,所以 Bean 的創(chuàng)建我就不從第一步開始了,咱們直接來看關(guān)鍵的方法,那就是 AbstractAutowireCapableBeanFactory#doCreateBean 方法:

protected?Object?doCreateBean(String?beanName,?RootBeanDefinition?mbd,?@Nullable?Object[]?args)
??throws?BeanCreationException?{
????//....
?Object?exposedObject?=?bean;
?try?{
??populateBean(beanName,?mbd,?instanceWrapper);
??exposedObject?=?initializeBean(beanName,?exposedObject,?mbd);
?}
?//...
?return?exposedObject;
}

在這個方法中,首先會創(chuàng)建原始的 Bean 對象,創(chuàng)建出來之后,會調(diào)用一個 populateBean 方法,這個方法就是給 Bean 的各個屬性賦值的方法,標注了 @Autowired 注解的屬性被自動賦值也是在這個方法中完成的。

2. populateBean

populateBean 方法內(nèi)容比較多,我們來看一些關(guān)鍵的地方:

protected?void?populateBean(String?beanName,?RootBeanDefinition?mbd,?@Nullable?BeanWrapper?bw)?{
?//?Give?any?InstantiationAwareBeanPostProcessors?the?opportunity?to?modify?the
?//?state?of?the?bean?before?properties?are?set.?This?can?be?used,?for?example,
?//?to?support?styles?of?field?injection.
?if?(!mbd.isSynthetic()?&&?hasInstantiationAwareBeanPostProcessors())?{
??for?(InstantiationAwareBeanPostProcessor?bp?:?getBeanPostProcessorCache().instantiationAware)?{
???if?(!bp.postProcessAfterInstantiation(bw.getWrappedInstance(),?beanName))?{
????return;
???}
??}
?}
?//...
?if?(hasInstantiationAwareBeanPostProcessors())?{
??if?(pvs?==?null)?{
???pvs?=?mbd.getPropertyValues();
??}
??for?(InstantiationAwareBeanPostProcessor?bp?:?getBeanPostProcessorCache().instantiationAware)?{
???PropertyValues?pvsToUse?=?bp.postProcessProperties(pvs,?bw.getWrappedInstance(),?beanName);
???if?(pvsToUse?==?null)?{
????return;
???}
???pvs?=?pvsToUse;
??}
?}
?boolean?needsDepCheck?=?(mbd.getDependencyCheck()?!=?AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
?if?(needsDepCheck)?{
??PropertyDescriptor[]?filteredPds?=?filterPropertyDescriptorsForDependencyCheck(bw,?mbd.allowCaching);
??checkDependencies(beanName,?mbd,?filteredPds,?pvs);
?}
?if?(pvs?!=?null)?{
??applyPropertyValues(beanName,?mbd,?bw,?pvs);
?}
}

這里松哥貼出來的是部分關(guān)鍵代碼。

首先來看上面有一個 if,這個 if 主要是判斷是否需要后置處理器進行處理,如果不需要,那么就直接 return 掉了,默認情況下,這里并不會 return 掉,而是會繼續(xù)走后面的流程,因為 postProcessAfterInstantiation 方法默認返回 true。

接下來第二個 if 就是比較關(guān)鍵的一個地方了,在這里會遍歷所有相關(guān)的后置處理器,嘗試通過這些處理器去獲取到需要的 value。

負責處理 @Autowired 注解的后置處理器是 AutowiredAnnotationBeanPostProcessor,所以現(xiàn)在,我們就來到 AutowiredAnnotationBeanPostProcessor#postProcessProperties 方法了。

3. postProcessProperties

@Override
public?PropertyValues?postProcessProperties(PropertyValues?pvs,?Object?bean,?String?beanName)?{
?InjectionMetadata?metadata?=?findAutowiringMetadata(beanName,?bean.getClass(),?pvs);
?try?{
??metadata.inject(bean,?beanName,?pvs);
?}
?catch?(BeanCreationException?ex)?{
??throw?ex;
?}
?catch?(Throwable?ex)?{
??throw?new?BeanCreationException(beanName,?"Injection?of?autowired?dependencies?failed",?ex);
?}
?return?pvs;
}

這個方法其實就兩步,第一步 findAutowiringMetadata,第二步 inject,就這兩件事。分別來看。

3.1 findAutowiringMetadata

private?InjectionMetadata?findAutowiringMetadata(String?beanName,?Class<?>?clazz,?@Nullable?PropertyValues?pvs)?{
?//?Fall?back?to?class?name?as?cache?key,?for?backwards?compatibility?with?custom?callers.
?String?cacheKey?=?(StringUtils.hasLength(beanName)???beanName?:?clazz.getName());
?//?Quick?check?on?the?concurrent?map?first,?with?minimal?locking.
?InjectionMetadata?metadata?=?this.injectionMetadataCache.get(cacheKey);
?if?(InjectionMetadata.needsRefresh(metadata,?clazz))?{
??synchronized?(this.injectionMetadataCache)?{
???metadata?=?this.injectionMetadataCache.get(cacheKey);
???if?(InjectionMetadata.needsRefresh(metadata,?clazz))?{
????if?(metadata?!=?null)?{
?????metadata.clear(pvs);
????}
????metadata?=?buildAutowiringMetadata(clazz);
????this.injectionMetadataCache.put(cacheKey,?metadata);
???}
??}
?}
?return?metadata;
}

這個方法會先嘗試從緩存中獲取 metadata,如果能夠從緩存中獲取到,那就直接返回,緩存中沒有的話,那么最終會調(diào)用到 buildAutowiringMetadata 方法,去重新構(gòu)建 metadata,并將構(gòu)建結(jié)果存入到緩存中,以備下一次使用。

那么我們來看下 metadata 到底是如何構(gòu)建出來的。

private?InjectionMetadata?buildAutowiringMetadata(Class<?>?clazz)?{
?if?(!AnnotationUtils.isCandidateClass(clazz,?this.autowiredAnnotationTypes))?{
??return?InjectionMetadata.EMPTY;
?}
?List<InjectionMetadata.InjectedElement>?elements?=?new?ArrayList<>();
?Class<?>?targetClass?=?clazz;
?do?{
??final?List<InjectionMetadata.InjectedElement>?currElements?=?new?ArrayList<>();
??ReflectionUtils.doWithLocalFields(targetClass,?field?->?{
???MergedAnnotation<?>?ann?=?findAutowiredAnnotation(field);
???if?(ann?!=?null)?{
????if?(Modifier.isStatic(field.getModifiers()))?{
?????if?(logger.isInfoEnabled())?{
??????logger.info("Autowired?annotation?is?not?supported?on?static?fields:?"?+?field);
?????}
?????return;
????}
????boolean?required?=?determineRequiredStatus(ann);
????currElements.add(new?AutowiredFieldElement(field,?required));
???}
??});
??ReflectionUtils.doWithLocalMethods(targetClass,?method?->?{
???Method?bridgedMethod?=?BridgeMethodResolver.findBridgedMethod(method);
???if?(!BridgeMethodResolver.isVisibilityBridgeMethodPair(method,?bridgedMethod))?{
????return;
???}
???MergedAnnotation<?>?ann?=?findAutowiredAnnotation(bridgedMethod);
???if?(ann?!=?null?&&?method.equals(ClassUtils.getMostSpecificMethod(method,?clazz)))?{
????if?(Modifier.isStatic(method.getModifiers()))?{
?????if?(logger.isInfoEnabled())?{
??????logger.info("Autowired?annotation?is?not?supported?on?static?methods:?"?+?method);
?????}
?????return;
????}
????if?(method.getParameterCount()?==?0)?{
?????if?(logger.isInfoEnabled())?{
??????logger.info("Autowired?annotation?should?only?be?used?on?methods?with?parameters:?"?+
????????method);
?????}
????}
????boolean?required?=?determineRequiredStatus(ann);
????PropertyDescriptor?pd?=?BeanUtils.findPropertyForMethod(bridgedMethod,?clazz);
????currElements.add(new?AutowiredMethodElement(method,?required,?pd));
???}
??});
??elements.addAll(0,?currElements);
??targetClass?=?targetClass.getSuperclass();
?}
?while?(targetClass?!=?null?&&?targetClass?!=?Object.class);
?return?InjectionMetadata.forElements(elements,?clazz);
}

這個方法比較長,我來和大家說一下核心邏輯。

首先會調(diào)用 isCandidateClass 方法判斷當前類是否為一個候選類,判斷的依據(jù)就是 autowiredAnnotationTypes 變量的值,這個變量在該類的構(gòu)造方法中進行了初始化,大家來看下這個構(gòu)造方法:

public?AutowiredAnnotationBeanPostProcessor()?{
?this.autowiredAnnotationTypes.add(Autowired.class);
?this.autowiredAnnotationTypes.add(Value.class);
?try?{
??this.autowiredAnnotationTypes.add((Class<??extends?Annotation>)
????ClassUtils.forName("jakarta.inject.Inject",?AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
??logger.trace("'jakarta.inject.Inject'?annotation?found?and?supported?for?autowiring");
?}
?catch?(ClassNotFoundException?ex)?{
??//?jakarta.inject?API?not?available?-?simply?skip.
?}
?try?{
??this.autowiredAnnotationTypes.add((Class<??extends?Annotation>)
????ClassUtils.forName("javax.inject.Inject",?AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
??logger.trace("'javax.inject.Inject'?annotation?found?and?supported?for?autowiring");
?}
?catch?(ClassNotFoundException?ex)?{
??//?javax.inject?API?not?available?-?simply?skip.
?}
}

小伙伴們看到,autowiredAnnotationTypes 集合中有兩個注解是固定的:@Autowired 和 @Value,另外就是如果項目引入了 JSR-330 依賴,則 @Inject 注解也會被加入進來,以前 @Inject 存在于 javax 包中,現(xiàn)在最新版 @Inject 注解存在于 jakarta 包中,這里把兩種情況都列出來了。

所以,isCandidateClass 方法實際上就是判斷當前類在類、屬性、方法等層面上是否存在上述三個注解,如果存在,則就是候選類,否則就不是候選類。如果不是候選類則返回一個空的 InjectionMetadata 對象,否則就繼續(xù)后面的流程。

后面的流程,首先是一個 do{}while() 結(jié)構(gòu),通過這個循環(huán)把當前類以及當前類的父類中的滿足條件的注解都找出來。具體的找法就是首先調(diào)用 ReflectionUtils.doWithLocalFields 方法,這個方法會遍歷當前類的所有屬性,找到那些包含了 autowiredAnnotationTypes 中定義的注解的屬性,并將之封裝為 AutowiredFieldElement 對象,然后存入到集合中,接下來就是調(diào)用 ReflectionUtils.doWithLocalMethods,這個是找到當前類中包含了上述三個注解的方法,然后把找到的滿足條件的方法封裝為 AutowiredMethodElement 然后存入到集合中。

另外大家需要注意,無論是 AutowiredFieldElement 還是 AutowiredMethodElement,都是 InjectionMetadata.InjectedElement 的子類。

這就是 findAutowiringMetadata 方法所做的事情,整體上來看,就是查找到添加了 @Autowired 或者 @Value 或者 @Inject 注解的屬性或者方法,并將之存入到集合中。

3.2 inject

接下來就該調(diào)用 metadata.inject 了,我們來看下該方法:

public?void?inject(Object?target,?@Nullable?String?beanName,?@Nullable?PropertyValues?pvs)?throws?Throwable?{
?Collection<InjectedElement>?checkedElements?=?this.checkedElements;
?Collection<InjectedElement>?elementsToIterate?=
???(checkedElements?!=?null???checkedElements?:?this.injectedElements);
?if?(!elementsToIterate.isEmpty())?{
??for?(InjectedElement?element?:?elementsToIterate)?{
???element.inject(target,?beanName,?pvs);
??}
?}
}

這里就是遍歷剛剛上一步收集到的 InjectedElement,然后挨個調(diào)用其 inject 方法進行屬性注入。以本文一開始的 demo 為例,@Autowired 注解加在屬性上面,所以我們這里實際上調(diào)用的是 AutowiredFieldElement#inject 方法:

@Override
protected?void?inject(Object?bean,?@Nullable?String?beanName,?@Nullable?PropertyValues?pvs)?throws?Throwable?{
?Field?field?=?(Field)?this.member;
?Object?value;
?if?(this.cached)?{
??try?{
???value?=?resolvedCachedArgument(beanName,?this.cachedFieldValue);
??}
??catch?(NoSuchBeanDefinitionException?ex)?{
???//?Unexpected?removal?of?target?bean?for?cached?argument?->?re-resolve
???value?=?resolveFieldValue(field,?bean,?beanName);
??}
?}
?else?{
??value?=?resolveFieldValue(field,?bean,?beanName);
?}
?if?(value?!=?null)?{
??ReflectionUtils.makeAccessible(field);
??field.set(bean,?value);
?}
}

這段代碼首先會調(diào)用 resolvedCachedArgument 方法嘗試從緩存中獲取想要的對象,如果緩存中存在,則可以直接使用,如果緩存中沒有,則調(diào)用 resolveFieldValue 方法去獲取。獲取到之后,通過反射調(diào)用 set 方法進行賦值就可以了。所以關(guān)鍵步驟又來到了 resolveFieldValue 方法中。

用緩存的好處就是,獲取到對象存入到緩存之后,如果相同的 Bean 在多個類中注入,那么只有第一次需要去加載,以后就直接用緩存中的數(shù)據(jù)即可。

@Nullable
private?Object?resolveFieldValue(Field?field,?Object?bean,?@Nullable?String?beanName)?{
?//...
?Object?value;
?try?{
??value?=?beanFactory.resolveDependency(desc,?beanName,?autowiredBeanNames,?typeConverter);
?}
????//...
?return?value;
}

這個方法的核心其實就是通過 beanFactory.resolveDependency 方法獲取到想要的 Bean 對象,我們直接來看這個核心方法,由于 BeanFactory 是一個接口,所以這個方法的實現(xiàn)實際上是在 DefaultListableBeanFactory#resolveDependency:

@Nullable
public?Object?resolveDependency(DependencyDescriptor?descriptor,?@Nullable?String?requestingBeanName,
??@Nullable?Set<String>?autowiredBeanNames,?@Nullable?TypeConverter?typeConverter)?throws?BeansException?{
?descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
?if?(Optional.class?==?descriptor.getDependencyType())?{
??return?createOptionalDependency(descriptor,?requestingBeanName);
?}
?else?if?(ObjectFactory.class?==?descriptor.getDependencyType()?||
???ObjectProvider.class?==?descriptor.getDependencyType())?{
??return?new?DependencyObjectProvider(descriptor,?requestingBeanName);
?}
?else?if?(javaxInjectProviderClass?==?descriptor.getDependencyType())?{
??return?new?Jsr330Factory().createDependencyProvider(descriptor,?requestingBeanName);
?}
?else?{
??Object?result?=?getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
????descriptor,?requestingBeanName);
??if?(result?==?null)?{
???result?=?doResolveDependency(descriptor,?requestingBeanName,?autowiredBeanNames,?typeConverter);
??}
??return?result;
?}
}

這里一共是四個分支,處理四種不同的情況,分別是 Optional、ObjectFactory、JSR-330 以及其他情況,很明顯,文章開頭的案例應該屬于第四種情況,我們繼續(xù)來看 doResolveDependency 方法。

3.3 doResolveDependency

這個方法也是比較長,我列出來了一些關(guān)鍵的部分:

@Nullable
public?Object?doResolveDependency(DependencyDescriptor?descriptor,?@Nullable?String?beanName,
??@Nullable?Set<String>?autowiredBeanNames,?@Nullable?TypeConverter?typeConverter)?throws?BeansException?{
????????//...
??Object?multipleBeans?=?resolveMultipleBeans(descriptor,?beanName,?autowiredBeanNames,?typeConverter);
??if?(multipleBeans?!=?null)?{
???return?multipleBeans;
??}
??Map<String,?Object>?matchingBeans?=?findAutowireCandidates(beanName,?type,?descriptor);
??if?(matchingBeans.isEmpty())?{
???if?(isRequired(descriptor))?{
????raiseNoMatchingBeanFound(type,?descriptor.getResolvableType(),?descriptor);
???}
???return?null;
??}
??String?autowiredBeanName;
??Object?instanceCandidate;
??if?(matchingBeans.size()?>?1)?{
???autowiredBeanName?=?determineAutowireCandidate(matchingBeans,?descriptor);
???if?(autowiredBeanName?==?null)?{
????if?(isRequired(descriptor)?||?!indicatesMultipleBeans(type))?{
?????return?descriptor.resolveNotUnique(descriptor.getResolvableType(),?matchingBeans);
????}
????else?{
?????//?In?case?of?an?optional?Collection/Map,?silently?ignore?a?non-unique?case:
?????//?possibly?it?was?meant?to?be?an?empty?collection?of?multiple?regular?beans
?????//?(before?4.3?in?particular?when?we?didn't?even?look?for?collection?beans).
?????return?null;
????}
???}
???instanceCandidate?=?matchingBeans.get(autowiredBeanName);
??}
??else?{
???//?We?have?exactly?one?match.
???Map.Entry<String,?Object>?entry?=?matchingBeans.entrySet().iterator().next();
???autowiredBeanName?=?entry.getKey();
???instanceCandidate?=?entry.getValue();
??}
??Object?result?=?instanceCandidate;
??return?result;
?}
?finally?{
??ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
?}
}

首先是調(diào)用 resolveMultipleBeans 方法去查找多個 Bean,這是因為我們在注入的時候,可以注入數(shù)組、集合和 Map,例如像下面這樣:

@Service
public?class?AService?{
????@Autowired
????BService?bService;
????@Autowired
????BService[]?bServices;
????@Autowired
????List<BService>?bServiceList;
????@Autowired
????Map<String,?BService>?bServiceMap;
}

具體查找方法如下:

@Nullable
private?Object?resolveMultipleBeans(DependencyDescriptor?descriptor,?@Nullable?String?beanName,
??@Nullable?Set<String>?autowiredBeanNames,?@Nullable?TypeConverter?typeConverter)?{
?Class<?>?type?=?descriptor.getDependencyType();
?if?(descriptor?instanceof?StreamDependencyDescriptor?streamDependencyDescriptor)?{
??Map<String,?Object>?matchingBeans?=?findAutowireCandidates(beanName,?type,?descriptor);
??//...
??return?stream;
?}
?else?if?(type.isArray())?{
??Class<?>?componentType?=?type.getComponentType();
??ResolvableType?resolvableType?=?descriptor.getResolvableType();
??Map<String,?Object>?matchingBeans?=?findAutowireCandidates(beanName,?componentType,
????new?MultiElementDescriptor(descriptor));
??return?result;
?}
?else?if?(Collection.class.isAssignableFrom(type)?&&?type.isInterface())?{
??Class<?>?elementType?=?descriptor.getResolvableType().asCollection().resolveGeneric();
??Map<String,?Object>?matchingBeans?=?findAutowireCandidates(beanName,?elementType,
????new?MultiElementDescriptor(descriptor));
??return?result;
?}
?else?if?(Map.class?==?type)?{
??ResolvableType?mapType?=?descriptor.getResolvableType().asMap();
??Map<String,?Object>?matchingBeans?=?findAutowireCandidates(beanName,?valueType,
????new?MultiElementDescriptor(descriptor));
??return?matchingBeans;
?}
?else?{
??return?null;
?}
}

這里會首先判斷你的數(shù)據(jù)類型,針對 Stream、數(shù)組、集合 以及 Map 分別處理,處理代碼都很好懂,以集合為例,首先獲取到集合中的泛型,然后調(diào)用 findAutowireCandidates 方法根據(jù)泛型去查找到 Bean,處理一下返回就行了,其他幾種數(shù)據(jù)類型也都差不多。

至于 findAutowireCandidates 方法的邏輯,我們就不去細看了,我大概和小伙伴們說一下,就是先根據(jù) Bean 的類型,調(diào)用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法去當前容器連同父容器中,查找到所有滿足條件的 Bean,處理之后返回。

接下來回到本小節(jié)一開始的源碼中,處理完集合之后,接下來也是調(diào)用 findAutowireCandidates 方法去查找滿足條件的 Bean,但是這個方法查找出來的 Bean 可能有多個,如果存在多個,則要通過 @Primary 注解或者其他優(yōu)先級順序,去確定到底使用哪一個(執(zhí)行 determineAutowireCandidate 方法),如果查找到一個 Bean,那就把找到的 Bean 返回即可。

這就是 @Autowired 一個完整的解析過程。

4. 時序圖

最后,結(jié)合如下時序圖,我再和小伙伴們梳理一下上面的過程。

1.在創(chuàng)建 Bean 的時候,原始 Bean 創(chuàng)建出來之后,會調(diào)用 populateBean 方法進行 Bean 的屬性填充。

2.接下來調(diào)用 postProcessAfterInstantiation 方法去判斷是否需要執(zhí)行后置處理器,如果不需要,就直接返回了。

3.調(diào)用 postProcessProperties 方法,去觸發(fā)各種后置處理器的執(zhí)行。

4.在第 3 步的方法中,調(diào)用 findAutowiringMetadata,這個方法又會進一步觸發(fā) buildAutorwiringMetadata 方法,去找到包含了 @Autowired、@Value 以及 @Inject 注解的屬性或者方法,并將之封裝為 InjectedElement 返回。

5.調(diào)用 InjectedElement#inject 方法進行屬性注入。

6.接下來執(zhí)行 resolvedCachedArgument 方法嘗試從緩存中找到需要的 Bean 對象。

7.如果緩存中不存在,則調(diào)用 resolveFieldValue 方法去容器中找到 Bean。

8.最后調(diào)用 makeAccessible 和 set 方法完成屬性的賦值。

以上就是詳解@Autowired是如何注入變量的的詳細內(nèi)容,更多關(guān)于@Autowired注入變量的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JDK的具體安裝步驟(帶圖帶解釋巨詳細)

    JDK的具體安裝步驟(帶圖帶解釋巨詳細)

    Java是一種廣泛使用的編程語言,許多應用程序和系統(tǒng)都依賴于它,如果您想進行Java編程或運行Java應用程序,首先需要安裝Java開發(fā)工具包(JDK),這篇文章主要給大家介紹了關(guān)于JDK具體安裝步驟的相關(guān)資料,文中介紹的方法帶圖帶解釋巨詳細,需要的朋友可以參考下
    2024-05-05
  • 一文教你如何判斷Java代碼中異步操作是否完成

    一文教你如何判斷Java代碼中異步操作是否完成

    在許多應用程序中,我們經(jīng)常使用異步操作來提高性能和響應度,這篇文章主要介紹了幾種常見的方法來判斷Java代碼中異步操作是否完成,希望對大家有所幫助
    2024-02-02
  • Eclipse?Jetty?server漏洞解決辦法

    Eclipse?Jetty?server漏洞解決辦法

    最近給?個客戶部署項?,但是客戶的安全稽核有點變態(tài),居然說 Eclipse Jetty Server?危漏洞,這篇文章主要給大家介紹了關(guān)于Eclipse?Jetty?server漏洞解決的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • Mybatis-plus批量插入的2種方式總結(jié)

    Mybatis-plus批量插入的2種方式總結(jié)

    這篇文章主要給大家總結(jié)介紹了關(guān)于Mybatis-plus批量插入的2種方式,Mybatis-Plus提供了多種方式進行批量插入優(yōu)化,文中通過代碼示例將實現(xiàn)的方法介紹的非常詳細,需要的朋友可以參考下
    2023-08-08
  • spring-boot-maven-plugin引入出現(xiàn)爆紅(已解決)

    spring-boot-maven-plugin引入出現(xiàn)爆紅(已解決)

    這篇文章主要介紹了spring-boot-maven-plugin引入出現(xiàn)爆紅(已解決),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • IDEA在plugins里搜不到mybatisx插件的解決方法

    IDEA在plugins里搜不到mybatisx插件的解決方法

    本文主要介紹了IDEA在plugins里搜不到mybatisx插件的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • Spring?Cloud?Gateway中netty線程池優(yōu)化示例詳解

    Spring?Cloud?Gateway中netty線程池優(yōu)化示例詳解

    這篇文章主要介紹了Spring?Cloud?Gateway中netty線程池優(yōu)化示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • springboot項目如何配置多數(shù)據(jù)源

    springboot項目如何配置多數(shù)據(jù)源

    本文介紹了如何在SpringBoot項目中配置多數(shù)據(jù)源,包括配置多個數(shù)據(jù)源、創(chuàng)建數(shù)據(jù)源配置類、配置事務管理器以及使用不同的Mapper,從而實現(xiàn)跨數(shù)據(jù)庫操作
    2025-03-03
  • Java中Cookie和Session的那些事兒

    Java中Cookie和Session的那些事兒

    Cookie和Session都是為了保持用戶的訪問狀態(tài),一方面為了方便業(yè)務實現(xiàn),另一方面為了簡化服務端的程序設計。這篇文章主要介紹了java中cookie和session的知識,需要的朋友可以參考下
    2016-09-09
  • 使用Spring Security集成手機驗證碼登錄功能實現(xiàn)

    使用Spring Security集成手機驗證碼登錄功能實現(xiàn)

    本文詳細介紹了如何利用SpringSecurity來實現(xiàn)手機驗證碼的注冊和登錄功能,在登錄過程中,同樣需通過驗證碼進行驗證,文章還提供了相關(guān)的代碼實現(xiàn)
    2024-10-10

最新評論