SpringBoot實現(xiàn)嵌入式 Servlet容器
一、如何定制和修改Servlet容器的相關(guān)配置
前言: SpringBoot在Web環(huán)境下,默認使用的是Tomact作為嵌入式的Servlet容器;

【1】修改和server相關(guān)的配置(ServerProperties實現(xiàn)了EmbeddedServletContainerCustomizer)例如:修改端口號
#通用的Servlet容器設(shè)置:修改端口號
server:
port: 8081
tomcat: #設(shè)置Tomact的相關(guān)屬性,例如編碼格式
uri-encoding: utf-8
? 我們也可以進入port所屬的對象中,發(fā)現(xiàn)其他可修改的參數(shù)等等,如下:
@ConfigurationProperties(
prefix = "server",
ignoreUnknownFields = true
)
public class ServerProperties implements EmbeddedServletContainerCustomizer, EnvironmentAware, Ordered {
private Integer port;
private InetAddress address;
private String contextPath;
private String displayName = "application";
......
【2】編寫一個EmbeddedServletContainerCustomizer:嵌入式的 Servlet容器的定制器,來修改 Servlet容器的配置。其實1中的 ServerProperties也是實現(xiàn)了 EmbeddedServletContainerCustomizer。xxxCustomizer 幫組我們進行定制配置。
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Bean
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(8082);
}
};
}
二、注冊Servlet三大組件【Servlet、Filter、Listener】
由于SpringBoot默認是以 jar包的方式啟動嵌入的Servlet容器來啟動SpringBoot的web應(yīng)用,沒有web.xml文件。注冊三大組件的方式如下:
【1】通過 ServletRegistrationBean注冊自定義的Servlet。
//首先創(chuàng)建一個Servlet
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello MyServlet");
super.doPost(req, resp);
}
}
//將創(chuàng)建的Servlet通過配置類注入到容器中,兩個是不同的類。
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Bean
public ServletRegistrationBean myServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(), "/myServlet");
return registrationBean;
}
【2】通過 FilterRegistrationBean 注冊攔截器 Filter。
//自定義一個filter實現(xiàn)servlet.Filter接口
public class myFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.printf("myFilter");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
//通過配置類注入自定義的Filter
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new myFilter());
registrationBean.setUrlPatterns(Arrays.asList("/hello","/myFilter"));
return registrationBean;
}
【3】通過`ServletListenerRegistrationBean`注冊自定義的`Listener`。
```java
//創(chuàng)建自定義的Listener監(jiān)聽
public class myListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.printf("服務(wù)啟動");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.printf("服務(wù)銷毀");
}
}
//通過配置類注入自定義的listener
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
public ServletListenerRegistrationBean myListener(){
ServletListenerRegistrationBean<MyListener> servletListenerRegistrationBean = new ServletListenerRegistrationBean<>(new MyListener());
return servletListenerRegistrationBean;
}
三、使用其他 Servlet容器:Jetty(長連接引用)、Undertow(不支持JSP)
【1】我們在定制嵌入式的Servlet容器時,會傳入ConfigurableEmbeddedServletContainer類,我們通過Ctrl+T查看此可配置嵌入式類容器中可以配置Tomcat、Jetty和Undertow。
//ConfigurableEmbeddedServletContainer
@Bean
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(8082);
}
};
}

【2】默認使用Tomcat,因為starter-web引入的是Tomcat的starter。我們排除Tomcat的依賴,引入Jetty的依賴即可。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-tomcat</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <dependency> <artifactId>spring-boot-starter-Jetty</artifactId> <groupId>org.springframework.boot</groupId> </dependency>
四、嵌入式 Servlet容器自動配置原理
【1】EmbeddedServletContainerAutoConfiguration類主要用來自動配置嵌入式的Servlet容器。
@AutoConfigureOrder(-2147483648)
@Configuration
@ConditionalOnWebApplication
//導(dǎo)入BeanPostProcessorsRegistrar:后置處理器:在bean初始化前后,執(zhí)行(剛創(chuàng)建完對象,還沒屬性賦值)初始化工作.
//給容器中導(dǎo)入一些組件,導(dǎo)入了embeddedServletContainerCustomizerBeanPostProcessor
@Import({EmbeddedServletContainerAutoConfiguration.BeanPostProcessorsRegistrar.class})
public class EmbeddedServletContainerAutoConfiguration {
@Configuration
@ConditionalOnClass({Servlet.class, Tomcat.class})//判斷當(dāng)前Servlet中是否引入的Tomcat依賴
@ConditionalOnMissingBean(
value = {EmbeddedServletContainerFactory.class},
search = SearchStrategy.CURRENT
)//判斷當(dāng)前容器中,沒有用戶自定義的EmbeddedServletContainerFactory嵌入式的Servlet容器工廠,
//作用:創(chuàng)建嵌入式的servlet容器。
public static class EmbeddedTomcat {
public EmbeddedTomcat() {
}
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory();
}
}
@Configuration
@ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
@ConditionalOnMissingBean(
value = {EmbeddedServletContainerFactory.class},
search = SearchStrategy.CURRENT
)
public static class EmbeddedUndertow {
public EmbeddedUndertow() {
}
@Bean
public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
return new UndertowEmbeddedServletContainerFactory();
}
}
@Configuration
@ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})
@ConditionalOnMissingBean(
value = {EmbeddedServletContainerFactory.class},
search = SearchStrategy.CURRENT
)
public static class EmbeddedJetty {
public EmbeddedJetty() {
}
@Bean
public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
return new JettyEmbeddedServletContainerFactory();
}
}
}
【2】嵌入式的容器工廠:EmbeddedServletContainerFactory,用來創(chuàng)建嵌入式的Servlet容器。
public interface EmbeddedServletContainerFactory {
//獲取嵌入式的Servlet容器
EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... var1);
}
? SpringBoot 再帶了三種嵌入式的容器工廠,如下:

【3】EmbeddedServletContainer:嵌入式的容器,SpringBoot 為我們提供了三種不同的嵌入式容器,與工廠相互對應(yīng),如下:

【4】我們進入工廠類 TomcatEmbeddedServletContainerFactory發(fā)現(xiàn),其實也是創(chuàng)建一個 Tomcat并配置其基本屬性。
public EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers) {
//創(chuàng)建Tomcat
Tomcat tomcat = new Tomcat();
//配置Tomcat的基本環(huán)境
File baseDir = this.baseDirectory != null?this.baseDirectory:this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
//將配置好的Tomcat傳入,并啟動Tomcat,Tomcat.start()
return this.getTomcatEmbeddedServletContainer(tomcat);
}
【5】用戶自定義的Servlet容器配置類和SpringBoot默認的ServerProperties配置類,都實現(xiàn)了EmbeddedServletContainerCustomizer接口。到底是怎么實現(xiàn)的哪?其實是SpringBoot自動配置類中引入了后置處理器,如下:
//與用戶自定義的Servlet容器實現(xiàn)的接口名很類似,有一定的命名規(guī)則。 embeddedServletContainerCustomizerBeanPostProcessor
? 進入后置處理器類中,重點看如下代碼:
//初始化之前執(zhí)行
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//如果當(dāng)前初始化的是當(dāng)前ConfigurableEmbeddedServletContainer類型的組件
if(bean instanceof ConfigurableEmbeddedServletContainer) {
this.postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer)bean);
}
return bean;
}
//上面的postProcessBeforeInitialization方法:
private void postProcessBeforeInitialization(ConfigurableEmbeddedServletContainer bean) {
Iterator var2 = this.getCustomizers().iterator();
while(var2.hasNext()) {
//獲取所有的定制器,調(diào)用每一個定制器的customize方法來給servlet屬性賦值。
EmbeddedServletContainerCustomizer customizer = (EmbeddedServletContainerCustomizer)var2.next();
customizer.customize(bean);
}
private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
if(this.customizers == null) {
//this.beanFactory.xx表示從容器中獲取XXCustomizer自定義類型的組件
this.customizers = new ArrayList(this.beanFactory.getBeansOfType(EmbeddedServletContainerCustomizer.class, false, false).values());
Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
整理下步驟:
【1】SpringBoot根據(jù)pom.xml中導(dǎo)入的依賴,給容器中添加其對應(yīng)的嵌入式的服務(wù)容器工廠類,例如默認的Tomcat工廠:EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactory】
【2】給容器中某個組件要創(chuàng)建對象就會觸發(fā)后置處理器EmbeddedServletContainerCustomizerBeanPostProcessor,只要是嵌入式的Servlet容器工廠,后置處理器就會工作(默認的ServerProperties也是實現(xiàn)了此類接口的,所以肯定存在相關(guān)配置類)
【3】后置處理器從容器中獲取所有的EmbeddedServletContainerCustomizer,調(diào)用定制器的定制方法。
五、嵌入式Servlet容器啟動原理
根據(jù)上述的流程,我們要研究Servlet容器的啟動原理。其實就是研究什么時候創(chuàng)建嵌入式的容器工廠和何時獲取嵌入式的容器并啟動Tomcat。獲取嵌入式的Servlet容器工廠的過程(在new TomcatEmbeddedServletContainerFactory()時打一個斷電,查看過程):
【1】SpringBoot 應(yīng)用啟動運行 run() 方法。
【2】this.refreshContext(context) 方法:用來初始化 IOC容器,既創(chuàng)建 IOC容器對象并初始化IOC容器中的每一個組件。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if(contextClass == null) {
try {
//判斷是不是web環(huán)境,是Web環(huán)境引入AnnotationConfigEmbeddedWebApplicationContext,否則引入AnnotationConfigApplicationContext
contextClass = Class.forName(this.webEnvironment
?"org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext"
:"org.springframework.context.annotation.AnnotationConfigApplicationContext");
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
}
【3】this.refresh(context):刷新剛才創(chuàng)建好的IOC容器。
【4】this.onRefresh():web的IoC容器重寫了onRefresh()方法。
protected void onRefresh() {
super.onRefresh();
try {
//重點是創(chuàng)建了嵌入式的Servlet容器
this.createEmbeddedServletContainer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start embedded container", var2);
}
}
【5】this.createEmbeddedServletContainer():web的IOC容器會創(chuàng)建嵌入式的Servlet容器。
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = this.getServletContext();
if(localContainer == null && localServletContext == null) {
// 1、獲取嵌入式的Servlet嵌入式的工廠
EmbeddedServletContainerFactory containerFactory = this.getEmbeddedServletContainerFactory();
this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(
new ServletContextInitializer[]{this.getSelfInitializer()});
}
}
【6】獲取嵌入式工廠后,便可從容器中獲取EmbeddedServletContainerFactory的組件tomcatEmbeddedServletContainerFactory來創(chuàng)建Tomcat對象,后置處理器就會觸發(fā)獲取所有的定制器來確定Servlet容器的相關(guān)配置。
【7】通過嵌入式工廠獲取嵌入式容器,如下:
this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(
new ServletContextInitializer[]{this.getSelfInitializer()});
● 嵌入式的Servlet容器創(chuàng)建并啟動對象:
public EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers) {
//創(chuàng)建對象
Tomcat tomcat = new Tomcat();
//啟動對象
this.tomcat.start();
● 先啟動嵌入式的Servlet容器,再將IOC容器中剩下沒有創(chuàng)建的對象進行初始化,如下:
this.onRefresh();
//啟動完嵌入式容器后,后續(xù)還有其他對象的初始化工作
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
六、使用外置的Servlet容器
嵌入式Servlet容器的缺點: 默認不支持JSP、優(yōu)化和定制比較復(fù)雜。外置Servlet容器:安裝外部的Tomcat,步驟如下:
1)、必須創(chuàng)建一個war項目,需要手動創(chuàng)建目錄(利用Idea快速創(chuàng)建)如下:

2)、將嵌入式的Tomcat指定為provide(Idea創(chuàng)建完后,會自動幫我們完成,但我們需要了解)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>
3)、需要編寫一個SpringBootServletInitializer的子類,并調(diào)用configure方法:
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringBootWebApplication.class);
}
}
4)、配置本地的Tomcat,并啟動Tomcat即可。(此項目運行run()方法是不能啟動項目的):需要設(shè)置名稱和本地Tomcat的路徑即可使用外部Servlet。

七、外置服務(wù)器的使用原理
? jar包:執(zhí)行SpringBoot主類的main方法,啟動并初始化IOC容器且創(chuàng)建嵌入式的Servlet容器。
? war包:啟動服務(wù)器后調(diào)用SpringBootServletInitializer中的configure()方法,加載我們的SpringBoot應(yīng)用并啟動。
Servlet3.0規(guī)則:
1)、服務(wù)器啟動后,會創(chuàng)建當(dāng)前web應(yīng)用中包含的每個jar內(nèi)的ServletContainerInitializer實例。
2)、ServletContainerInitializer的實現(xiàn)放在jar包的META-INF/services文件夾下(javax.servlet.ServletContainerInitializer:內(nèi)容就是ServletContainerInitializer的全類名)
3)、可以使用@handlesTypes注解,在應(yīng)用啟動時加載我們需要的類。
流程: 1)、啟動Tomcat后,獲取servlet.ServletContainerInitializer文件如下:其中的內(nèi)容同下:

#文件中的內(nèi)容 org.springframework.web.SpringServletContainerInitializer
2)、進入SpringServletContainerInitializer發(fā)現(xiàn)此類將@HandlesTypes({WebApplicationInitializer.class})標(biāo)注的所有這個類型的類都傳入到onStartup方法中的Set<Class<?>>,并為這些類創(chuàng)建實例。
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
//onStartup方法,用來實例化感興趣的對象
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
if(webAppInitializerClasses != null) {
var4 = webAppInitializerClasses.iterator();
while(var4.hasNext()) {
Class<?> waiClass = (Class)var4.next();
if(!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
//實例化
initializers.add((WebApplicationInitializer)waiClass.newInstance());
} catch (Throwable var7) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
}
}
}
}
3)、每一個WebApplicationInitializer都調(diào)用自己的onStartup()方法。
while(var4.hasNext()) {
WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
//onStartup()方法
initializer.onStartup(servletContext);
}
4)、WebApplicationInitializer只是一個接口,其實現(xiàn)類主要有以下三個:SpringBootServletInitalizer正是SpringBoot給我們創(chuàng)建好的啟動類,會被創(chuàng)建對象,并啟動自身的onStartup()方法。

5)、執(zhí)行onStartup()方法時,會調(diào)用createRootApplicationContext()方法來創(chuàng)建容器
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(this.getClass());
//創(chuàng)建容器
WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
if(rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
public void contextInitialized(ServletContextEvent event) {
}
});
//容器的具體調(diào)用實現(xiàn)
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
//創(chuàng)建Spring應(yīng)用的構(gòu)建器
SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
//設(shè)置主類
builder.main(this.getClass());
//創(chuàng)建一些環(huán)境
ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
if(parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
}
builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
//重要:子類中重寫了此方法,子類出入了應(yīng)用的主程序類
builder = this.configure(builder);
builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext, null)});
//使用build()創(chuàng)建一個Spring應(yīng)用
SpringApplication application = builder.build();
if(application.getAllSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(this.getClass()));
}
Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
if(this.registerErrorPageFilter) {
application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
}
//啟動應(yīng)用
return this.run(application);
}
6)、執(zhí)行應(yīng)用的run()方法,來啟動Spring應(yīng)用并創(chuàng)建IOC容器。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, new Object[]{context});
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新IOC容器
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if(this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}到此這篇關(guān)于SpringBoot實現(xiàn)嵌入式 Servlet容器的文章就介紹到這了,更多相關(guān)SpringBoot 嵌入式 Servlet容器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java用POI解析excel并獲取所有單元格數(shù)據(jù)的實例
下面小編就為大家?guī)硪黄狫ava用POI解析excel并獲取所有單元格數(shù)據(jù)的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
JAVA面試題之Forward與Redirect的區(qū)別詳解
這篇文章主要給大家介紹了在JAVA面試中可能遇到會遇到的一道題,就是java中Forward與Redirect兩者之前的區(qū)別,文中介紹的非常詳細,對大家具有一定參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。2017-05-05
Spring Cloud Hystrix線程池不足的解決方法
這篇文章主要介紹了Spring Cloud Hystrix線程池不足的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
2種Java刪除ArrayList中的重復(fù)元素的方法
這篇文章主要介紹了2種Java刪除ArrayList中的重復(fù)元素的方法,感興趣的朋友可以參考下2015-08-08

