一、Condition注解
1.情景一:判断指定文件是否存在
需求:在Spring的IoC容器中有一个User的bean,要求:
导入Jedis坐标后,加载该Bean,没有导入,则不加载。
实现:
项目结构:
(1)创建User类:
package mainDir.domain;
public class User {
private String name;
private int age;
//getter & setter & toString
......
}
(2)创建bean定义的源UserConfig:
package mainDir.config;
import mainDir.condition.ClassCondition;
import mainDir.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
//@Configuration类允许通过调用同一类中的其他@Bean方法来定义bean之间的依赖关系
@Configuration
public class UserConfig {
//@Bean注解用于告诉该方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。
//产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象
//放在自己的IOC容器中
@Bean
@Conditional(ClassCondition.class)
public User user(){
//instantiate, configure and return bean
//通过这个方法生成一个名为user的Bean
return new User();
}
}
如果你不清楚@Configuration标签和@Bean注解的作用,可以参考下面的博客:
@Configuration注解详解_技术宅丶拾年的博客-CSDN博客
大白话讲解Spring的@bean注解 - 知乎 (zhihu.com)
接下来我们来关注@Conditional注解的使用:
首先,在这个注解中,我们要传入一个静态类ClassCondition(这个类名可以自定义),在这个类中编写判断user()方法是否执行的条件。
(3)编写ClassCondition类进行测试:
package mainDir.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class ClassCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return false;
}
}
启动类:(测试是否获取到bean)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
//返回Spring的IoC容器
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
//获取bean -- user
User user = context.getBean("user", User.class);
System.out.println(user);
}
}
注意你引入的Condition接口是否正确:org.springframework.context.annotation.Condition。
在上面的代码中,如果直接返回false,那么这个方法将不会被调用。同理,你在进行测试的时候如果执行context.getBean("user")时,这个语句将会报错:No bean named 'user' available
在进行判断前,我们在路径中加入Jedis依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
接下来修改判断条件文件ClassCondition:
public class ClassCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
boolean flag = true;
try {
//思路:判断redis.clents.jedis.Jedis.class文件是否存在
Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
}catch (ClassNotFoundException e){
flag = false;
}
return flag;
}
}
此时,你可以修改这个文件,观察@Conditional注解下的bean能否成功创建。
2.情景二:进行动态判断
需求:现在需要在书写UserConfig.class时再向condition文件中传入要进行判断的参数(而非直接写在定义好的ClassCondition类中)。
(1)创建自己的注解@ConditionOnClass:
package com.test.condition;
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
//在自定义注解上添加三个原注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
String[] value();
}
注意这个类上有四个注解,前三个注解可以在Conditional类的定义中找到。
第四个注解@Conditional(ClassCondtion.class)则指向你之前定义的条件类。这个注解中写入你进行判断的方法。
(2)ClassCondition类:
package com.test.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;
public class ClassCondition implements Condition {
/***
* matches方法的两个参数能够帮助我们动态地获取需要进行判断的信息
* @param context 上下文对象。用于获取环境、IoC容器、ClassLoader对象等
* @param metadata 注解元对象。可以用于获取注解定义的属性值
* @return
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取环境信息(注意导入的是springframework包下的类)
//Environment environment = context.getEnvironment();
//还可以通过环境获取配置类
//environment.getProperty();
//通过注解属性value指定的坐标后创建bean
//获取注解属性值value
Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
//获取"value"对应的值(即在@ConditionOnClass()自定义注解中传入的值)
String value[] = (String[]) map.get("value");
boolean flag = true;
try {
//遍历value数组的值,即各级路径,若存在一个路径对应不上,则说明文件不存在
for (String className : value){
Class<?> cls = Class.forName(className);
}
}catch (ClassNotFoundException e){
flag = false;
}
return flag;
}
}
在这个类中,写入你要进行判断的条件。与情景一不同的是,你将获取到由自定义注解ConditionOnClass传入的参数String value[]。可以在metadata属性中使用getAnnotationAttributes方法来进行获取。
(3)接下来,就可以使用启动类进行测试,和情景一相同。
小结
自定义条件:自定义类实现Condition接口,重写matches方法,在matches方法中进行逻辑判断,返回boolean值。matches方法有两个参数:
context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。
metaData:元数组对象,用于获取注解属性
判断条件:初始化bean时,使用@Condition(条件类.class)注解
SpringBoot提供的常用条件注解
@ConditionalOnProperty:当配置文件中(application.yml)指定的属性和对应的值存在,才会创建bean。
@ConditionalOnClass:判断环境中是否有对应字节码文件才初始化bean。
@ConditionalOnMissingBean:判断环境中有没有对应bean才初始化bean。
二、切换内置服务器
在依赖文件的web包下,可以找到一个叫做embedded的文件夹,里面有包含Tomcat在内的4种内置服务器:
接下来,我们找到文件夹中的配置类EmbeddedWebServerFactoryCustomizerAutoConfiguration:
在这个类中,我们可以看到刚刚学到的@Condition注解。@ConditionalOnClass判断环境中是否有相应字节码文件,才初始化bean。
接下来,我们看spring-boot-starter-web中的依赖:
现在我们来排除tomcat的依赖: 点击tomcat,右键Exclude,在pom.xml文件中观察变化:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
可以发现,现在tomcat的依赖已经被排除。接下来引入对jetty的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
现在重启服务器,观察控制台可以发现,当前启动的服务器为jetty:
文章来源:https://www.toymoban.com/news/detail-832830.html
参考资源:
【黑马程序员SpringBoot教程,6小时快速入门Java微服务架构Spring Boot】 https://www.bilibili.com/video/BV1Lq4y1J77x/?p=22&share_source=copy_web&vd_source=97444cc205d3d3ef369bafb60cf90ca0文章来源地址https://www.toymoban.com/news/detail-832830.html
到了这里,关于SpringBoot自动配置-Condition/切换内置服务器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!