(1). 概述

在前分析了,Spring Boot是通过(SPI)LoggingSystemFactory工厂加载:LoggingSystem(以LogbackLoggingSystem为例),然后,调用LoggingSystem的相关方法加载XML.

(2). Logback加载xml的顺序

protected String[] getStandardConfigLocations() {
	// ********************************************************************
	// logback-test.groovy
	// logback-test.xml
	// logback.groovy
	// logback.xml
	// ********************************************************************
	return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
}

(3). Logback加载xml(spring)的顺序

protected String[] getSpringConfigLocations() {
	String[] locations = getStandardConfigLocations();
	for (int i = 0; i < locations.length; i++) {
		// ********************************************************************
		String extension = StringUtils.getFilenameExtension(locations[i]);
		// logback-test-spring.groovy
		// logback-test-spring.xml
		// logback-spring.groovy
		// logback-spring.xml
		// ********************************************************************
		locations[i] = locations[i].substring(0, locations[i].length() - extension.length() - 1) + "-spring."
				+ extension;
	}
	return locations;
}

(4). LogbackLoggingSystem.initialize

public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
	LoggerContext loggerContext = getLoggerContext();
	if (isAlreadyInitialized(loggerContext)) {
		return;
	}
	// ********************************************************************
	// 调用父类AbstractLoggingSystem.initialize
	// ********************************************************************
	super.initialize(initializationContext, configLocation, logFile);
	loggerContext.getTurboFilterList().remove(FILTER);
	markAsInitialized(loggerContext);
	if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
		getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY
				+ "' system property. Please use 'logging.config' instead.");
	}
}

(5). AbstractLoggingSystem.initialize

public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
	if (StringUtils.hasLength(configLocation)) {
		initializeWithSpecificConfig(initializationContext, configLocation, logFile);
		return;
	}
	
	// ********************************************************************
	// ********************************************************************
	initializeWithConventions(initializationContext, logFile);
} // end initialize

private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
	
	// ********************************************************************
	// 1. 读取如下配置
	// logback-test.groovy
	// logback-test.xml
	// logback.groovy
	// logback.xml
	// ********************************************************************
	String config = getSelfInitializationConfig();
	if (config != null && logFile == null) {
		// self initialization has occurred, reinitialize in case of property changes
		reinitialize(initializationContext);
		return;
	}
	
	// 2. 读取spring的配置
	if (config == null) {
		// ********************************************************************
		// logback-test-spring.groovy
		// logback-test-spring.xml
		// logback-spring.groovy
		// logback-spring.xml
		// ********************************************************************
		config = getSpringInitializationConfig();
	}
	
	// 3. 加载配置文件
	if (config != null) {
		// ********************************************************************
		// 委托给子类:LogbackLoggingSystem.loadConfiguration
		// ********************************************************************
		loadConfiguration(initializationContext, config, logFile);
		return;
	}
	loadDefaults(initializationContext, logFile);
} // end 

(6). LogbackLoggingSystem.loadConfiguration

protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
			LogFile logFile) {
	super.loadConfiguration(initializationContext, location, logFile);
	LoggerContext loggerContext = getLoggerContext();
	stopAndReset(loggerContext);
	try {
		// ********************************************************************
		// 加载xml
		// ********************************************************************
		configureByResourceUrl(initializationContext, loggerContext, ResourceUtils.getURL(location));
	}
	catch (Exception ex) {
		throw new IllegalStateException("Could not initialize Logback logging from " + location, ex);
	}
	List<Status> statuses = loggerContext.getStatusManager().getCopyOfStatusList();
	StringBuilder errors = new StringBuilder();
	for (Status status : statuses) {
		if (status.getLevel() == Status.ERROR) {
			errors.append((errors.length() > 0) ? String.format("%n") : "");
			errors.append(status.toString());
		}
	}
	if (errors.length() > 0) {
		throw new IllegalStateException(String.format("Logback configuration error detected: %n%s", errors));
	}
}

(7). LogbackLoggingSystem.configureByResourceUrl

private void configureByResourceUrl(LoggingInitializationContext initializationContext, LoggerContext loggerContext,
			URL url) throws JoranException {
	if (XML_ENABLED && url.toString().endsWith("xml")) {
		// ********************************************************************
		// SpringBootJoranConfigurator是对xml标签的扩展
		// JoranConfigurator是加载logback.xml的入口(里面定义logback.xml解析的规则)
		// ********************************************************************
		JoranConfigurator configurator = new SpringBootJoranConfigurator(initializationContext);
		configurator.setContext(loggerContext);
		configurator.doConfigure(url);
	}
	else {
		new ContextInitializer(loggerContext).configureByResource(url);
	}
}

(8). Logback类结构图

类图对照XML查看,效果更明显

"Logback类结构图"

(9). 总结

从上面能看得出来Logback加载配置语言件的顺序为:logback-test.groovy/logback-test.xml/logback.groovy/logback.xml/logback-test-spring.groovy/logback-test-spring.xml/logback-spring.groovy/logback-spring.xml.