SpringBoot自动配置

一.Condition接口

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

        当我们为spring容器添加了redis坐标后,我们就可以通过getBean()方法获取到redisTemplate对象,如果没有添加坐标则会报错,那么spring容器是怎么知道我们要配置那个类呢?

二.@conditional注解

        其实spring容器是通过@conditional注解来判断我们是否添加了Redis坐标。

        @conditional注解只有一个属性,就是一个Condition类型的数组,在Condition接口中只有一个matches方法,用于判断是否注入相关类,当matches返回值为true时,spring会进行注入,所以我们使用 @conditional注解时要给它一个属性值作为判断是否注入相关类的条件

        所以我们用一个案例说明:

案例:

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

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

        我们自定义一个配置类(UserConfig)和一个Condition的实现类(ClassCondition):

public class ClassCondition  implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        /**
         *
         * @param context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
         * @param metadata 注解元对象。 可以用于获取注解定义的属性值
         * @return
         */
        //1.需求: 导入Jedis坐标后创建Bean
        //思路:判断redis.clients.jedis.Jedis.class文件是否存在
        boolean flag = true;
        try {
            Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
        } catch (ClassNotFoundException e) {
            flag = false;
        }

        return flag;
    }

}
public class UserConfig {
    //@Conditional中的ClassCondition.class的matches方法,返回true执行以下代码,否则反之
    @Bean
    @Conditional(value= ClassCondition.class)
    public User user(){
        return new User();
    }

}

         @conditional注解的属性是我们自定义的Condition的实现类,在这个实现类中,我们重写了matches方法,用于自定义判断条件,当这个条件成立时,即我们导入了jedis坐标,spring会为我们自动注入相关类。

        当我们没有注入jedis坐标时,matches返回值为false,所以就无法创建User对象:

@SpringBootApplication
public class Springboot815ConditionZidongpeizhiApplication {

    public static void main(String[] args) {
        //启动SpringBoot的应用,返回Spring的IOC容器
        ConfigurableApplicationContext context =  SpringApplication.run(Springboot815ConditionZidongpeizhiApplication.class, args);
        /********************案例1********************/
        Object user = context.getBean("user");
        System.out.println(user);
    }
}

        当我们注入jedis坐标后,我们成功获取到了User对象:

        那当我们的判断条件为添加多个坐标时才创建对象,要是一个一个重写太麻烦,所以我们使用动态装配。

需求二:

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

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

实现步骤:

不使用@Conditional(ClassCondition.class)注解 自定义注解@ConditionOnClass,因为他和之前@Conditional注解功能一直,所以直接复制 编写ClassCondition中的matches方法:

//1.自定义注解(ConditionOnClass):

import java.lang.annotation.*;
//自定义注解(仿照conditional注解)
@Target({ElementType.TYPE, ElementType.METHOD})//可以修饰在类与方法上
@Retention(RetentionPolicy.RUNTIME)//注解生效节点runtime
@Documented//生成文档
@Conditional(value=ClassCondition.class)

public @interface ConditionOnClass {
    String[] value();//设置此注解的属性redis.clients.jedis.Jedis

}

//2.配置类

用一个map存所有的标签名,然后通过遍历来一个个判断他们的坐标是否导入了,所有坐标都添加了才会创建对象。

