Spring boot如何自动加载

对于Springboot的ConditionalOnClass注解一直非常好奇,原因是我们的jar包里面可能没有对应的class,而使用ConditionalOnClass标注的Configuration类又import了这个类,那么如果想加载Configuration类,就会报ClassNotFoundException,那么又如何取到这个类上的注解呢

SpringFactoriesLoader获取"META-INF/spring.factories"路径下的所有文件,解析出需要自动加载的类

判断的逻辑为配置中的org.springframework.boot.autoconfigure.EnableAutoConfiguration=xxx

xxx即为我们配置的需要自动加载的@Configuration标注的类

解析出需要加载的所有Configuration类,无论该类是否被ConditionOnClass注解声明,都使用OnClassCondition类进行match,判断是否需要加载当前类

做这个解析有点耗时,spring boot将筛选出了所有Configuration类数目的一半,单独放到另外一个线程中执行,这样相当于并发两个线程解析

可以参照OnClassCondition类的getOutcomes方法

具体执行解析操作的类为StandardOutcomesResolver,方法:resolveOutcomes()

以Gson自动配置类org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration 为例

package org.springframework.boot.autoconfigure.gson;

import com.google.gson.Gson;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /**
* {@link EnableAutoConfiguration Auto-configuration} for Gson.
*
* @author David Liu
* @since 1.2.0
*/
@Configuration
@ConditionalOnClass(Gson.class)
public class GsonAutoConfiguration { @Bean
@ConditionalOnMissingBean
public Gson gson() {
return new Gson();
} }

假如当前classpath下并没有引入Gson类的jar包

	private ConditionOutcome[] getOutcomes(final String[] autoConfigurationClasses,
int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
for (int i = start; i < end; i++) {
String autoConfigurationClass = autoConfigurationClasses[i]; //GsonAutoConfiguration类的字符串
Set<String> candidates = autoConfigurationMetadata
.getSet(autoConfigurationClass, "ConditionalOnClass"); //获取当前class上的ConditionOnClass注解配置
if (candidates != null) {
outcomes[i - start] = getOutcome(candidates);
}
}
return outcomes;
}

AutoConfigurationMetadataLoader类将加载META-INF/spring-autoconfigure-metadata.properties下所有的配置,如果你使用了ConditionalOnClass注解,需要写到文件中,如

	org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration.ConditionalOnClass=io.searchbox.client.JestClient

这样Set candidates = autoConfigurationMetadata.getSet就能获取到需要判断ConditionalOnclass有哪些类了,实际上上述过程是非常快速的。

下面的速度会明显变慢,原因是将根据上述执行结果,找出对应的Class是否在classpath*下可以找到

	private ConditionOutcome getOutcome(Set<String> candidates) {
try {
List<String> missing = getMatches(candidates, MatchType.MISSING,
this.beanClassLoader);
if (!missing.isEmpty()) {
return ConditionOutcome.noMatch(
ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class", "required classes")
.items(Style.QUOTE, missing));
}
}
catch (Exception ex) {
// We'll get another chance later
}
return null;
} }

使用MatchType.MISSING来判断,如果不为空,则说明缺少这个类了。

	private enum MatchType {

		PRESENT {

			@Override
public boolean matches(String className, ClassLoader classLoader) {
return isPresent(className, classLoader);
} }, MISSING { @Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
} }; private static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
} private static Class<?> forName(String className, ClassLoader classLoader)
throws ClassNotFoundException {
if (classLoader != null) {
return classLoader.loadClass(className);
}
return Class.forName(className);
} public abstract boolean matches(String className, ClassLoader classLoader); }

最终回到了原始的方法,调用classLoader.loadClass(className)来判断类是否在classpath下,加载类相对于内存计算,比较耗时,这也是为什么需要再开一个线程和主线程一起工作的原因,使用Thread.join()来等待线程结束,并获取最终结果

延伸

既然加载ConfigurationBean时,用ClassNotFound就能发现对应的类没有在classpath下,又何必多此一举,绕这么大个弯来发现没有对应的class呢?

  • 原因是ConditionalOnClass还支持输入字符串类型的class name,在Configuration中可以面向接口编程的方式来生成bean

  • Spring boot还提供了类似ConditionalOnBean的注解,有可能一个class在classpath下,而不是spring里面的bean;

    @Target({ ElementType.TYPE, ElementType.METHOD })

    @Retention(RetentionPolicy.RUNTIME)

    @Documented

    @Conditional(OnClassCondition.class)

    public @interface ConditionalOnClass {

      /**
    * The classes that must be present. Since this annotation is parsed by loading class
    * bytecode, it is safe to specify classes here that may ultimately not be on the
    * classpath, only if this annotation is directly on the affected component and
    * <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
    * use this annotation as a meta-annotation, only use the {@link #name} attribute.
    * @return the classes that must be present
    */
    Class<?>[] value() default {}; /**
    * The classes names that must be present.
    * @return the class names that must be present.
    */
    String[] name() default {};

    }

