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

深入了解SpringMVC初始化流程

 更新時(shí)間:2022年07月07日 16:13:10   作者:江南一點(diǎn)雨  
框架源碼是我們?Coding?晉級(jí)中的必修課,SSM?應(yīng)該算是小伙伴們?nèi)粘=佑|最多的框架了,這其中?SpringMVC?初始化流程相對(duì)來說要簡單一些,因此本文就先來和大家分析一下?SpringMVC?初始化流程

前言

框架源碼是我們 Coding 晉級(jí)中的必修課,SSM 應(yīng)該算是小伙伴們?nèi)粘=佑|最多的框架了,這其中 SpringMVC 初始化流程相對(duì)來說要簡單一些,因此今天松哥就先來和大家分析一下 SpringMVC 初始化流程。

本文算是 SpringMVC 用法的一個(gè)進(jìn)階,如果小伙伴們對(duì) SpringMVC 的基礎(chǔ)用法還不熟悉,可以在公眾號(hào)后臺(tái)回復(fù) ssm,有松哥錄制的免費(fèi)視頻教程。

即使你沒看過 SpringMVC 的源碼,估計(jì)也聽說過:DispatcherServlet 是 SpringMVC 的大腦,它負(fù)責(zé)整個(gè) SpringMVC 的調(diào)度工作,是 SpringMVC 中最最核心的類,SpringMVC 整個(gè)頂層架構(gòu)設(shè)計(jì)都體現(xiàn)在這里,所以搞明白 DispatcherServlet 的源碼,基本上 SpringMVC 的工作原理也就了然于胸了。

然而 DispatcherServlet 繼承自 FrameworkServlet,F(xiàn)rameworkServlet 又繼承自 HttpServletBean,如下圖:

因此我們的分析就從 HttpServletBean 開始。

1.HttpServletBean

HttpServletBean 繼承自 HttpServlet,它負(fù)責(zé)將 init-param 中的參數(shù)注入到當(dāng)前 Servlet 實(shí)例的屬性中,同時(shí)也為子類提供了增加 requiredProperties 的能力,需要注意的是 HttpServletBean 并不依賴于 Spring 容器。

大家知道,HttpServlet 的初始化是從 init 方法開始的,所以我們就先從 HttpServletBean 的 init 方法開始看起:

@Override
public?final?void?init()?throws?ServletException?{
?//?Set?bean?properties?from?init?parameters.
?PropertyValues?pvs?=?new?ServletConfigPropertyValues(getServletConfig(),?this.requiredProperties);
?if?(!pvs.isEmpty())?{
??try?{
???BeanWrapper?bw?=?PropertyAccessorFactory.forBeanPropertyAccess(this);
???ResourceLoader?resourceLoader?=?new?ServletContextResourceLoader(getServletContext());
???bw.registerCustomEditor(Resource.class,?new?ResourceEditor(resourceLoader,?getEnvironment()));
???initBeanWrapper(bw);
???bw.setPropertyValues(pvs,?true);
??}
??catch?(BeansException?ex)?{
???if?(logger.isErrorEnabled())?{
????logger.error("Failed?to?set?bean?properties?on?servlet?'"?+?getServletName()?+?"'",?ex);
???}
???throw?ex;
??}
?}
?//?Let?subclasses?do?whatever?initialization?they?like.
?initServletBean();
}

在這個(gè)方法里,首先獲取到 Servlet 的所有配置并轉(zhuǎn)為 PropertyValues,然后通過 BeanWrapper 修改目標(biāo) Servlet 的相關(guān)屬性。BeanWrapper 是 Spring 中提供一個(gè)工具,使用它可以修改一個(gè)對(duì)象的屬性,像下面這樣:

public?class?Main?{
????public?static?void?main(String[]?args)?{
????????User?user?=?new?User();
????????BeanWrapper?beanWrapper?=?PropertyAccessorFactory.forBeanPropertyAccess(user);
????????beanWrapper.setPropertyValue("username",?"itboyhub");
????????PropertyValue?pv?=?new?PropertyValue("address",?"www.itboyhub.com");
????????beanWrapper.setPropertyValue(pv);
????????System.out.println("user?=?"?+?user);
????}
}

