Spring boot ConditionalOnClass原理解析
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原理解析的更多相关文章
- Spring Boot启动原理解析
Spring Boot启动原理解析http://www.cnblogs.com/moonandstar08/p/6550758.html 前言 前面几章我们见识了SpringBoot为我们做的自动配置 ...
- Spring Boot 运作原理
Spring Boot 运作原理 1.Spring Boot 简介 SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了 ...
- Spring Boot运行原理
概述 本文主要写了下Spring Boot运行原理,还有一个小例子. Spring4.x提供了基于条件来配置Bean的能力,而Spring Boot的实现也是基于这一原理的. Spring Boot关 ...
- Spring IOC设计原理解析:本文乃学习整理参考而来
Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...
- spring boot启动原理步骤分析
spring boot最重要的三个文件:1.启动类 2.pom.xml 3.application.yml配置文件 一.启动类->main方法 spring boot启动原理步骤分析 1.spr ...
- Spring Boot核心原理
Spring Boot核心原理 spring-boot-starter-xxx 方便开发和配置 1.没有depoy setup tomcat 2.xml文件里面的没有没有了 @SpringBootA ...
- spring boot 启动原理详细解析
我们开发任何一个Spring Boot项目,都会用到如下的启动类 1 @SpringBootApplication 2 public class Application { 3 public stat ...
- Spring Boot 运行原理
Spring Boot并没有任何新的技术,全都是基于Spring4提供的技术,用优秀的设计,为Web开发提供了一套新的方式. 在HelloWorld中,我们没有进行任何显示的配置,但是程序还是运行起来 ...
- Spring boot运行原理-自定义自动配置类
在前面SpringBoot的文章中介绍了SpringBoot的基本配置,今天我们将给大家讲一讲SpringBoot的运行原理,然后根据原理我们自定义一个starter pom. 本章对于后续继续学习S ...
随机推荐
- 测试必备:jmeter测试http协议接口的各种传参方式
测试接口,postman和jmeter是用得最频繁的工具,特别是jmeter,支持很多种协议,且除了测功能,还能做自动化测试和性能测试.下面主要介绍jmeter测试http协议接口的用法,包含get, ...
- Azure 内容审查器之文本审查
内容审查器 Azure 内容审查器也是一项认知服务.它支持对文本.图形.视频进行内容审核.可以过滤出某些不健康的内容,关键词.使你的网站内容符合当地的法律法规,提供更好的用户体验. 文本内容审核 其中 ...
- java.lang.illegalArgumentException异常
今天在使用spring3.2的时候,配置好注解开发后,运行出现异常 java.lang.illegalArgumentException 经查为 JRE 版本域spring3.2不兼容所致, 将项目J ...
- Leetcode-栈&队列
20. 有效的括号 https://leetcode-cn.com/problems/valid-parentheses/ 给定一个只包括 '(',')','{','}','[',']' 的字符串,判 ...
- springboot项目打包瘦身
默认情况下,Spring Boot 项目发布时会将项目代码和项目的所有依赖文件一起打成一个可执行的 jar 包.但如果项目的依赖包很多,那么这个文件就会非常大.这样每次即使只改动一点东西,就需要将整个 ...
- Python导入模块的几种方法
Python 模块 Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句. 模块让你能够有逻辑地组织你的 Python 代 ...
- 019 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 13 数据类型转换的代码示例
019 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 13 数据类型转换的代码示例 本文知识点:Java中的数据类型转换案例 学习视频有误,导致没法写文,文章内容 ...
- opencv的imread函数相对路径问题和 main 参数问题
参考: https://blog.csdn.net/u013404374/article/details/80178822 https://blog.csdn.net/fujilove/article ...
- Sprign-mvc系列之Spring快速入门 什么是sprign-mvc spring-mvc的作用及其基本使用+组件解析+注解解析
Spring-mvc 什么是SpringMvc SpringMvc是一种基于java的实现Mvc设计模式的请求驱动类型的轻量级web框架,属于SpringFrameWork的后续产品,已经融合在Spr ...
- JavaScript利用函数反转数组
要求: 给定一数组,将其元素倒序排列并输出. 代码实现: // 利用函数翻转任意数组 reverse 翻转 function reverse(arr) { var newArr = []; for ( ...