史上最详细的 Apereo CAS 5.3开发教程:四、CAS 定制化必须明白Spring Boot自动加载的原理
Apereo CAS 5.3 项目源码地址:https://github.com/apereo
CAS 系列详解:https://blog.****.net/makyan/column/info/36060
上一节内容:https://blog.****.net/makyan/article/details/88907349
本节继上一节内容讲解
四、Spring Boot自动加载的原理
在讲解CAS 5.3 Server 端自定义注册、修改密码、验证等功能之前,我们先来了解一下Spring Boot 自动加载的原理。
4.1. SpringBoot自动加载的原理:
SpringBoot在进行SpringApplication对象实例化时会加载META-INF/spring.factories文件,将该配置文件中的配置载入到Spring容器。
4.2. 源码分析
SpringBoot在进行对象实例化时,使用的是SpringApplication,这是SpringBoot的入口程序,它通过run方法运行。
分析SpringApplication类的源码
public static void main(String[] args) throws Exception {
run(new Object[0], args);
}
查看run方法
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return (new SpringApplication(sources)).run(args);
}
run方法只是利用自己的构造器,创建自己的一个对象,然后再调用run方法。
查看这个构造器SpringApplication(sources)
public SpringApplication(ResourceLoader resourceLoader, Object... sources) {
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.resourceLoader = resourceLoader;
this.initialize(sources);
}
发现这个构造器里面调用了一个自己的initializa方法
查看initialize(sources) 方法
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = this.deduceWebEnvironment();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
根据这个方法中的两个“set”(setInitializers、setListeners),我们得知这个方法主要的作用是初始化一些对象和实例
查看getSpringFactoriesInstances方法
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
这个方法,是根据SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法获取一些名字,
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
while(urls.hasMoreElements()) {
URL 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 var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
}
}
从这个方法中,我们得知,在SpringApplication初始化时,会加载配置文件"META-INF/spring.factories" ,将此配置文件中的值
添加进ArrayList中返回,
然后通过返回的配置文件"META-INF/spring.factories"中的值,创建Spring实例
createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names) 方法如下:
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList(names.size());
Iterator var7 = names.iterator();
while(var7.hasNext()) {
String name = (String)var7.next();
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable var12) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
}
}
return instances;
}
由此得知,配置文件"META-INF/spring.factories"配置的是一些需要初始化的类,查看spring boot中的"META-INF/spring.factories"配置文件
具体内容如下:
# 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.embedded.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.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
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
这里面的内都是spring boot启动时自动加载的,由此可见,spring boot就是读取这个配置文件来加载内容。
4.3. spring boot自动加载原理的运用
好了,我们了解Spring Boot自动加载的原理后,如果我们要写一个类,让spring boot启动时也能自动加载的话,
我们就需要重写配置文件"META-INF/spring.factories",向这个配置文件中添加我们自己的类就好了。
接下来,让我们借助插件maven-war-plugin的overlay功能,为我们重写cas项目中的接口、配置文件,
然后通过maven打包,将我们新增的接口或配置文件打包进去,这样保持了原项目的无侵入性。