本专栏将从基础开始,循序渐进,以实战为线索,逐步深入SpringBoot相关知识相关知识,打造完整的云原生学习步骤,提升工程化编码能力和思维能力,写出高质量代码。希望大家都能够从中有所收获,也请大家多多支持。
专栏地址:SpringBoot专栏
本文涉及的代码都已放在gitee上:gitee地址
如果文章知识点有错误的地方,请指正!大家一起学习,一起进步。
文章目录
1 条件注解
条件注解可用于修饰@Configuration类或@Bean方法等,表示只有当特定条件有效时,被修饰的配置类或配置方法才会生效。正是得益于条件注解的帮助,Spring Boot 的自动配置才能执行类似于如下的智能行为。
➢ 当Spring Boot检测到类加载路径包含某个框架时,会自动配置该框架的基础Bean。
➢ 只有当开发者没配置某些Bean时,Spring Boot才会在容器中自动配置对应的Bean。
➢ 只有当开发者配置了某些属性时,Spring Boot才会在容器中自动配置对应的Bean。
总结起来,Spring Boot的条件注解可支持如下几类条件。
➢ 类条件注解:@ConditionalOnClass、@ConditionalOnMissingClass。 (注意,这里指的是项目中是否有某一个类,而不是容器中是否有该类)
➢ Bean条 注解:@Conditional On Missing Bean、@Conditional On Bean、@Conditional On Single Candidate、@Conditional On Missing FilterBean。
➢ 属性条件注解:@ConditionalOnProperty。
➢ 资源条件注解:@ConditionalOnResource。
➢ Web应用条件注解:@ConditionalOnWebApplication、@ConditionalOnNotWebApplication、@ConditionalOnWarDeployment。
➢ SpEL表达式条件注解:@ConditionalOnExpression。
➢ 特殊条件注解:@ConditionalOnCloudPlatform、@ConditionalOnJava、@ConditionalOnJndi、@ConditionalOnRepositoryType。
上面这些条件注解都是基于Spring的@Conditional条件注解变化而来的。
类条件注解有两个,即@ConditionalOnClass和@ConditionalOnMissingClass,分别表示某些类存在或不存在时被修饰的类或被修饰的方法生效。@ConditionalOnClass注解可通过value或name属性指定它所要求存在的类,其中value属性值是被检查类的Class对象,name属性值是被检查类的字符串形式的全限定类名—既然是检查目标类是否存在,那么通常用 name 属性值居多;@ConditionalOnMissingClass则只能通过value属性指定它所要求不存在的类,value属性值只能是被检查类的字符串形式的全限定类名—既然要确保该类不存在,那么该类对应的Class通常也就不存在了。
1.1 @ConditionalOnClass注解
下面通过例子来示范@ConditionalOnClass注解的用法。首先创建一个Maven项目,定义如下配置类:
- src/main/java/com/example/_003configtest/config/MyConfigTest.java
package com.example._003configtest.config;
import com.example._003configtest.mybean.MyBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// proxyBeanMethods = true :单例模式,保证每个@Bean方法被调用多少次返回的组件都是同一个
// proxyBeanMethods = false :原型模式,每个@Bean方法被调用多少次返回的组件都是新创建的
@Configuration(proxyBeanMethods = false)
//仅当com.mysql.cj.jdbc.Driver类存在时该配置类生效
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
public class MyConfigTest {
@Bean
public MyBean myBean(){
return new MyBean();
}
}
上面配置类使用了@ConditionalOnClass(name=“com.mysql.cj.jdbc.Driver”)修饰,这意味着只有当com.mysql.cj.jdbc.Driver类存在时,该配置类才会上面配置类使用了@ConditionalOnClass(name=“com.mysql.cj.jdbc.Driver”)修饰,这意味着只有当com.mysql.cj.jdbc.Driver类存在时,该配置类才会生效。此处使用的com.mysql.cj.jdbc.Driver类可改为任何要检查的类,这里仅仅是随便选一个目标类进行示范,并没有任何特别的意义。
该示例的主类代码如下:
package com.example._003configtest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Application.class, args);
System.out.println(run.getBean("myBean"));
}
}
此时项目还没有导入mysql驱动,运行项目结果如下:
maven中添加mysql依赖:
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
再次访问,结果如下:
1.2 @ConditionalOnMissingClass注解
@ConditionalOnMissingClass注解的用法与@ConditionalOnClass注解的用法大致相同,只不过它要求的是被检查类不存在,这样被它修饰的配置类或方法才会生效。
1.3 @ConditionalOnMissingBean、@ConditionalOnBean、@ConditionalOnSingleCandidate注解
@ConditionalOnMissingBean、@ConditionalOnBean、@ConditionalOnSingleCandidate都用于要求目标Bean存在或不存在(带Missing的注解要求目标Bean不存在)。
如果@ConditionalOnBean、@ConditionalOnMissingBean 注解不指定任何属性,则默认根据目标Bean的类型进行检查,默认检查被修饰的方法所返回的Bean类型。例如如下代码片段:
上面配置意味着当容器中不存在MyService类型的Bean时,该配置方法就会生效。
如果要检查具有特定ID的Bean是否存在,则需要指定name属性。例如如下代码片段:
上面配置意味着只要容器中不存在ID为jdbcTemplate的Bean,该配置方法就会生效。
1.4 @ConditionalOnMissingFilterBean注解
@ConditionalOnMissingFilterBean注解相当于@ConditionalOnMissingBean的特殊版本,它专门用于检查容器中是否存在指定类型的javax.servlet.Filter,因此它只能通过value属性指定其要检查的Filter的类型。
1.5 @ConditionalOnProperty注解
@ConditionalOnProperty注解用于检查特定属性是否具有指定的属性值。该注解支持如下属性:
➢ String[] value:指定要检查的属性。
➢ String[] name:指定value属性的别名。
➢ String havingValue:指定被检查属性必须具有的属性值。
➢ String prefix:自动为各属性名添加该属性指定的前缀。
➢ boolean matchIfMissing:指定当属性未设置属性值时,是否通过检查。
例如:
@Configuration(proxyBeanMethods = true)
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
public class MyConfigTest {
@Bean
@ConditionalOnProperty(name="test",havingValue = "foo",prefix = "com.example._003configtest")
public MyBean myBean(){
return new MyBean();
}
}
配置application.properties:
# 应用名称
spring.application.name=003-config-test
# 应用服务 WEB 访问端口
server.port=8080
com.example._003configtest.test = foo
此时Bean容器中有MyBean的对象。
1.6 @ConditionalOnResource注解
@ConditionalOnResource注解的作用很简单,它要求指定的资源必须存在,其修饰的配置类或方法才会生效。使用该注解时只需指定resources属性,该属性指定必须存在的资源。
1.7 @ConditionalOnWebApplication注解
@ConditionalOnWebApplication要求当前应用必须是Web应用时,其修饰的配置类或方法才会生效。使用该注解时可通过type属性指定Web应用的类型,该属性支持如下三个枚举值。
➢ ANY:当前应用是任何Web应用时,该注解修饰的配置类或方法都会生效。
➢ REACTIVE:只有当应用是反应式Web应用时(Spring WebFlux),该注解修饰的配置类或方法才会生效。
➢ SERVLET:只有当应用是基于Servlet的Web应用时(Spring MVC),该注解修饰的配置类或方法才会生效。
下面通过一个例子来示范@ConditionalOnWebApplication 注解的用法。
在配置类中添加如下代码:
@Configuration(proxyBeanMethods = true)
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
public class MyConfigTest {
@Bean
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public MyBean myBean(){
return new MyBean();
}
}
直接访问的结果如下:
该异常说明@ConditionalOnWebApplication注解修饰的方法并未生效。上面的粗体字注解要求当前应用是反应式Web应用,只有这样该注解修饰的方法才会生效。
下面修改本例代码,改为Spring WebFlux的项目程序:
- 添加webFlux依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-webflux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.7.0</version>
</dependency>
- 修改main
package com.example._003configtest;
import com.example._003configtest.mybean.MyBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
@ImportResource("classpath:beans.xml")
@SpringBootApplication
public class Application {
public static void main(String[] args) throws InterruptedException {
SpringApplication app = new SpringApplication(Application.class);
//设置web应用的类型,如果不设置则使用默认的类型,如果有Spring Web依赖,则自动是基于Servlet的Web应用
//如果有Spring WebFlux依赖,则自动是反应式Web应用
app.setWebApplicationType(WebApplicationType.REACTIVE);
//创建Spring容器,运行Spring Boot应用
ConfigurableApplicationContext run = app.run(args);
System.out.println(run.getBean("myBean"));
}
}
此时再次访问,结果如下:
1.8 其他不常用条件注解
@ConditionalOnNotWebApplication要求当前应用不是Web应用时,该注解修饰的配置类或方法才会生效。
@ConditionalOnWarDeployment要求当前应用以传统WAR包方式被部署到Web服务器或应用服务器中时(不以独立Java程序的方式运行),该注解修饰的配置类或方法才会生效。
@ConditionalOnNotWebApplication和**@ConditionalOnWarDeployment**用起来更简单,它们都不需要指定任何属性。
@ConditionalOnExpression 注解要求指定SpEL表达式的值为true,这样其所修饰的配置类或方法才会生效。例如下面的配置类。
@Configuration(proxyBeanMethods = true) //仅当com.mysql.cj.jdbc.Driver类存在时该配置类生效 @ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver") public class MyConfigTest { @Bean //当user.active表达式为true时,该方法才会生效,user是容器中的一个bean,active是该bean的一个属性 @ConditionalOnExpression("user.active") public MyBean myBean(){ return new MyBean(); } }
- @ConditionalOnCloudPlatform 注解要求应用被部署在特定云平台上,这样其修饰的配置类或方法才会生效。该注解可通过value属性指定它所要求的云平台,该value属性支持如下枚举值。
➢ CLOUD_FOUNDRY:要求应用被部署在CLOUD_FOUNDRY云平台上时,该注解修饰的配置类或方法才会生效。
➢ HEROKU:要求应用被部署在HEROKU平台上时,该注解修饰的配置类或方法才会生效。
➢ KUBERNETES:要求应用被部署在K8s平台上时,该注解修饰的配置类或方法才会生效。
➢ SAP:要求应用被部署在SAP云平台上时,该注解修饰的配置类或方法才会生效。
@ConditionalOnJava注解对目标平台的Java版本进行检测,它既可要求目标平台的Java版本是某个具体的版本,也可要求其高于或低于某个版本。使用该注解时可指定如下两个属性。
➢ JavaVersion value:指定要求的Java版本。
➢ ConditionalOnJava.Range range:该属性支持EQUAL_OR_NEWER(大于或等于value属性指定的版本)和 OLDER_THAN(小于 value 属性指定的版本)两个枚举值。如果不指定该属性,则要求目标平台的Java版本必须是value属性所指定的版本。
- 下面的配置类示范了**@ConditionalOnJava**注解的用法。
上面粗体字注解要求目标平台的Java版本必须高于或等于11时,该配置才会生效。因此,只有用Java 11或更新版本的Java运行该程序,才会看到容器中的dateFormat Bean。
@ConditionalOnJndi注解要求指定JNDI必须存在,使用该注解时通过 value属性指定要检查的JNDI。
@ConditionalOnRepositoryType注解要求特定的Spring Data Repository被启用时,其修饰的配置类或方法才会生效。