最終輸出:

user = User{username='itboyhub', address='www.itboyhub.com'}

所以前面的 bw 實(shí)際上就代表當(dāng)前 DispatcherServlet 對(duì)象。

通過 BeanWrapper 修改目標(biāo) Servlet 的相關(guān)屬性時(shí),有一個(gè) initBeanWrapper 方法是空方法,開發(fā)者如有需要可以在子類中實(shí)現(xiàn)該方法,并且完成一些初始化操作。

屬性配置完成后,最終調(diào)用 initServletBean 方法進(jìn)行 Servlet 初始化,然而該方法也是一個(gè)空方法,在子類中實(shí)現(xiàn)。

這就是 HttpServletBean 所做的事情,比較簡單,加載 Servlet 相關(guān)屬性并設(shè)置給當(dāng)前 Servlet 對(duì)象,然后調(diào)用 initServletBean 方法繼續(xù)完成 Servlet 的初始化操作。

2.FrameworkServlet

從前面的介紹可知,F(xiàn)rameworkServlet 初始化的入口方法就是 initServletBean,因此我們就從 FrameworkServlet#initServletBean 方法開始看起:

@Override
protected?final?void?initServletBean()?throws?ServletException?{
?//省略...
?try?{
??this.webApplicationContext?=?initWebApplicationContext();
??initFrameworkServlet();
?}
?catch?(ServletException?|?RuntimeException?ex)?{
??//省略...
?}
}

這個(gè)方法原本挺長的,但是拋開日志打印異常拋出,剩下的核心代碼其實(shí)就兩行:

  • initWebApplicationContext 方法用來初始化 WebApplicationContext。
  • initFrameworkServlet 方法用來初始化 FrameworkServlet,但是這個(gè)方法是一個(gè)空方法,沒有具體的實(shí)現(xiàn)。本來子類可以重寫該方法做一些初始化操作,但是實(shí)際上子類并沒有重寫該方法,所以這個(gè)方法我們就暫且忽略之,不去分析了。

那么這里最為重要的其實(shí)就是 initWebApplicationContext 方法了,我們一起來看下:

protected?WebApplicationContext?initWebApplicationContext()?{
?WebApplicationContext?rootContext?=
???WebApplicationContextUtils.getWebApplicationContext(getServletContext());
?WebApplicationContext?wac?=?null;
?if?(this.webApplicationContext?!=?null)?{
??wac?=?this.webApplicationContext;
??if?(wac?instanceof?ConfigurableWebApplicationContext)?{
???ConfigurableWebApplicationContext?cwac?=?(ConfigurableWebApplicationContext)?wac;
???if?(!cwac.isActive())?{
????if?(cwac.getParent()?==?null)?{
?????cwac.setParent(rootContext);
????}
????configureAndRefreshWebApplicationContext(cwac);
???}
??}
?}
?if?(wac?==?null)?{
??wac?=?findWebApplicationContext();
?}
?if?(wac?==?null)?{
??wac?=?createWebApplicationContext(rootContext);
?}
?if?(!this.refreshEventReceived)?{
??synchronized?(this.onRefreshMonitor)?{
???onRefresh(wac);
??}
?}
?if?(this.publishContext)?{
??String?attrName?=?getServletContextAttributeName();
??getServletContext().setAttribute(attrName,?wac);
?}
?return?wac;
}

