关于conditionalonclass的信息

http://www.itjxue.com  2023-01-15 10:07  来源:未知  点击次数: 

spring的 @ConditionalOnClass 这种配置是可以提高效率吗?直接从字典表拿不好吗?

这跟效率有什么关系呢。。这是表示按条件注入配置Bean

有了解springboot的autoconfigure包嘛? 为什么能开箱即用呢,springboot已经做好了这些配置类,你依赖相关的底层组件就能自动被Spring容器管理使用了,那你用redis时依赖个spring-boot-starter-data-redis就能直接使用redis的缓存了,为什么啊,因为springboot已经写好了RedisAutoConfiguraion,还有Properties,通过conditional看redis相关的类有,就会自动注入配置了,那你要不使用redis它还给你注入不就报错了,根本找不到redis的类怎么注入

这些条件控制就是提供了多个实现的可能,比如你们用了缓存功能,单机时使用本地缓存,集群时使用分布式缓存,那你就可以用这种控制来选择使用那套配置,是单机的配置还是集群的配置

SpringBoot中所有@Conditional注解和作用说明

1、@ConditionalOnClass,当classpath下发现该类的情况下进行自动配置。

2、@ConditionalOnMissingBean,当Spring Context中不存在该Bean时。

3、@ConditionalOnProperty(prefix = "example.show",value = "enabled",havingValue = "true"),当配置文件中example.show.enabled=true时。

4、@ConditionalOnBean:当容器中有指定的Bean的条件下

5、@ConditionalOnExpression:基于SpEL表达式作为判断条件

6、@ConditionalOnJava:基于JVM版本作为判断条件

7、ConditionalOnJndi:在JNDI存在的条件下查找指定的位置

8、@ConditionalOnMissingClass:当类路径下没有指定的类的条件下

9、@ConditionalOnNotWebApplication:当前项目不是Web项目的条件下

10、@ConditionalOnResource:类路径下是否有指定的资源

11、@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者在有多个Bean的情况下,用来指定首选的Bean @ConditionalOnWebApplication:当前项目是Web项目的条件下

嵌入式Servlet容器

SpringBoot 最显著的特点之一,就是 web 项目不用打成 war 包,放在自己安装的 tomcat 中运行,而是直接打成 jar 包,直接用 java -jar 运行即可。在项目的 jar 包中可以看到有关 tomcat 的包,Spring Boot 自动配置了 tomcat,并且在启动项目时启动容器。

容器工厂,顾名思义,就是用来生产容器的类

配置文件中又有三个内部配置类,分别对应 undertow, jetty, tomcat三种容器,通过 @Import 导入,并且利用 @ConditionalOnClass, @ConditionalOnMissingBean 进行条件装配。总的来说,实现自动配置有两个前提条件:

spring-boot-starter-web 中已经默认引入了 tomcat 依赖(并且没有其他几种容器的依赖),这就满足了第一条;而且一般情况下也不会自定义容器工厂,所以容器中默认会注入 tomcatServletWebServerFactory,也就是 tomcat 容器工厂。

@EnableConfigurationProperties({ServerProperties.class})则是将配置文件中相关属性和 ServerProperties 绑定,并且注入了该组件,组件属性的用这个组件的属性进进行填充。所以我们可以通过修改配置文件来配置容器。

程序会将 ApplicationContext 包装成 ServletWebServerApplicationContext,

所以,ServletWebServerApplicationContext 调用 onfresh,createWebServer 获取容器工厂,并用容器工厂创建容器并返回。而且,TomcatWebServer 的构造器拥有初始化方法initialize---this.tomcat.start(),所以,容器初始化就自动启动了。

根据上面的结论,我们只需要导入对应的依赖,并且在pom中将默认的 tomcat 依赖排除即可。

*3.1. 在配置文件中配置:

自动配置类中,可以看到 @EnableConfigurationProperties({ServerProperties.class}),属性是和 ServerProperties 绑定的,即 server 开头的配置,如:server.port,server.tomcat.**。这种方法最简单直接。

3.2. 直接自定义 ConfigurableServletWebServerFactory

因为上面源码可以看到,创建容器是基于容器工厂的,所以可以直接用 @Bean 注入一个该类组件,创建自己的工厂,并给工厂设置属性。

3.3. 实现 WebServerFactoryCustomizerConfigurableServletWebServerFactory

Customizer 就是把配置文件的值和 ServletWebServerFactory 进行绑定,所以也可以自己用 @Bean 注入一个 Customizer,自己绑定属性。

SpringBoot条件注解ConditionalOnClass底层原理

最近使用了ConditionalOnClass注解,但是不知道底层是怎么实现的,趁周末看一下,顺便进行记录。

先来看一下结构:核心是OnClassCondition这个类,最上层实现了Condition接口。最后判断是否存在这个类,是使用的ClassLoader.loadClass()方法。

然后看@ConditionalOnClass注解源码

它有2个属性,分别是类数组和字符串数组(作用一样,类型不一样),而且被@Conditional注解所修饰,这个@Conditional注解有个名为values的 Class ? extends Condition [] 类型的属性。 这个Condition是个接口,用于匹配组件是否有资格被容器注册,定义如下:

也就是说@Conditional注解属性中可以持有多个Condition接口的实现类,所有的Condition接口需要全部匹配成功后这个@Conditional修饰的组件才有资格被注册。

Condition接口有个实现抽象类SpringBootCondition,SpringBoot中所有条件注解对应的条件类都继承这个抽象类。它实现了matches方法:

然后提供了一个抽象方法getMatchOutcome等待子类去实现。

SpringBoot提供了两个基于Class的条件注解:@ConditionalOnClass(类加载器中存在指明的类)或者@ConditionalOnMissingClass(类加载器中不存在指明的类)。

@ConditionalOnClass或者@ConditionalOnMissingClass注解对应的条件类是OnClassCondition,定义如下:

先看实现的getMatchOutcome方法

最后看一下是如何匹配的,在 ListString missing = filter(onClasses, ClassNameFilter.MISSING, classLoader); 方法

ConditionalOnClass实现原理

Spring在加载类之前,会提前使用字节码技术来读取这个类(并没有使用ClassLoader),然后解析里面的 ConditionalOnClass ,再在classpath下找到对应的类,如果找到就注入,否则就不注入

其中 AnnotatedTypeMetadata 就会通过字节码技术解析得到的注解信息,可以通过断点的方式,可以跟踪注解信息是如何从字节码解析得到的

通过跟踪Spring解析流程,可以得到以下字节码解析类信息的路径如下。

注意:整个过程程序并没有使用ClassLoader加载 UserManager 类

(责任编辑:IT教学网)

更多

推荐3DMAX教程文章