springboot自动配置原理

1.Condition

        Condition是在spring4.0增加的条件判断功能,通过这个功能可以实现选择性的创建bean的操作

常用到的注解:

  • @Configuration:标明该类是一个配置类,并注入容器
  • @ConditionalOnClass:当需求类均存在时,满足启用要求

1-1.创建一个springboot工程,编写启动类

        原理:我们通过springboot提供的run方法查看,发现其返回ConfigurableApplicationContext这个对象,它是spring的Ioc容器,因此我们通过它获取IOC容器,再通过IOC容器获取相应的bean

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {
        //启动springbootd时,返回spring的IOC容器
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
        //获取redisTemplate 的 bean
        Object redisTemplate = context.getBean("redisTemplate");
        System.out.println(redisTemplate);
    }

}

1-2.启动这个启动类

        这时我们并没有导入redis的依赖,所以根本无法获取redisTemplate的bean,会报这样的一个错:意思就是没有这个叫redisTemplate的bean 可以用

 1-3.导入redisTemplate的所需依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        我们再次启动,发现并没有报错,那么问题来了,springboot是如何知道我们要创建的哪个bean,接下通过一个案例来理解:

需求:在spring的IOC容器中有一个User的bean,要求导入jedis依赖后才能加载该bean,没导入则不加载

1-4.创建User类

package cn.ck.pojo;

public class User {
    private String name;
    private int age;

    //get与set方法。。。
}

1-5.创建一个条件类ClassCondition,继承Condition接口 

        该接口org.springframework.context.annotation这个包下

        实现这个接口的matches方法,其返回值为布尔值,true代表创建指定的bean,false代表不创建指定的bean

public class ClassCondition implements Condition {
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //需求:要求导入jedis依赖后才能加载该bean,没导入则不加载
        //思路:判断jedis依赖是否导入
        Boolean flag = true;
        try {
            Class<?> aClass = Class.forName("redis.clients.jedis.Jedis");
        } catch (ClassNotFoundException e) {
            //出现异常说明没有该类
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }
}

  1-6   创建配置类UserConfig用于加载创建User这个实列: 

        使用@Conditional(条件类.class)来引用我们创建的条件类,来判断是否符合我们指定的条件来生成User的bean

@Configuration
public class UserConfig {
    @Bean(name = "user")
    @Conditional(ClassCondition.class)//引用条件是否要创建该bean
    public User getUser(){
        return new User();
    }
}

1-7.编写启动类

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {
        //启动springbootd时,返回spring的IOC容器
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
        //获取user的bean
        User user = (User) context.getBean("user");
        user.setName("张三");
        user.setAge(20);
        System.out.println(user.getName()+" "+user.getAge());
    }

}

1-8.启动

        这时我们并没有导入jedid的相关依赖,所以不应该获取到user的bean

 

接着导入jedis的相关依赖:

         <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

再次运行:

能够获取到bean以及数据,说明Condition是在spring4.0增加的条件判断功能,通过这个功能可以实现选择性的创建bean的操作


需求2:

  • 在spring的IOC容器中有一个User的bean
  • 要求导入指定依赖后才能加载该bean,没导入则不加载
  • 将类的判断定义为动态的,判断哪个字节码文件存在可以通过动态指定

1-1.定义一个注解类 ConditionOnClass

import org.springframework.context.annotation.Conditional;

import java.lang.annotation.*;
//定义当前注解的使用范围
@Target({ElementType.TYPE, ElementType.METHOD})
//注解的生效时机
@Retention(RetentionPolicy.RUNTIME)
//可以生成javadoc的文档
@Documented
//将我们定义好的条件类通过@Conditional指定,这样该注解就完成了ClassCondition的功能
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
    //给当前这个注解添加属性数组,用于可以指定很多的类
    String[] value();
}

1-2.改写我们自定义的条件类

        思路:根据提供的metadata参数,来动态获取上方自定义注解的value值

public class ClassCondition implements Condition {
    /**
     * @param context  上下文对象,用于获取环境,IOC容器,以及classloader对象
     * @param metadata 注解源对象,用于获取注解定义时输入的属性值value
     * @return boolean
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //需求:导入指定依赖后才能加载该bean,没导入则不加
        //通过我们自定义的注解类的名称,来获取指定的依赖,也就是自定义注解属性的值 value
        Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
        assert map != null;
        String[] value = (String[]) map.get("value");
        boolean flag = true;
        try {
            for (String s : value) {
                Class<?> aClass = Class.forName(s);
            }
        } catch (ClassNotFoundException e) {
            //出现异常说明没有该类
            e.printStackTrace();
            flag = false;
        }
        return flag;
    }
}

1-3.在UserConfig中使用我们自定义的注解,并传入指定要判断的依赖

@Configuration
public class UserConfig {
    @Bean(name = "user")
    //自定义的注解,判断指定的依赖
    @ConditionOnClass(value = "redis.clients.jedis.Jedis")
    public User getUser(){
        return new User();
    }
}

1-4.再次启动启动类

 说明我们自定的注解是可以实现的,这时我们就可以动态的去使用该注解实现自动配置的机制

更多注解:

        回到 SpringBoot,除了一开始介绍的 @ConditionalOnClass 外,SpringBoot 还封装了这些注解,供我们使用:

  • @ConditionalOnSingleCandidate    类似于@ConditionalOnBean

  • 但只有在确定了给定bean类的单个候选项时才会加载bean。

  • @ConditionalOnMissingBean    当给定的 Bean 不存在时返回 true

  • 各类型间是 or 的关系

  • @ConditionalOnBean    与上面相反,要求bean存在

  • @ConditionalOnMissingClass    当给定的类名在类路径上不存在时返回true

  • 各类型间是 and 的关系

  • @ConditionalOnClass    与上面相反,要求类存在

  • @ConditionalOnExpression    Spel 表达式成立,返回 true

  • @ConditionalOnJava    运行时的Java版本号包含给定的版本号,返回 true

  • @ConditionalOnProperty    属性匹配条件满足,返回 true

  • @ConditionalOnWebApplication    web环境存在时,返回 true

  • @ConditionalOnNotWebApplication    web环境不存在时,返回 true

  • @ConditionalOnResource    指定的资源存在时,返回 true
     

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值