這里的邏輯也比較清晰:

  1. 首先獲取 rootContext。在默認(rèn)情況下,Spring 會(huì)將容器設(shè)置為 ServletContext 的一個(gè)屬性,屬性的 key 為 org.springframework.web.context.WebApplicationContext.ROOT,所以根據(jù)這個(gè) key 就可以調(diào)用 ServletContext#getAttribute 方法獲取到 rootContext 了。
  2. 獲取 WebApplicationContext 實(shí)例,也就是給 wac 變量賦值的過程,這里存在三種可能性:1.如果已經(jīng)通過構(gòu)造方法給 webApplicationContext 賦值了,則直接將其賦給 wac 變量,同時(shí),如果需要設(shè)置 parent 就設(shè)置,需要刷新就刷新。這種方式適用于 Servlet3.0 以后的環(huán)境,因?yàn)閺?Servlet3.0 開始,才支持直接調(diào)用 ServletContext.addServlet 方法去注冊 Servlet,手動(dòng)注冊的時(shí)候就可以使用自己提前準(zhǔn)備好的 WebApplicationContext 了,這塊松哥在我錄制的 Spring Boot 視頻中也講過,感興趣的小伙伴可以在公眾號(hào)后臺(tái)回復(fù) vhr 查看視頻詳情;2.如果第一步?jīng)]能成功給 wac 賦值,那么調(diào)用 findWebApplicationContext 方法嘗試去 ServletContext 中查找 WebApplicationContext 對(duì)象,找到了就賦值給 wac;3.如果第二步?jīng)]能成功給 wac 賦值,那么調(diào)用 createWebApplicationContext 方法創(chuàng)建一個(gè) WebApplicationContext 對(duì)象并賦值給 wac,一般來說都是通過這種方式創(chuàng)建的 WebApplicationContext。這三套組合拳下來,wac 肯定是有值了。
  3. 當(dāng) ContextRefreshedEvent 事件沒有觸發(fā)時(shí),調(diào)用 onRefresh 方法完成容器刷新(由于第一種和第三種獲取 WebApplicationContext 的方式最終都會(huì)調(diào)用 configureAndRefreshWebApplicationContext 方法,然后發(fā)布事件,再將 refreshEventReceived 變量標(biāo)記為 true,所以實(shí)際上只有第二種方式獲取 wac 實(shí)例的時(shí)候,這里才會(huì)刷新,具體可以看下文分析)。
  4. 最后將 wac 保存到到 ServletContext 中。保存的時(shí)候會(huì)根據(jù) publishContext 變量的值來決定是否保存,publishContext 可以在 web.xml 中配置 Servlet 時(shí)通過 init-param 進(jìn)行配置,保存的目的是為了方便獲取。

上面的這些步驟中,通過 createWebApplicationContext 方法創(chuàng)建 WebApplicationContext 對(duì)象需要和大家細(xì)說下,因?yàn)橐话闱闆r下就是通過這種方式創(chuàng)建的 WebApplicationContext。我們來看一下相關(guān)的方法:

protected?WebApplicationContext?createWebApplicationContext(@Nullable?ApplicationContext?parent)?{
?Class<?>?contextClass?=?getContextClass();
?if?(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass))?{
??throw?new?ApplicationContextException(
????"Fatal?initialization?error?in?servlet?with?name?'"?+?getServletName()?+
????"':?custom?WebApplicationContext?class?["?+?contextClass.getName()?+
????"]?is?not?of?type?ConfigurableWebApplicationContext");
?}
?ConfigurableWebApplicationContext?wac?=
???(ConfigurableWebApplicationContext)?BeanUtils.instantiateClass(contextClass);
?wac.setEnvironment(getEnvironment());
?wac.setParent(parent);
?String?configLocation?=?getContextConfigLocation();
?if?(configLocation?!=?null)?{
??wac.setConfigLocation(configLocation);
?}
?configureAndRefreshWebApplicationContext(wac);
?return?wac;
}
protected?void?configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext?wac)?{
?if?(ObjectUtils.identityToString(wac).equals(wac.getId()))?{
??//?The?application?context?id?is?still?set?to?its?original?default?value
??//?->?assign?a?more?useful?id?based?on?available?information
??if?(this.contextId?!=?null)?{
???wac.setId(this.contextId);
??}
??else?{
???//?Generate?default?id...
???wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX?+
?????ObjectUtils.getDisplayString(getServletContext().getContextPath())?+?'/'?+?getServletName());
??}
?}
?wac.setServletContext(getServletContext());
?wac.setServletConfig(getServletConfig());
?wac.setNamespace(getNamespace());
?wac.addApplicationListener(new?SourceFilteringListener(wac,?new?ContextRefreshListener()));
?//?The?wac?environment's?#initPropertySources?will?be?called?in?any?case?when?the?context
?//?is?refreshed;?do?it?eagerly?here?to?ensure?servlet?property?sources?are?in?place?for
?//?use?in?any?post-processing?or?initialization?that?occurs?below?prior?to?#refresh
?ConfigurableEnvironment?env?=?wac.getEnvironment();
?if?(env?instanceof?ConfigurableWebEnvironment)?{
??((ConfigurableWebEnvironment)?env).initPropertySources(getServletContext(),?getServletConfig());
?}
?postProcessWebApplicationContext(wac);
?applyInitializers(wac);
?wac.refresh();
}

