转自:https://blog.csdn.net/m0_37298252/article/details/122355631
最近两篇文章主要分析了ConfigFileApplicationListener对事件ApplicationEnvironmentPreparedEvent的处理,包括EnvironmentPostProcessor扩展点和系统配置文件的加载,而之前也提到过,实际上有很多监听器都会监听该事件的发布,本文对其它几个监听器的相关处理做个简单的介绍
首先看下收到事件的监听器列表
ConfigFileApplicationListener已经介绍地很详细了,接下来对剩下的监听器做逐一分析
AnsiOutputApplicationListener
1 public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { 2 ConfigurableEnvironment environment = event.getEnvironment(); 3 Binder.get(environment).bind("spring.output.ansi.enabled", Enabled.class).ifBound(AnsiOutput::setEnabled); 4 AnsiOutput.setConsoleAvailable((Boolean)environment.getProperty("spring.output.ansi.console-available", Boolean.class)); 5 }
这个监听器主要用来设置日志的颜色,比如默认情况下控制台看到的日志格式如下
如果设置了spring.output.ansi.enabled=ALWAYS,日志的颜色发生了变化,可读性更好一点
这里重点说下Binder这个类
Binder.get(environment).bind("spring.output.ansi.enabled", Enabled.class).ifBound(AnsiOutput::setEnabled);
它是SpringBoot 2.x加入的新特性,用来将Environment中的属性绑定到指定的类型中,可以是List、Map等集合,也可以是自定义的实体类
这里就是将spring.output.ansi.enabled属性的值赋给AnsiOutput.Enabled,它是一个枚举类
1 public static enum Enabled { 2 DETECT, 3 ALWAYS, 4 NEVER; 5 private Enabled() { 6 } 7 }
绑定的结果存储到BindResult中
1 public final class BindResult<T> { 2 ...... 3 private final T value; 4 ...... 5 public void ifBound(Consumer<? super T> consumer) { 6 Assert.notNull(consumer, "Consumer must not be null"); 7 if (this.value != null) { 8 consumer.accept(this.value); 9 } 10 } 11 ...... 12 }
ifBound接收一个Consumer,如果绑定的属性不为空,则调用consumer的处理逻辑
上面传进来的是AnsiOutput::setEnabled,所以就相当于把配置文件中的spring.output.ansi.enabled属性赋给AnsiOutput的enabled变量
而Binder后面的一行代码就直接到Environment中取了spring.output.ansi.console-available赋值给AnsiOutput的consoleAvailable属性
这个监听器就是用来设置日志颜色的,用处不是很大,一般也不会做额外配置
另外由于会在日志中输出表示颜色的分隔符,有可能会对一些日志收集组件产生一定干扰,所以还是慎用
LoggingApplicationListener
1 public void onApplicationEvent(ApplicationEvent event) { 2 if (event instanceof ApplicationStartingEvent) { 3 this.onApplicationStartingEvent((ApplicationStartingEvent)event); 4 } else if (event instanceof ApplicationEnvironmentPreparedEvent) { 5 this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event); 6 } else if (event instanceof ApplicationPreparedEvent) { 7 this.onApplicationPreparedEvent((ApplicationPreparedEvent)event); 8 } else if (event instanceof ContextClosedEvent && ((ContextClosedEvent)event).getApplicationContext().getParent() == null) { 9 this.onContextClosedEvent(); 10 } else if (event instanceof ApplicationFailedEvent) { 11 this.onApplicationFailedEvent(); 12 } 13 }
对当前事件的处理,在方法onApplicationEnvironmentPreparedEvent中
1 private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { 2 if (this.loggingSystem == null) { 3 this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader()); 4 } 5 this.initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader()); 6 }
loggingSystem在之前处理ApplicationStartingEvent事件的时候已经做了初始化,我们之前对它做过详细的介绍,表示当前系统使用的日志体系是logback,还是log4j,亦或者是JDK自带的日志框架
initialize方法主要是对日志相关的配置做一个初始化,比如日志大小、日志文件地址、日志滚动的周期等等
1 protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) { 2 (new LoggingSystemProperties(environment)).apply(); 3 this.logFile = LogFile.get(environment); 4 if (this.logFile != null) { 5 this.logFile.applyToSystemProperties(); 6 } 7 8 this.initializeEarlyLoggingLevel(environment); 9 this.initializeSystem(environment, this.loggingSystem, this.logFile); 10 this.initializeFinalLoggingLevels(environment, this.loggingSystem); 11 this.registerShutdownHookIfNecessary(environment, this.loggingSystem); 12 }
我们点开其中的几个方法,可以看到都是一些日志文件,或者日志相关的配置
1 public void apply(LogFile logFile) { 2 PropertyResolver resolver = this.getPropertyResolver(); 3 this.setSystemProperty(resolver, "LOG_EXCEPTION_CONVERSION_WORD", "exception-conversion-word"); 4 this.setSystemProperty("PID", (new ApplicationPid()).toString()); 5 this.setSystemProperty(resolver, "CONSOLE_LOG_PATTERN", "pattern.console"); 6 this.setSystemProperty(resolver, "FILE_LOG_PATTERN", "pattern.file"); 7 this.setSystemProperty(resolver, "LOG_FILE_MAX_HISTORY", "file.max-history"); 8 this.setSystemProperty(resolver, "LOG_FILE_MAX_SIZE", "file.max-size"); 9 this.setSystemProperty(resolver, "LOG_LEVEL_PATTERN", "pattern.level"); 10 this.setSystemProperty(resolver, "LOG_DATEFORMAT_PATTERN", "pattern.dateformat"); 11 if (logFile != null) { 12 logFile.applyToSystemProperties(); 13 } 14 }
1 public static LogFile get(PropertyResolver propertyResolver) { 2 String file = propertyResolver.getProperty("logging.file"); 3 String path = propertyResolver.getProperty("logging.path"); 4 return !StringUtils.hasLength(file) && !StringUtils.hasLength(path) ? null : new LogFile(file, path); 5 }
总而言之,这个监听器会根据日志的专有配置文件、或者系统配置文件中的日志相关属性,对日志组件做一些初始化
ClasspathLoggingApplicationListener
1 public void onApplicationEvent(ApplicationEvent event) { 2 if (logger.isDebugEnabled()) { 3 if (event instanceof ApplicationEnvironmentPreparedEvent) { 4 logger.debug("Application started with classpath: " + this.getClasspath()); 5 } else if (event instanceof ApplicationFailedEvent) { 6 logger.debug("Application failed to start with classpath: " + this.getClasspath()); 7 } 8 } 9 }
如果当前日志级别是debug,就把classpath打印出来
BackgroundPreinitializer
1 public void onApplicationEvent(SpringApplicationEvent event) { 2 if (!Boolean.getBoolean("spring.backgroundpreinitializer.ignore") && event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) { 3 this.performPreinitialization(); 4 } 5 if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent) && preinitializationStarted.get()) { 6 try { 7 preinitializationComplete.await(); 8 } catch (InterruptedException var3) { 9 Thread.currentThread().interrupt(); 10 } 11 } 12 }
这个监听器虽然接收了当前事件,但是并没有针对它做任何处理
DelegatingApplicationListener
这个监听器之前在分析事件ApplicationStartingEvent的时候也提到过,它在接收到事件ApplicationEnvironmentPreparedEvent的时候会做一些初始化
1 public void onApplicationEvent(ApplicationEvent event) { 2 3 if (event instanceof ApplicationEnvironmentPreparedEvent) { 4 List<ApplicationListener<ApplicationEvent>> delegates = this.getListeners(((ApplicationEnvironmentPreparedEvent)event).getEnvironment()); 5 if (delegates.isEmpty()) { 6 return; 7 } 8 9 this.multicaster = new SimpleApplicationEventMulticaster(); 10 Iterator var3 = delegates.iterator(); 11 12 while(var3.hasNext()) { 13 ApplicationListener<ApplicationEvent> listener = (ApplicationListener)var3.next(); 14 this.multicaster.addApplicationListener(listener); 15 } 16 } 17 18 if (this.multicaster != null) { 19 this.multicaster.multicastEvent(event); 20 } 21 22 }
进入第一行的getListeners方法,它从Environment中获取了context.listener.classes属性,我们可以在这个属性中配置一些自定义的监听器,获取到类名后实例化,返回实例化后的监听器列表
1 private List<ApplicationListener<ApplicationEvent>> getListeners(ConfigurableEnvironment environment) { 2 if (environment == null) { 3 return Collections.emptyList(); 4 } else { 5 String classNames = environment.getProperty("context.listener.classes"); 6 List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList(); 7 if (StringUtils.hasLength(classNames)) { 8 Iterator var4 = StringUtils.commaDelimitedListToSet(classNames).iterator(); 9 10 while(var4.hasNext()) { 11 String className = (String)var4.next(); 12 13 try { 14 Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader()); 15 Assert.isAssignable(ApplicationListener.class, clazz, "class [" + className + "] must implement ApplicationListener"); 16 listeners.add((ApplicationListener)BeanUtils.instantiateClass(clazz)); 17 } catch (Exception var7) { 18 throw new ApplicationContextException("Failed to load context listener class [" + className + "]", var7); 19 } 20 } 21 } 22 23 AnnotationAwareOrderComparator.sort(listeners); 24 return listeners; 25 } 26 }
若这一步得到的监听器列表不为空,即我们通过context.listener.classes属性配置了一些监听器,那么它就初始化内部的事件多播器,并把这些监听器添加到多播器中
后续再接受到事件,包括当前的这个事件,会通过这个多播器向我们配置的监听器进行广播
FileEncodingApplicationListener
1 public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { 2 ConfigurableEnvironment environment = event.getEnvironment(); 3 if (environment.containsProperty("spring.mandatory-file-encoding")) { 4 String encoding = System.getProperty("file.encoding"); 5 String desired = environment.getProperty("spring.mandatory-file-encoding"); 6 if (encoding != null && !desired.equalsIgnoreCase(encoding)) { 7 logger.error("System property 'file.encoding' is currently '" + encoding + "'. It should be '" + desired + "' (as defined in 'spring.mandatoryFileEncoding')."); 8 logger.error("Environment variable LANG is '" + System.getenv("LANG") + "'. You could use a locale setting that matches encoding='" + desired + "'."); 9 logger.error("Environment variable LC_ALL is '" + System.getenv("LC_ALL") + "'. You could use a locale setting that matches encoding='" + desired + "'."); 10 throw new IllegalStateException("The Java Virtual Machine has not been configured to use the desired default character encoding (" + desired + ")."); 11 } 12 } 13 }
这个监听器用来对文件编码做一个校验,如果我们配置了属性spring.mandatory-file-encoding,并且系统属性file.encoding不为空,那么这两个属性指定的文件编码必须一致
标签:file,SpringBoot,encoding,ApplicationEnvironmentPreparedEvent,environment,源码,监听器, From: https://www.cnblogs.com/fnlingnzb-learner/p/16800734.html