亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

解決spring-boot使用logback的大坑

 更新時(shí)間:2021年07月27日 11:07:26   作者:Cumu_  
這篇文章主要介紹了解決spring-boot使用logback的大坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

最近在寫(xiě)一個(gè)logback的kafka appender,無(wú)意中發(fā)現(xiàn)spring-boot在使用logback時(shí)的一個(gè)坑

用ConsoleAppender.java來(lái)舉例,假設(shè)在logback.xml中使用了該appender,那么這個(gè)類(lèi)的相關(guān)的初始化方法都會(huì)調(diào)兩次,如start()方法

打斷點(diǎn)進(jìn)行debug,第一次進(jìn)入start()方法如下:

可以看到所有的調(diào)用鏈(除了自己代碼的方法)都是logback或者slf4j相關(guān)的比較正常

當(dāng)跳過(guò)該斷點(diǎn)時(shí)又會(huì)進(jìn)入以此這個(gè)方法,看下調(diào)用鏈:

可以看到這次的初始化是由spring-boot發(fā)起的,所以這樣logback初始化一次,然后spring-boot初始化一次,一共兩次

我們現(xiàn)在可以將spring-boot的初始化去掉

debug代碼可以發(fā)現(xiàn)LoggingApplicationListener.java這個(gè)監(jiān)聽(tīng)器主要是用來(lái)初始化spring-boot的日志系統(tǒng),現(xiàn)在目的將該listener在啟動(dòng)之前去掉

spring-boot的啟動(dòng)代碼為:

new SpringApplicationBuilder(Launcher.class).application().run(args);

在SpringApplicationBuilder.java的構(gòu)造方法打斷點(diǎn)進(jìn)行跟蹤,

進(jìn)入SpringAppication.java會(huì)發(fā)現(xiàn)該類(lèi)中的代碼:

private void initialize(Object[] sources) {
   if (sources != null && sources.length > 0) {
      this.sources.addAll(Arrays.asList(sources));
   }
   this.webEnvironment = deduceWebEnvironment();
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   this.mainApplicationClass = deduceMainApplicationClass();
}

第八行應(yīng)該就是注冊(cè)監(jiān)聽(tīng)器的地方了,繼續(xù)往下跟蹤,進(jìn)入以下方法:

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, Object... args) {
   ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
   // Use names and ensure unique to protect against duplicates
   Set<String> names = new LinkedHashSet<String>(
         SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
         classLoader, args, names);
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

繼續(xù)進(jìn)入loadFactoryNames()方法,核心就在這里了

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
   String factoryClassName = factoryClass.getName();
   try {
      Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      List<String> result = new ArrayList<String>();
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
         String factoryClassNames = properties.getProperty(factoryClassName);
         result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
      }
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
            "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}

FACTORIES_RESOURCE_LOCATION這個(gè)常量的值為META-INF/spring.factories,

打開(kāi)該文件可以發(fā)現(xiàn):

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

ApplicationListener應(yīng)該就是我們需要修改的地方了,去掉org.springframework.boot.logging.LoggingApplicationListener就可以了,我們可以在代碼里面覆蓋一份這塊代碼從而實(shí)現(xiàn)去掉這行,但是實(shí)際得再跑一遍,發(fā)現(xiàn)還是一樣初始化兩次

問(wèn)題出在

Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
      ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

這塊代碼是將所有的META-INF/spring.factories都讀取過(guò)來(lái)了然后進(jìn)行合并,所以說(shuō)哦這個(gè)META-INF/spring.factories只能增加內(nèi)容,但是不能去掉某些內(nèi)容,沒(méi)辦法了只能在代碼初始化了所有的listener之后再將listener去掉,

具體代碼如下(啟動(dòng)spring-boot的main方法中):

SpringApplicationBuilder builder = new SpringApplicationBuilder(Launcher.class);
Set<ApplicationListener<?>> listeners = builder.application().getListeners();
for (Iterator<ApplicationListener<?>> it = listeners.iterator(); it.hasNext();) {
    ApplicationListener<?> listener = it.next();
    if (listener instanceof LoggingApplicationListener) {
        it.remove();
    }
}
builder.application().setListeners(listeners);
builder.run(args);

PS:其實(shí)log初始化兩次并無(wú)傷大雅,關(guān)鍵是遇到了問(wèn)題總是想解決下或者了解下原理

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論