這里一共涉及到兩個(gè)方法:

createWebApplicationContext

首先獲取到創(chuàng)建類型,并檢查創(chuàng)建類型,沒問題的話調(diào)用 instantiateClass 方法完成創(chuàng)建工作,然后給創(chuàng)建好的 wac 對(duì)象配置各種屬性,配置的 configLocation 就是我們在 web.xml 文件中配置的 SpringMVC 配置文件路徑,默認(rèn)的文件路徑是 /WEB-INF/[servletName]-servlet.xml。

configureAndRefreshWebApplicationContext

configureAndRefreshWebApplicationContext 方法主要也是配置&刷新 WebApplicationContext,在這個(gè)方法里會(huì)調(diào)用 addApplicationListener 為 wac 添加一個(gè)監(jiān)聽器,監(jiān)聽的是 ContextRefreshedEvent 事件,當(dāng)收到該事件后,會(huì)調(diào)用 FrameworkServlet 的 onApplicationEvent 方法,并在該方法中調(diào)用 onRefresh 方法完成刷新,刷新之后,會(huì)將 refreshEventReceived 變量標(biāo)記為 true。

public?void?onApplicationEvent(ContextRefreshedEvent?event)?{
?this.refreshEventReceived?=?true;
?synchronized?(this.onRefreshMonitor)?{
??onRefresh(event.getApplicationContext());
?}
}

這就是 FrameworkServlet#initServletBean 方法的大致工作邏輯。這里涉及到了 onRefresh 方法,但是這是一個(gè)空方法,在子類 DispatcherServlet 中實(shí)現(xiàn)了,所以接下來我們就來看 DispatcherServlet。

3.DispatcherServlet

這里我們就不廢話了,直接來看 onRefresh 方法,如下:

@Override
protected?void?onRefresh(ApplicationContext?context)?{
?initStrategies(context);
}
protected?void?initStrategies(ApplicationContext?context)?{
?initMultipartResolver(context);
?initLocaleResolver(context);
?initThemeResolver(context);
?initHandlerMappings(context);
?initHandlerAdapters(context);
?initHandlerExceptionResolvers(context);
?initRequestToViewNameTranslator(context);
?initViewResolvers(context);
?initFlashMapManager(context);
}

在 onRefresh 方法中調(diào)用了 initStrategies 進(jìn)行初始化操作。initStrategies 的內(nèi)容其實(shí)很簡單,就是九個(gè)組件的初始化。九個(gè)的初始化流程比較類似,這里我們以常見的視圖解析器的初始化方法 initViewResolvers 為例,來一起看看初始化流程:

private?void?initViewResolvers(ApplicationContext?context)?{
?this.viewResolvers?=?null;
?if?(this.detectAllViewResolvers)?{
??//?Find?all?ViewResolvers?in?the?ApplicationContext,?including?ancestor?contexts.
??Map<String,?ViewResolver>?matchingBeans?=
????BeanFactoryUtils.beansOfTypeIncludingAncestors(context,?ViewResolver.class,?true,?false);
??if?(!matchingBeans.isEmpty())?{
???this.viewResolvers?=?new?ArrayList<>(matchingBeans.values());
???//?We?keep?ViewResolvers?in?sorted?order.
???AnnotationAwareOrderComparator.sort(this.viewResolvers);
??}
?}
?else?{
??try?{
???ViewResolver?vr?=?context.getBean(VIEW_RESOLVER_BEAN_NAME,?ViewResolver.class);
???this.viewResolvers?=?Collections.singletonList(vr);
??}
??catch?(NoSuchBeanDefinitionException?ex)?{
???//?Ignore,?we'll?add?a?default?ViewResolver?later.
??}
?}
?//?Ensure?we?have?at?least?one?ViewResolver,?by?registering
?//?a?default?ViewResolver?if?no?other?resolvers?are?found.
?if?(this.viewResolvers?==?null)?{
??this.viewResolvers?=?getDefaultStrategies(context,?ViewResolver.class);
??if?(logger.isTraceEnabled())?{
???logger.trace("No?ViewResolvers?declared?for?servlet?'"?+?getServletName()?+
?????"':?using?default?strategies?from?DispatcherServlet.properties");
??}
?}
}