Spring boot ConditionalOnClass原理解析的更多相关文章

  1. Spring Boot启动原理解析

    Spring Boot启动原理解析http://www.cnblogs.com/moonandstar08/p/6550758.html 前言 前面几章我们见识了SpringBoot为我们做的自动配置 ...

  2. Spring Boot 运作原理

    Spring Boot 运作原理 1.Spring Boot 简介 SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了 ...

  3. Spring Boot运行原理

    概述 本文主要写了下Spring Boot运行原理,还有一个小例子. Spring4.x提供了基于条件来配置Bean的能力,而Spring Boot的实现也是基于这一原理的. Spring Boot关 ...

  4. Spring IOC设计原理解析:本文乃学习整理参考而来

    Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...

  5. spring boot启动原理步骤分析

    spring boot最重要的三个文件:1.启动类 2.pom.xml 3.application.yml配置文件 一.启动类->main方法 spring boot启动原理步骤分析 1.spr ...

  6. Spring Boot核心原理

    Spring Boot核心原理 spring-boot-starter-xxx  方便开发和配置 1.没有depoy setup tomcat 2.xml文件里面的没有没有了 @SpringBootA ...

  7. spring boot 启动原理详细解析

    我们开发任何一个Spring Boot项目,都会用到如下的启动类 1 @SpringBootApplication 2 public class Application { 3 public stat ...

  8. Spring Boot 运行原理

    Spring Boot并没有任何新的技术,全都是基于Spring4提供的技术,用优秀的设计,为Web开发提供了一套新的方式. 在HelloWorld中,我们没有进行任何显示的配置,但是程序还是运行起来 ...

  9. Spring boot运行原理-自定义自动配置类

    在前面SpringBoot的文章中介绍了SpringBoot的基本配置,今天我们将给大家讲一讲SpringBoot的运行原理,然后根据原理我们自定义一个starter pom. 本章对于后续继续学习S ...

随机推荐

  1. Python爬虫练习:爬取800多所大学学校排名、星级等

    前言 国内大学最新排名,北大反超,浙大仅第四,中科大跌至第八 时隔五年,"双一流"大学即将迎来首次大考,这也是继改变高校评断标准之后,第一次即将以官方对外发布,自然是引来了许多人的 ...

  2. nginx安装步骤

    1.下载地址:下载nginx压缩包wget -c https://nginx.org/download/nginx-1.10.1.tar.gz2.配置nginx安装所需的环境yum install g ...

  3. java基础篇1

    JAVA基础篇1 注释 单行注释 //这是一个单行注释,由两个斜杠组成,不能嵌套多行注释 多行注释 /*这是一个 多行注释 ,//里面不能嵌套多行注释, 但是可以嵌套单行注释*/ 文档注释 /**ja ...

  4. Centos-系统任务队列信息-uptime

    uptime 显示系统的当前时间.系统从启动到当前运行时间.当前总共在线用户.系统1.5.15分钟负载情况

  5. SpringBoot2.3中@Async实现异步

    启动加上@EnableAsync ,需要执行异步方法上加入@Async. 在方法上加上@Async之后 底层使用多线程技术. 不使用异步 先关代码: package com.yiyang.myfirs ...

  6. 图像分辨率 像素 大小 LCD显示 为OLED屏增加GUI支持

    1. 根据一张标准图片的分辨率,结合每个像素的大小,可以计算得到这张图片的大小(字节数) 补充点:bmp格式的图片有24色或者32色.(其一个像素点可能占用24bits或者32bits)  至于图片怎 ...

  7. 【数量技术宅 | Python爬虫系列分享】实时监控股市重大公告的Python爬虫

    实时监控股市重大公告的Python爬虫小技巧 精力有限的我们,如何更加有效率地监控信息? 很多时候特别是交易时,我们需要想办法监控一些信息,比如股市的公告.如果现有的软件没有办法实现我们的需求,那么就 ...

  8. Linux 百度网盘卡在等待页

    解决办法1 如果无法登录百度网盘,一直在加载,运行命令:rm -rf ~/baidunetdisk 然后关闭百度客户端,重新登录百度客户端. 解决办法2 如果已经登录进百度网盘,退出百度网盘时,不要直 ...

  9. 十一、模拟扫码登录微信(用Django简单的布置了下页面)发送接收消息

    为了能够模拟登陆QQ,并获取信息.对扫码登录微信进行了分析.简单的用了一下Django将获取的信息映射到页面上.(python3+pycharm) 主要过程就是: 1.获取二维码 2.扫码登录(有三种 ...

  10. PicGo图床与Typora(PicGo+Typora+GitHub的完整设置)

    PicGo图床与Typora(PicGo+Typora+GitHub的完整设置) 如何更方便的用markdown写文章,接下来按照我的步骤来,你一定可以的,这个文章包含了GitHub图床配置.PicG ...