public class ClassCondition implements Condition {
    /**
     *
     * @param context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
     * @param metadata 注解元对象。 可以用于获取注解定义的属性值
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
        System.out.println(map);
        String[] value = (String[]) map.get("value");



        boolean flag = true;
        try {
            for (String className : value) {
                Class<?> cls = Class.forName(className);
            }
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;


    }
}

此处的value为:"com.alibaba.fastjson.JSON","redis.clients.jedis.Jedis",只有这两个坐标均添加,才会创建user对象:

情况1:没有添加fastjson坐标

user对象无法创建:

当两个坐标都添加后:

 三.@Enable注解

        SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理 是使用@Import注 解导入一些配置类,实现Bean的动态加载

@Import注解

        @Enable底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。 而@Import提供4中用法:

我们先创建一个import_demo工程,将他里面的对象导入另一个工程(demo):

//1.创建两个实体类(User和Student):

public class User {
}
public class Student {
}

//2.创建配置类:

@Configuration
public class UserConfig {
    @Bean
    public User user() {
        return new User();
    }
    @Bean
    public Student student() {
        return new Student();
    }


}

//3.ImportSelector 实现类:

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //目前字符串数组的内容是写死的,未来可以设置在配置文件中动态加载
        return new String[]{"com.apesource.domain.User", "com.apesource.domain.Student"};

    }

//4.ImportBeanDefinitionRegistrar实现类:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //AnnotationMetadata注解
        //BeanDefinitionRegistry向spring容器中注入

        //1.获取user的definition对象
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();

        //2.通过beanDefinition属性信息,向spring容器中注册id为user的对象
        registry.registerBeanDefinition("user", beanDefinition);

    }
}

demo工程:

//1.导入 import_demo坐标

 

① 导入Bean

@SpringBootApplication
//@ComponentScan("com.apesource.import_demo02.config")
@Import(User.class)//导入javaBean
public class SpringbootImportDemo01Application {
    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(ImportDemo02Application.class, args);
        /**
         * @SpringBootApplication中有@ComponentScan注解, 扫描范围:当前引导类所在包及其子包
         *  当前引导类所在包com.apesource.springbootenable03
         *  注入user类所在包com.apesource.springbootenable_other04.config
         *  因此扫描不到,所以容器中没有user
         *  解决方案:
         *          1.使用@ComponentScan扫描com.apesource.springbootenable_other04.config包
         *          2.可以使用@Import注解,加载类。这些类都会被Spring创建,并放入IOC容器
         *          3.可以对Import注解进行封装。
         *
         */
//
        Student student = context.getBean(Student.class);
        System.out.println(student);

        //获取Bean
        User user = context.getBean(User.class);
        System.out.println(user);

    }
}

② 导入配置类

@SpringBootApplication
@Import(UserConfig.class)
public class SpringbootImportDemo01Application {
    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(ImportDemo02Application.class, args);
        /**
         * @SpringBootApplication中有@ComponentScan注解, 扫描范围:当前引导类所在包及其子包
         *  当前引导类所在包com.apesource.springbootenable03
         *  注入user类所在包com.apesource.springbootenable_other04.config
         *  因此扫描不到,所以容器中没有user
         *  解决方案:
         *          1.使用@ComponentScan扫描com.apesource.springbootenable_other04.config包
         *          2.可以使用@Import注解,加载类。这些类都会被Spring创建,并放入IOC容器
         *          3.可以对Import注解进行封装。
         *
         */
//
        Student student = context.getBean(Student.class);
        System.out.println(student);

        //获取Bean
        User user = context.getBean(User.class);
        System.out.println(user);

    }
}

③ 导入 ImportSelector 实现类。

  

@SpringBootApplication
@Import(MyImportSelector.class)
public class SpringbootImportDemo01Application {
    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(ImportDemo02Application.class, args);
        /**
         * @SpringBootApplication中有@ComponentScan注解, 扫描范围:当前引导类所在包及其子包
         *  当前引导类所在包com.apesource.springbootenable03
         *  注入user类所在包com.apesource.springbootenable_other04.config
         *  因此扫描不到,所以容器中没有user
         *  解决方案:
         *          1.使用@ComponentScan扫描com.apesource.springbootenable_other04.config包
         *          2.可以使用@Import注解,加载类。这些类都会被Spring创建,并放入IOC容器
         *          3.可以对Import注解进行封装。
         *
         */
//
        Student student = context.getBean(Student.class);
        System.out.println(student);

        //获取Bean
        User user = context.getBean(User.class);
        System.out.println(user);

    }
}

④ 导入 ImportBeanDefinitionRegistrar 实现类。

@SpringBootApplication
@Import({MyImportBeanDefinitionRegistrar.class})
public class SpringbootImportDemo01Application {
    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(ImportDemo02Application.class, args);
        /**
         * @SpringBootApplication中有@ComponentScan注解, 扫描范围:当前引导类所在包及其子包
         *  当前引导类所在包com.apesource.springbootenable03
         *  注入user类所在包com.apesource.springbootenable_other04.config
         *  因此扫描不到,所以容器中没有user
         *  解决方案:
         *          1.使用@ComponentScan扫描com.apesource.springbootenable_other04.config包
         *          2.可以使用@Import注解,加载类。这些类都会被Spring创建,并放入IOC容器
         *          3.可以对Import注解进行封装。
         *
         */
//
        Student student = context.getBean(Student.class);
        System.out.println(student);

        //获取Bean
        User user = context.getBean(User.class);
        System.out.println(user);

    }
}

结果:

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值