一開始的 viewResolvers 變量是一個(gè)集合,解析出來的視圖解析器對(duì)象都將放入這個(gè)集合中。

首先判斷 detectAllViewResolvers 變量是否為 true,如果為 true,則直接去查找 Spring 容器中的所有視圖解析器,將查找結(jié)果賦值給 viewResolvers,然后進(jìn)行排序。默認(rèn)情況下 detectAllViewResolvers 變量的值為 true,如果有需要,可以在 web.xml 中進(jìn)行配置,像下面這樣:

<servlet>
????<servlet-name>springmvc</servlet-name>
????<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
????<init-param>
????????<param-name>contextConfigLocation</param-name>
????????<param-value>classpath:spring-servlet.xml</param-value>
????</init-param>
????<init-param>
????????<param-name>detectAllViewResolvers</param-name>
????????<param-value>false</param-value>
????</init-param>
????<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
????<servlet-name>springmvc</servlet-name>
????<url-pattern>/</url-pattern>
</servlet-mapping>

如果 detectAllViewResolvers 的值為 false,那么接下來就會(huì)去 Spring 容器中查找一個(gè)名為 viewResolver 的視圖解析器,此時(shí)查找到的就是一個(gè)單獨(dú)的視圖解析器。

一般來說,我們并不需要在 web.xml 中去配置 detectAllViewResolvers 的值,視圖解析器有多少個(gè)就加載多少個(gè)。

舉個(gè)簡單例子,我們在 SpringMVC 的配置文件中可能像下面這樣配置視圖解析器:

<bean?class="org.springframework.web.servlet.view.InternalResourceViewResolver"?id="viewResolver">
????<property?name="prefix"?value="/WEB-INF/jsp/"/>
????<property?name="suffix"?value=".jsp"/>
</bean>

默認(rèn)情況下,這個(gè) bean 的 id 有沒有都行,如果有,取什么值都可以,反正最終都是通過類型而不是 id 去查找的視圖解析器。但是如果你在 web.xml 中將 detectAllViewResolvers 修改為 false,那么這個(gè) bean 的 id 取值就比較重要了,就一定要是 viewResolver。

如果在 Spring 容器中通過這兩種方式(通過類型查找或通過 id 查找)都沒有找到 ViewResolver 實(shí)例,那么會(huì)調(diào)用 getDefaultStrategies 方法去獲取一個(gè)默認(rèn)的 ViewResolver 實(shí)例。默認(rèn)實(shí)例的獲取方式如下:

