SpringBoot 原理分析

SpringBoot自动配置

Condition

Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建Bean操作。

思考:比如 SpringBoot 是如何知道要创建哪个 Bean 的?比如 SpringBoot 是如何知道要创建 RedisTemplate 的?

背景(结合上面的思考)

  1. 创建一个 SpringBoot项目,不整合框架
java 复制代码
@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
        //获取 bean
        Object redisTemplate = context.getBean("redisTemplate");
        System.out.println(redisTemplate);

    }
}

此时会报错 No bean named 'redisTemplate' available

  1. 导入 redis 的依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

再次启动发现没有报错,说明我们找到了 bean


实例一

在 Spring 的 IOC 容器中有一个 User 的 Bean,现要求:

1.导入 Jedis 坐标厚,加载该 Bean,没导入,则不加载

1. 创建一个 User 实体类

java 复制代码
public class User {

}

2. 创建一个配置类 UserConfig

java 复制代码
@Configuration
public class UserConfig {
    @Bean
    public User user() {
        return new User();
    }
}

3. 在启动类中获取user 的 bean

java 复制代码
@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
        Object user = context.getBean("user");
        System.out.println(user);

    }
}

此时可以获取到容器中定义的 bean>user

4. 在创建 bean 的时候使用注解@Conditional

首先我们要知道@Conditional 这个注解的作用是什么
@Conditional 注解是 Spring Framework 提供的一个条件注解,用于根据指定条件决定是否创建某个 Bean 或者应用某个配置。具体来说,@Conditional 注解的作用是根据指定的条件判断是否满足,若条件满足则才会创建相应的 Bean 或者应用相应的配置。 使用 @Conditional 注解可以实现一些条件化的 Bean 创建或者配置,例如基于环境、系统属性、配置值等进行条件判断。这样可以根据不同的条件,灵活地控制 Spring 应用中的 Bean 创建和配置。

1️⃣ 创建条件类实现条件接口

java 复制代码
package cn.hxy.springbootcondition.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 context, AnnotatedTypeMetadata metadata) {
        return false;
    }
}

2️⃣ 使用条件类

java 复制代码
@Configuration
public class UserConfig {
    @Bean
    @Conditional(ClassCondition.class)
    public User user() {
        return new User();
    }
}
java 复制代码
@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

此时当我们启动时,会发现找不到 user 的 bean,因为此时的 condition 的条件对象返回的是 false

5. 根据是否导入Jedis 判断是否让条件类返回 true

在条件类中判断 Jedis 是否导入坐标

java 复制代码
package cn.hxy.springbootcondition.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 context, AnnotatedTypeMetadata metadata) {
        boolean flag = true;
        try {
            //根据路径去查询文件类型,如果不存在就会报错,然后异常被捕获,执行 catch 中的代码
            Class<?> aClass = Class.forName("redis.clients.jedis.Jedis");
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;
    }
}

如果导入了,此时条件类返回为 true,那么@conditional 注解满足条件,执行对应的 bean 创建注入,在启动时打印user成功

如果没有导入对应的 Jedis 坐标,那么 ClassCondition 中的判断结果为 false,则 bean 不会被创建注入,在启动时打印 user 找不到 bean

实例二

将类的判断定义为动态的.判断哪个字节码文件存在可以动态指定.

1. 定义一个注解 ConditionOnClass

将我们之前的 conditional 注解放在自定义的注解上

java 复制代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ClassOnCondition {
    String[] value();
}

2. 在要创建 bean 上使用我们自定义的注解判断

java 复制代码
package cn.hxy.springbootcondition.config;

import cn.hxy.springbootcondition.condition.ClassOnCondition;
import cn.hxy.springbootcondition.domain.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class UserConfig {
    @Bean
    @ClassOnCondition("redis.clients.jedis.Jedis")
    public User user() {
        return new User();
    }
}

3. 修改我们的ClassCondition 条件类

java 复制代码
package cn.hxy.springbootcondition.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.util.Map;


/**
 * context 上下文对象,用于获取环境、ioc 容器、classLoader 对象
 * metadata 注解元对象,可以用于获取注解定义的属性值
 */
public class ClassCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //获取注解属性值 value
        Map<String, Object> map = metadata.getAnnotationAttributes(ClassOnCondition.class.getName());
        System.out.println(map);
        //可以指定多个属性值进行判断 
        String[] value = (String[]) map.get("value");
        boolean flag = true;
        try {
            for (String s : value) {
                //根据路径去查询文件类型,如果不存在就会报错,然后异常被捕获,执行 catch 中的代码
                Class<?> aClass = Class.forName(s);
            }
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;
    }
}

4. 修改我们的 pom 导入新的坐标测试

导入 fastjson 坐标进行测试

xml 复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.9.graal</version>
</dependency>

在创建 user 的 bean时使用自定义注解传入坐标

java 复制代码
@Configuration
public class UserConfig {
    @Bean
    @ClassOnCondition("com.alibaba.fastjson.JSON")
    public User user() {
        return new User();
    }
}

此时如果 fastjson 的坐标导入了,那么我们自定义注解中使用的自定义条件类判断的结果为 true,那么创建 user 的 bean。 否则不创建 user 的 bean,打印 user 失败,找不到 bean

那么回到我们的思考:SpringBoot 是如何知道要创建哪个 Bean 的?其实就是通过我们的 Condition来判断,判断当前环境中有没有导入我们的redis,如果导入了,就帮我们创建 RedisTemplate
其实在我们的 Springboot 中已经帮我们定义好了很多 condition 的注解,我们只需要去按照不同功能进行使用即可

java 复制代码
@Bean
@ConditionalOnBean(DataSource.class)
public User user1() {
    return new User();
}

例如,当我们要创建一个 user1 的 bean 时,只有当容器中存在 DataSource 类型的 Bean 时,user1的 Bean 才会被创建注册到容器中。

SpringBoot监听机制

SpringBoot启动流程分析

相关推荐
千叶寻-35 分钟前
正则表达式
前端·javascript·后端·架构·正则表达式·node.js
小咕聊编程2 小时前
【含文档+源码】基于SpringBoot的过滤协同算法之网上服装商城设计与实现
java·spring boot·后端
追逐时光者8 小时前
推荐 12 款开源美观、简单易用的 WPF UI 控件库,让 WPF 应用界面焕然一新!
后端·.net
Jagger_8 小时前
敏捷开发流程-精简版
前端·后端
苏打水com9 小时前
数据库进阶实战:从性能优化到分布式架构的核心突破
数据库·后端
西瓜er9 小时前
JAVA:Spring Boot 集成 FFmpeg 实现多媒体处理
java·spring boot·ffmpeg
间彧10 小时前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
间彧10 小时前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧10 小时前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
间彧10 小时前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端