SpringBoot

【黑马程序员SpringBoot2全套视频教程,springboot零基础到项目实战(spring boot2完整版)】

SpringBoot 原理篇


文章目录

  • SpringBoot
  • SpringBoot 原理篇
  • 1 自动配置
  • 1.8 bean 的加载方式【六】
  • 1.8.1 ImportSelector


1 自动配置

1.8 bean 的加载方式【六】
1.8.1 ImportSelector

OK,上一节又说完了第五种

使用上下文对象在容器初始化完毕后注入bean

spring boot bean加载顺序 springboot加载bean原理_加载

下面就来说说第六种

第六种方式平时咱们自己用的少,但是在框架内部大量使用

先来一个全新的类

package com.dingjiaxiong.bean;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

/**
 * ClassName: MyImportSelector
 * date: 2022/10/24 15:39
 *
 * @author DingJiaxiong
 */

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.dingjiaxiong.bean.Dog"};
    }
}

数组中是要加载bean 的全路径名

再来一个新的配置类

package com.dingjiaxiong.config;

import com.dingjiaxiong.bean.Dog;
import com.dingjiaxiong.bean.MyImportSelector;
import org.springframework.context.annotation.Import;

/**
 * ClassName: SpringConfig6
 * date: 2022/10/24 14:52
 *
 * @author DingJiaxiong
 */

@Import(MyImportSelector.class)
public class SpringConfig6 {
}

最后来个运行类

package com.dingjiaxiong.app;

import com.dingjiaxiong.bean.Cat;
import com.dingjiaxiong.bean.Mouse;
import com.dingjiaxiong.config.SpringConfig4;
import com.dingjiaxiong.config.SpringConfig6;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * ClassName: App6
 * date: 2022/10/24 13:42
 *
 * @author DingJiaxiong
 */

public class App6 {

    public static void main(String[] args) {

        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig6.class);

        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("=========================");

    }

}

OK,直接运行

spring boot bean加载顺序 springboot加载bean原理_加载_02

OK, 效果很明显,已经上来了

还可以写多个

spring boot bean加载顺序 springboot加载bean原理_spring boot_03

没毛病

加载就是这样了,难免会有人想问,为嘛要折腾这?

这种方式的关键

spring boot bean加载顺序 springboot加载bean原理_spring_04

在于这个东西

Metadata 元数据:

举个例子,我们造了一个数据库、表,表里面有数据,那么这个数据表的信息在哪儿描述?

还得有另外一张表来描述这张表的结构,那这另外这张表就可以称为被描述表的元数据

还可以有元元数据、元元元数据…【数据库中有四层】

在我们这儿,元数据就是往上追溯一下

【这个东西有什么用?】

修改一下

package com.dingjiaxiong.bean;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

/**
 * ClassName: MyImportSelector
 * date: 2022/10/24 15:39
 *
 * @author DingJiaxiong
 */

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        System.out.println(importingClassMetadata.getClassName());

        return new String[]{"com.dingjiaxiong.bean.Dog"};
    }
}

先拿个classname 看看

spring boot bean加载顺序 springboot加载bean原理_spring boot_05

意思就是 importingClassMetadata 这个形参描述的是 SpringConfig6

简单的说,

spring boot bean加载顺序 springboot加载bean原理_spring boot_06

加载的是谁,描述的就是谁

再来

System.out.println(importingClassMetadata.hasAnnotation("org.springframework.context.annotation.Configuration"));

这个意思就是描述的那个东西上面有没有Configuration 注解

直接看看

spring boot bean加载顺序 springboot加载bean原理_加载_07

如果我注掉

spring boot bean加载顺序 springboot加载bean原理_java_08

这样就false了

所以现在就可以明确了 importingClassMetadata 这个指的就是它那个类出现在谁上面

spring boot bean加载顺序 springboot加载bean原理_spring boot_09

意思就是SpringConfig6 就是它对应的元数据

再来一个

spring boot bean加载顺序 springboot加载bean原理_java_10

现在我想知道,它有没有加basePackages 这个属性

package com.dingjiaxiong.bean;

import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.Map;

/**
 * ClassName: MyImportSelector
 * date: 2022/10/24 15:39
 *
 * @author DingJiaxiong
 */

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        System.out.println("===========================");

        System.out.println(importingClassMetadata.getClassName());
        System.out.println(importingClassMetadata.hasAnnotation("org.springframework.context.annotation.Configuration"));
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes("org.springframework.context.annotation.ComponentScan");

        System.out.println(attributes);
        
        System.out.println("===========================");
        return new String[]{"com.dingjiaxiong.bean.Dog"};
    }
}

OK,直接运行

spring boot bean加载顺序 springboot加载bean原理_加载_11

OK, 没啥毛病

到现在大概就知道了,我们可以利用这个东西去做一系列的判定,ImportSelector 的意义就在于我们可以进行各种条件的判定,判定完毕后,决定是否装载指定的bean

举个栗子

package com.dingjiaxiong.bean;

import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.Map;

/**
 * ClassName: MyImportSelector
 * date: 2022/10/24 15:39
 *
 * @author DingJiaxiong
 */

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        boolean flag = importingClassMetadata.hasAnnotation("org.springframework.context.annotation.Configuration");
        if (flag){
            return new String[]{"com.dingjiaxiong.bean.Dog"};
        }
        return new String[]{"com.dingjiaxiong.bean.Cat"};
    }
}

判定运行结果

spring boot bean加载顺序 springboot加载bean原理_java_12

就是这样,意思就是这个东西不仅能够加载bean,它还可以进行条件判定、然后控制加载谁

OK,回顾一下

导入实现了ImportSelector接口的类,实现对导入源的编程式处理

spring boot bean加载顺序 springboot加载bean原理_加载_13

相当于现在谁导入这个类,它就可以查谁的户口

SpringConfig6 导入了它,它就可以查SpringConfig6 的户口

spring boot bean加载顺序 springboot加载bean原理_spring_14

OK