Spring Boot示例分析講解自動(dòng)化裝配機(jī)制核心注解
1. 自動(dòng)化裝配介紹
Spring Boot針對(duì)mvc做了大量封裝,簡(jiǎn)化開發(fā)者的使用,內(nèi)部是如何管理資源配置,Bean配置,環(huán)境變量配置以及啟動(dòng)配置等? 實(shí)質(zhì)是SpringBoot做了大量的注解封裝,比如@SpringBootApplication, 同時(shí)采用Spring 4框架的新特性@Conditional基于條件的Bean創(chuàng)建管理機(jī)制來實(shí)現(xiàn);
實(shí)際的工作場(chǎng)景中是復(fù)雜多樣的, 有些項(xiàng)目需要不同的組件, 比如REDIS、MONGODB作緩存; RABBITMQ、KAFKA作消息隊(duì)列; 有些項(xiàng)目運(yùn)行環(huán)境不同, 比如JDK7、JDK8不同版本,面對(duì)眾多復(fù)雜的需求, 又要做到最大化支持, Spring Boot是如何管理實(shí)現(xiàn)的, 這就依賴Conditional功能,基于條件的自動(dòng)化配置。
2. Spring Boot 自動(dòng)化配置UML圖解
SpringBootApplication是我們所常用熟知的注解, 它是一個(gè)組合注解, 依賴多個(gè)注解,共同實(shí)現(xiàn)Spring Boot應(yīng)用功能, 以下為所有依賴的UML圖解,我們圍繞這些注解深入研究,看下具體的實(shí)現(xiàn)。
3. Spring Boot 自動(dòng)化配置核心注解分析
SpringBootApplication注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) @ConfigurationPropertiesScan public @interface SpringBootApplication { /** * 需要排除的自動(dòng)化配置, 根據(jù)類名進(jìn)行排除, 比如MongoAutoConfiguration, JpaRepositoriesAutoConfiguration等 */ @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; /** * 需要排除的自動(dòng)化配置, 根據(jù)名稱進(jìn)行排除 */ @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; /** * 指定需要掃描的包路徑,參數(shù)填寫包名 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; /** * 指定需要掃描的包路徑 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; /** * Bean方法的動(dòng)態(tài)代理配置, 如果沒有采用工廠方法, 可以標(biāo)記為false, 采用cglib代理。 */ @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods() default true; }
3.1 @Inherited
java.lang.annotation.@Inherited 注解,從包名可以看出為JDK自帶注解, 作用是讓子類能夠繼承父類中引用Inherited的注解, 但需注意的是, 該注解作用范圍只在類聲明中有效; 如果是接口與接口的繼承, 類與接口的繼承, 是不會(huì)生效。
3.2 @SpringBootConfiguration
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration(proxyBeanMethods = false) public @interface SpringBootConfiguration { /** * Bean方法的動(dòng)態(tài)代理配置 */ @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods() default true; }
這是配置型處理注解, 可以看到內(nèi)部源碼引用了@Configuration注解,
自身沒有太多的實(shí)現(xiàn), 那為什么還需要再包裝?官方給出的解釋是對(duì)Spring的@Configuration的擴(kuò)展,
用于實(shí)現(xiàn)SpringBoot的自動(dòng)化配置。proxyBeanMethods屬性默認(rèn)為true, 作用是對(duì)bean的方法是否開啟代理方式調(diào)用, 默認(rèn)為true, 如果沒有采用工廠方法,可以設(shè)為false, 通過cglib作動(dòng)態(tài)代理。
3.3 @EnableAutoConfiguration
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { // 設(shè)置注解支持重載的標(biāo)識(shí) String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; /** * 排除自動(dòng)化配置的組件, 如MongoAutoConfiguration, JpaRepositoriesAutoConfiguration等 */ Class<?>[] exclude() default {}; /** * 排除自動(dòng)化配置的組件, 根據(jù)名稱設(shè)置 */ String[] excludeName() default {}; }
用于管理開啟Spring Boot的各種自動(dòng)化配置注解, 如datasource, mongodb, redis等,也是spring-boot-autoconfigure工程的核心注解。
AutoConfigurationPackage
它的主要作用是掃描主程序同級(jí)及下級(jí)的包路徑所有Bean與組件注冊(cè)到Spring Ioc容器中。
Import
它可以把沒有聲明配置的類注冊(cè)到Spring Ioc容器中管理引用。 導(dǎo)入的AutoConfigurationImportSelector類實(shí)現(xiàn)BeanClassLoaderAware、ResourceLoaderAware、EnvironmentAware等接口, 管理類裝載器, 資源裝載器及環(huán)境配置等, 是一個(gè)負(fù)責(zé)處理自動(dòng)化配置導(dǎo)入的選擇管理器。在下面【@AutoConfigurationImportSelector剖析】進(jìn)行詳解。
3.4 @ComponentScan
這是我們?cè)赟pring下面常用的一個(gè)注解,它可以掃描Spring定義的注解, 如@Componment, @Service等, 常用的屬性有basePackages掃描路徑,includeFilters包含路徑過濾器, excludeFilters排除路徑過濾器,lazyInit是否懶加載等,能夠非常靈活的掃描管理需要注冊(cè)組件。
3.5 @ConfigurationPropertiesScan
作用是掃描指定包及子包路徑下面的ConfigurationProperties注解,管理工程配置屬性信息。主要屬性為basePackages掃描路徑, 支持多個(gè)路徑,數(shù)組形式;basePackageClasses屬性也可以具體到包下面的類,
支持多個(gè)配置。
3.6 @AutoConfigurationImportSelector
AutoConfigurationImportSelector 實(shí)現(xiàn) DeferredImportSelector、BeanClassLoaderAware、ResourceLoaderAware、BeanFactoryAware、EnvironmentAware、Ordered 接口, 為自動(dòng)化配置的核心處理類, 主要負(fù)責(zé)自動(dòng)化配置規(guī)則的一系列處理邏輯:
/** * {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration * auto-configuration}. This class can also be subclassed if a custom variant of * {@link EnableAutoConfiguration @EnableAutoConfiguration} is needed. * * @author Phillip Webb * @author Andy Wilkinson * @author Stephane Nicoll * @author Madhura Bhave * @since 1.3.0 * @see EnableAutoConfiguration */ public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { ... } }
講解幾個(gè)技術(shù)點(diǎn):
getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }
該方法是獲取所有Spring Boot聲明定義的自動(dòng)化配置類。
看下具體有哪些信息:
這些實(shí)際是配置在Spring-boot-autoconfigure工程下的META-INF/spring.factories文件中:
看到這里, 我們應(yīng)該可以明白,為什么AOP,RABBIT,DATASOURCE, REIDS等組件SPRING BOOT都能幫我們快速配置實(shí)現(xiàn),其實(shí)它內(nèi)部遵循SPI機(jī)制, 已經(jīng)把自動(dòng)化配置做好了封裝。
AutoConfigurationGroup類
它是AutoConfigurationImportSelector的內(nèi)部類,實(shí)現(xiàn)了DeferredImportSelector.Group、BeanClassLoaderAware、BeanFactoryAware、ResourceLoaderAware接口,是一個(gè)重要的核心類。主要作用是負(fù)責(zé)自動(dòng)化配置條目信息的記錄, 排序,元數(shù)據(jù)處理等。它通過getImportGroup方法獲取返回,該方法實(shí)現(xiàn)DeferredImportSelector的接口。
private static class AutoConfigurationGroup implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware { // 記錄注解的元數(shù)據(jù) private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>(); // 記錄自動(dòng)化配置條目,放入集合 private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>(); // 設(shè)置bean的類加載器 private ClassLoader beanClassLoader; // 設(shè)置bean工廠信息 private BeanFactory beanFactory; // 設(shè)置資源加載器信息 private ResourceLoader resourceLoader; // 設(shè)置自動(dòng)化配置的元數(shù)據(jù)記錄 private AutoConfigurationMetadata autoConfigurationMetadata; ... }
屬性主要定義了一些自動(dòng)化配置類目信息、BEAN工廠、類和資源加載器信息。entries條目有22條, 具體內(nèi)容如下:
里面是主要的自動(dòng)化配置類的元數(shù)據(jù)信息,autoConfigurationEntries屬性就是具體的自動(dòng)化配置條目。這些主要自動(dòng)化類配置是Spring boot幫助我們實(shí)現(xiàn)mvc的核心功能,如請(qǐng)求分發(fā),文件上傳,參數(shù)驗(yàn)證,編碼轉(zhuǎn)換等功能。還有一部分是定制條件自動(dòng)化配置類,
autoConfigurationMetadata元數(shù)據(jù)內(nèi)容較多, 包含各種組件, 根據(jù)環(huán)境配置和版本不同, 這里可以看到共有705個(gè):
由于Spring Boot支持眾多插件,功能豐富, 數(shù)量較多; 這里存在些疑問, 這里面的元數(shù)據(jù)和上面的entries條目都是AutoConfiguration自動(dòng)化配置類, 那有什么區(qū)別? 其實(shí)這里面的, 都是基于條件的自動(dòng)化配置。
我們就拿KafkaAutoConfiguration來看:
可以看到注解ConditionalOnClass,意思是KafkaAutoConfiguration生效的前提是基于KafkaTemplate類的初始化成功,這就是定制條件,也就是基于條件的自動(dòng)化配置類,雖然有七百多個(gè),但其實(shí)是根據(jù)工程實(shí)際用到的組件,才會(huì)觸發(fā)加載對(duì)應(yīng)的配置。 有關(guān)Conditional基于條件的自動(dòng)化配置實(shí)現(xiàn)原理, 在下面我們?cè)僮魃钊胙芯俊?/p>
繼續(xù)看AutoConfigurationImportSelector內(nèi)部類的selectImports方法:
@Override public Iterable<Entry> selectImports() { if (this.autoConfigurationEntries.isEmpty()) { return Collections.emptyList(); } // 將所有自動(dòng)化條目根據(jù)配置的Exclusion條件作過濾, 并轉(zhuǎn)換為SET集合 Set<String> allExclusions = this.autoConfigurationEntries.stream() .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet()); // SET集合, 記錄所有需要處理的自動(dòng)化配置 Set<String> processedConfigurations = this.autoConfigurationEntries.stream() .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream) .collect(Collectors.toCollection(LinkedHashSet::new)); // 兩個(gè)SET, 做交集過濾, 排除不需要的配置 processedConfigurations.removeAll(allExclusions); // 最后進(jìn)行排序處理 return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream() .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) .collect(Collectors.toList()); }
該方法是針對(duì)autoConfigurationEntries自動(dòng)化配置條目做過濾,根據(jù)指定的排除規(guī)則處理;再根據(jù)設(shè)置的啟動(dòng)的優(yōu)先級(jí)做排序整理。從代碼中可以看到,先獲取所有的allExclusions排除配置信息,再獲取所有需要處理的processedConfigurations配置信息,然后做過濾處理,最后再調(diào)用sortAutoConfigurations方法,根據(jù)order順序做排序整理。
AutoConfigurationImportSelector內(nèi)部類的process方法:
@Override public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); // 獲取自動(dòng)化配置條目 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); // 記錄獲取的條目 this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { // 放入成員變量entries中 this.entries.putIfAbsent(importClassName, annotationMetadata); } }
該方法是掃描獲取autoConfigurationEntries自動(dòng)化配置條目信息。
annotationMetadata參數(shù):
為注解元數(shù)據(jù),有也就是被@SpringBootApplication修飾的類信息,在這里就是我們的啟動(dòng)入口類信息。
deferredImportSelector參數(shù):
通過@EnableAutoConfiguration注解定義的 @Import 的類,也就是AutoConfigurationImportSelector對(duì)象。根據(jù)配置,會(huì)加載指定的beanFactory、classLoader、resourceLoader和environment對(duì)象。
AutoConfigurationImportSelector內(nèi)部類的getAutoConfigurationEntry方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { // 1、判斷是否開對(duì)應(yīng)注解 if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 2、獲取注解定義的屬性 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 3、獲取符合規(guī)則的Spring Boot 內(nèi)置的自動(dòng)化配置類, 并做去重處理 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); // 4、做排除規(guī)則匹配, 過濾處理 Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); // 5、觸發(fā)自動(dòng)導(dǎo)入處理完成事件 fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
該方法主要作用是獲取Spring Boot 內(nèi)置的自動(dòng)化條目, 例AopAutoConfiguration等,該方法會(huì)調(diào)用上面講解的getCandidateConfigurations方法。 主要步驟邏輯如下:
- 判斷是否開啟元注解掃描, 對(duì)應(yīng)屬性為spring.boot.enableautoconfiguration,默認(rèn)情況下, 是開啟自動(dòng)配置。
- 獲取定義的注解屬性, 跟蹤內(nèi)部源碼, 里面會(huì)返回exclude和excludeName等屬性。
- 獲取符合規(guī)則的Spring Boot 內(nèi)置的自動(dòng)化配置, 并做去重處理,也就是我們上面講解的getCandidateConfigurations方法, 從中我們就可以理解其中的關(guān)聯(lián)關(guān)系。
- 做排除規(guī)則檢查與過濾處理, 根據(jù)上面第2個(gè)步驟獲取的exclude等屬性以及配置屬性spring.autoconfigure.exclude做過濾處理。
- 觸發(fā)自動(dòng)導(dǎo)入完成事件, 該方法內(nèi)部邏輯正常處理完成才會(huì)觸發(fā),會(huì)調(diào)用AutoConfigurationImportListener監(jiān)聽器做通知處理。
3.7 @AutoConfigurationPackages
AutoConfigurationPackages是EnableAutoConfiguration上的另一個(gè)核心注解類, 官方解釋為:
Indicates that the package containing the annotated class should be registered
意思是包含該注解的類,所在包下面的class, 都會(huì)注冊(cè)到Spring Ioc容器中。對(duì)應(yīng)源碼:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { ... }
Import注解, 導(dǎo)入AutoConfigurationPackages抽象類下面的內(nèi)部靜態(tài)類Registrar,研究Registrar實(shí)現(xiàn)原理:
Registrar實(shí)現(xiàn) ImportBeanDefinitionRegistrar、DeterminableImports 接口,它負(fù)責(zé)存儲(chǔ)從@AutoConfigurationPackage注解掃描到的信息。 源碼如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { // 注冊(cè)BEAN的定義信息 @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport(metadata).getPackageName()); } // 決定是否導(dǎo)入注解中的配置內(nèi)容 @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); } }
這里面主要涉及到PackageImport類, 它是AutoConfigurationPackages的內(nèi)部私有靜態(tài)類,主要是記錄導(dǎo)入的 報(bào)名信息, 源碼如下:
/** * Wrapper for a package import. */ private static final class PackageImport { private final String packageName; // 構(gòu)造方法, 記錄注解內(nèi)容 PackageImport(AnnotationMetadata metadata) { this.packageName = ClassUtils.getPackageName(metadata.getClassName()); } // 獲取指定包名稱 public String getPackageName() { return this.packageName; } // 重載父類比較邏輯, 根據(jù)包名判斷 @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) { return false; } return this.packageName.equals(((PackageImport) obj).packageName); } // 重載hash標(biāo)識(shí), 以包名的HASH值為準(zhǔn) @Override public int hashCode() { return this.packageName.hashCode(); } // 重載toString, 打印內(nèi)容 @Override public String toString() { return "Package Import " + this.packageName; } }
內(nèi)部斷點(diǎn)跟蹤的話, 可以看到它記錄的是我們啟動(dòng)類所在的包名。這也就是為什么不需要指定掃描包路徑, 也會(huì)加載啟動(dòng)類所在包下面的JavaConfig配置信息。
回到上面Registrar的registerBeanDefinitions方法, 內(nèi)部調(diào)用的是register方法:
它是處理記錄AutoConfigurationPackages掃描包信息,源碼如下:
public static void register(BeanDefinitionRegistry registry, String... packageNames) { // 判斷是否包含BEAN定義信息, 如果包含, 更新packageNames信息 if (registry.containsBeanDefinition(BEAN)) { BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN); ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues(); constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames)); } // 如果registry中不包含BEAN定義, 重新構(gòu)造GenericBeanDefinition對(duì)象, 記錄相關(guān)信息 else { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(BasePackages.class); beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(BEAN, beanDefinition); } }
先判斷AutoConfigurationPackages注解, 記錄對(duì)應(yīng)的掃描包信息;如果不存在,則自行創(chuàng)建基于BasePackages的BEAN定義信息, 并進(jìn)行注冊(cè)。再看下addBasePackages方法:
private static String[] addBasePackages(ConstructorArgumentValues constructorArguments, String[] packageNames) { // 獲取已經(jīng)存在的Bean定義信息 String[] existing = (String[]) constructorArguments.getIndexedArgumentValue(0, String[].class).getValue(); // 創(chuàng)建合并集合, 過濾重復(fù)的Bean定義 Set<String> merged = new LinkedHashSet<>(); // 根據(jù)Set特性, 自動(dòng)合并去重 merged.addAll(Arrays.asList(existing)); merged.addAll(Arrays.asList(packageNames)); return StringUtils.toStringArray(merged); }
獲取已經(jīng)存在的定義信息,再和packageNames合并, 過濾重復(fù)的掃描包。
自動(dòng)化配置到此就不再對(duì)其他代碼進(jìn)行深入跟蹤分析,Spring Boot整個(gè)框架代碼還是較多, 大家可以按這種思路, 逐個(gè)層級(jí)去剖析,深入挖掘更多技術(shù)點(diǎn)。
4. 總結(jié)
我們研究了Spring Boot的自動(dòng)化配置原理,逐層研究剖析,從@SpringBootApplication啟動(dòng)注解開始,到下面的@SpringBootConfiguration, @ConfigurationPropertiesScan, @ComponentScan以及核心@EnableAutoConfiguration。我們對(duì)@EnableAutoConfiguration下面的@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)兩個(gè)重要注解作了深入研究,從中可以看到Spring Boot針對(duì)自動(dòng)化配置, 是分為兩部分, 一部分是核心注解,來支撐服務(wù)的正常運(yùn)行; 另一部分是非核心的各種自動(dòng)化組件注解,做了大量封裝,便于我們集成使用。
到此這篇關(guān)于Spring Boot示例分析講解自動(dòng)化裝配機(jī)制核心注解的文章就介紹到這了,更多相關(guān)Spring Boot自動(dòng)化裝配機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java8如何用Stream查L(zhǎng)ist對(duì)象某屬性是否有重復(fù)
這篇文章主要介紹了java8如何用Stream查L(zhǎng)ist對(duì)象某屬性是否有重復(fù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09JPA如何將查詢結(jié)果轉(zhuǎn)換為DTO對(duì)象
這篇文章主要介紹了JPA如何將查詢結(jié)果轉(zhuǎn)換為DTO對(duì)象,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02Java高級(jí)特性之反射機(jī)制實(shí)例詳解
這篇文章主要介紹了Java高級(jí)特性之反射機(jī)制,結(jié)合實(shí)例形式詳細(xì)分析了Java反射機(jī)制原理、功能、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2018-08-08Springmvc基于fastjson實(shí)現(xiàn)導(dǎo)包及配置文件
這篇文章主要介紹了Springmvc基于fastjson實(shí)現(xiàn)導(dǎo)包及配置文件,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10java中hashCode方法與equals方法的用法總結(jié)
總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。前者集合內(nèi)的元素是有序的,元素可以重復(fù);后者元素?zé)o序,但元素不可重復(fù)2013-10-10java實(shí)現(xiàn)圖片上傳至本地實(shí)例詳解
我們給大家分享了關(guān)于java實(shí)現(xiàn)圖片上傳至本地的實(shí)例以及相關(guān)代碼,有需要的朋友參考下。2018-08-08springboot基于IDEA環(huán)境熱加載與熱部署教程
這篇文章主要為大家介紹了springboot在IDEA環(huán)境下的熱加載與熱部署教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03SpringBoot集成mybatis連接oracle的圖文教程
這篇文章主要介紹了Spring Boot集成mybatis連接oracle的圖文教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02