protected?<T>?List<T>?getDefaultStrategies(ApplicationContext?context,?Class<T>?strategyInterface)?{
?if?(defaultStrategies?==?null)?{
??try?{
???//?Load?default?strategy?implementations?from?properties?file.
???//?This?is?currently?strictly?internal?and?not?meant?to?be?customized
???//?by?application?developers.
???ClassPathResource?resource?=?new?ClassPathResource(DEFAULT_STRATEGIES_PATH,?DispatcherServlet.class);
???defaultStrategies?=?PropertiesLoaderUtils.loadProperties(resource);
??}
??catch?(IOException?ex)?{
???throw?new?IllegalStateException("Could?not?load?'"?+?DEFAULT_STRATEGIES_PATH?+?"':?"?+?ex.getMessage());
??}
?}
?String?key?=?strategyInterface.getName();
?String?value?=?defaultStrategies.getProperty(key);
?if?(value?!=?null)?{
??String[]?classNames?=?StringUtils.commaDelimitedListToStringArray(value);
??List<T>?strategies?=?new?ArrayList<>(classNames.length);
??for?(String?className?:?classNames)?{
???try?{
????Class<?>?clazz?=?ClassUtils.forName(className,?DispatcherServlet.class.getClassLoader());
????Object?strategy?=?createDefaultStrategy(context,?clazz);
????strategies.add((T)?strategy);
???}
???catch?(ClassNotFoundException?ex)?{
????throw?new?BeanInitializationException(
??????"Could?not?find?DispatcherServlet's?default?strategy?class?["?+?className?+
??????"]?for?interface?["?+?key?+?"]",?ex);
???}
???catch?(LinkageError?err)?{
????throw?new?BeanInitializationException(
??????"Unresolvable?class?definition?for?DispatcherServlet's?default?strategy?class?["?+
??????className?+?"]?for?interface?["?+?key?+?"]",?err);
???}
??}
??return?strategies;
?}
?else?{
??return?Collections.emptyList();
?}
}

這段代碼其實(shí)也比較簡單,就是通過反射去獲取默認(rèn)的視圖解析器。

首先給 defaultStrategies 賦值,defaultStrategies 的值實(shí)際上就是從 DispatcherServlet.properties 文件中加載到的,我們來看下這個(gè)文件內(nèi)容:

可以看到,這里一共定義了 8 個(gè)默認(rèn)的鍵值對(duì),有的值是一個(gè),有的值是多個(gè)。前面 initStrategies 方法中一共要初始化九個(gè)組件,這里默認(rèn)只定義了 8 個(gè),少了一個(gè) MultipartResolver,這也好理解,并非所有的項(xiàng)目都有文件上傳,而且即使有文件上傳,用哪一個(gè)具體的 MultipartResolver 也不好確定,還是要開發(fā)者自己決定。

defaultStrategies 其實(shí)加載到的就是這 8 個(gè)鍵值對(duì),其中視圖解析器對(duì)應(yīng)的是 org.springframework.web.servlet.view.InternalResourceViewResolver,通過反射創(chuàng)建該類的實(shí)例,當(dāng) Spring 容器中不存在任何視圖解析器的時(shí)候,默認(rèn)的視圖解析器即此。

這就是 initViewResolvers 的工作流程,另外 8 個(gè)也和它差不多,唯一不同的是 initMultipartResolver,如下:

private?void?initMultipartResolver(ApplicationContext?context)?{
?try?{
??this.multipartResolver?=?context.getBean(MULTIPART_RESOLVER_BEAN_NAME,?MultipartResolver.class);
?}
?catch?(NoSuchBeanDefinitionException?ex)?{
??this.multipartResolver?=?null;
?}
}

可以看到,它只是根據(jù) bean 的名字去查找 bean 實(shí)例,沒有去查找默認(rèn)的 MultipartResolver。

說到這里,松哥和大家多說一句 SpringMVC 配置中的小細(xì)節(jié),

<bean?class="org.springframework.web.servlet.view.InternalResourceViewResolver"?id="viewResolver">
????<property?name="prefix"?value="/WEB-INF/jsp/"/>
????<property?name="suffix"?value=".jsp"/>
</bean>
<bean?class="org.springframework.web.multipart.commons.CommonsMultipartResolver"?id="multipartResolver">
</bean>

上面這個(gè)關(guān)于視圖解析器和文件上傳解析器的配置,不知道小伙伴們有沒有注意過,視圖解析器的 id 可有可無,而文件上傳解析器的 id 必須是 multipartResolver,回顧我們上面的源碼分析,你就知道為啥了!

4.小結(jié)

好啦,這就是松哥和小伙伴們分享的 SpringMVC 的初始化流程,主要涉及到了 HttpServletBean、FrameworkServlet 以及 DispatcherServlet 三個(gè)實(shí)例,HttpServletBean 主要是加載 Servlet 配置的各種屬性并設(shè)置到 Servlet 上;FrameworkServlet 則主要是初始化了 WebApplicationContext;DispatcherServlet 則主要是初始化了自身的九個(gè)組件。

