史上最详细的 Apereo CAS 5.3开发教程:四、CAS 定制化必须明白Spring Boot自动加载的原理

版权声明:本文为博主原创文章,转载请注明出处。作者:杨雄进 https://blog.csdn.net/makyan/article/details/88907539

Apereo CAS 5.3 项目源码地址:https://github.com/apereo
CAS 系列详解:https://blog.csdn.net/makyan/column/info/36060
上一节内容:https://blog.csdn.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打包,将我们新增的接口或配置文件打包进去,这样保持了原项目的无侵入性。

猜你喜欢

转载自blog.csdn.net/makyan/article/details/88907539
Cas