到此這篇關(guān)于深入了解SpringMVC初始化流程的文章就介紹到這了,更多相關(guān)SpringMVC初始化流程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java實(shí)現(xiàn)批量導(dǎo)入.csv文件到mysql數(shù)據(jù)庫

    java實(shí)現(xiàn)批量導(dǎo)入.csv文件到mysql數(shù)據(jù)庫

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)批量導(dǎo)入.csv文件到mysql數(shù)據(jù)庫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • SpringBoot詳解如何進(jìn)行整合Druid數(shù)據(jù)源

    SpringBoot詳解如何進(jìn)行整合Druid數(shù)據(jù)源

    Druid是阿里開發(fā)的一款開源的數(shù)據(jù)源,被很多人認(rèn)為是Java語言中最好的數(shù)據(jù)庫連接池,本文主要介紹了SpringBoot整合Druid數(shù)據(jù)源的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Java 細(xì)致圖解帶你分析漢諾塔

    Java 細(xì)致圖解帶你分析漢諾塔

    漢諾塔問題是一個(gè)經(jīng)典的問題。漢諾塔(Hanoi Tower),又稱河內(nèi)塔,源于印度一個(gè)古老傳說。本文將用Java求解這一問題,感興趣的可以學(xué)習(xí)一下
    2022-03-03
  • Java中l(wèi)ist集合的clear方法及空字符串的區(qū)別

    Java中l(wèi)ist集合的clear方法及空字符串的區(qū)別

    這篇文章主要介紹了Java中l(wèi)ist集合的clear方法及空字符串的區(qū)別,在使用list?結(jié)合的時(shí)候習(xí)慣了?list=null?;在創(chuàng)建這樣的方式,但是發(fā)現(xiàn)使用list的clear?方法很不錯(cuò),尤其是有大量循環(huán)的時(shí)候<BR>list.clear()與list?=?null?區(qū)別,需要的朋友可以參考下
    2023-08-08
  • java程序中指定某個(gè)瀏覽器打開的實(shí)現(xiàn)方法

    java程序中指定某個(gè)瀏覽器打開的實(shí)現(xiàn)方法

    最近工作中遇到一個(gè)需求,是要利用java打開指定瀏覽器,整理后發(fā)現(xiàn)有四種解決的方法,所以想著分享出來,下面這篇文章主要給大家介紹了java程序中指定某個(gè)瀏覽器打開的實(shí)現(xiàn)方法,,需要的朋友可以參考下。
    2017-03-03
  • 淺談java Properties類的使用基礎(chǔ)

    淺談java Properties類的使用基礎(chǔ)

    下面小編就為大家分享一篇淺談java Properties類的使用基礎(chǔ),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • SpringBoot如何使用applicationContext.xml配置文件

    SpringBoot如何使用applicationContext.xml配置文件

    這篇文章主要介紹了SpringBoot使用applicationContext.xml配置文件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • SpringBoot實(shí)現(xiàn)過濾器、攔截器與切片的實(shí)現(xiàn)和區(qū)別

    SpringBoot實(shí)現(xiàn)過濾器、攔截器與切片的實(shí)現(xiàn)和區(qū)別

    本文詳細(xì)介紹了使用過濾器、攔截器與切片實(shí)現(xiàn)每個(gè)請(qǐng)求耗時(shí)的統(tǒng)計(jì),并比較三者的區(qū)別與聯(lián)系,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-02-02
  • Java編程讀寫鎖詳解

    Java編程讀寫鎖詳解

    本篇文章給大家詳細(xì)分享了Java編程讀寫鎖的相關(guān)原理以及知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們可以參考下。
    2018-08-08
  • Java用BigDecimal解決double類型相減時(shí)可能存在的誤差

    Java用BigDecimal解決double類型相減時(shí)可能存在的誤差

    這篇文章主要介紹了Java用BigDecimal解決double類型相減時(shí)可能存在的誤差,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05

最新評(píng)論