Spring原理Boot

Spring原理 SpringBoot

1 Boot

1.1 Boot 骨架项目

如果是 linux 环境,用以下命令即可获取 spring boot 的骨架 pom.xml

curl -G https://start.spring.io/pom.xml -d dependencies=web,mysql,mybatis -o pom.xml

也可以使用 Postman 等工具实现

若想获取更多用法,请参考

curl https://start.spring.io

1.2 Boot War项目

步骤1:创建模块,区别在于打包方式选择 war

image-20241016211334908

接下来勾选 Spring Web 支持

image-20241016211357080

步骤2:编写控制器

@Controller
public class MyController {

    @RequestMapping("/hello")
    public String abc() {
        System.out.println("进入了控制器");
        return "hello";
    }
}

步骤3:编写 jsp 视图,新建 webapp 目录和一个 hello.jsp 文件,注意文件名与控制器方法返回的视图逻辑名一致

src
	|- main
		|- java
		|- resources
		|- webapp
			|- hello.jsp

步骤4:配置视图路径,打开 application.properties 文件

spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

将来 prefix + 控制器方法返回值 + suffix 即为视图完整路径

测试

如果用 mvn 插件 mvn spring-boot:run 或 main 方法测试

  • 必须添加如下依赖,因为此时用的还是内嵌 tomcat,而内嵌 tomcat 默认不带 jasper(用来解析 jsp)
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <scope>provided</scope>
</dependency>

也可以使用 Idea 配置 tomcat 来测试,此时用的是外置 tomcat

  • 骨架生成的代码中,多了一个 ServletInitializer,它的作用就是配置外置 Tomcat 使用的,在外置 Tomcat 启动后,去调用它创建和运行 SpringApplication

启示

对于 jar 项目,若要支持 jsp,也可以在加入 jasper 依赖的前提下,把 jsp 文件置入 META-INF/resources

1.3 Boot 启动过程

阶段一:SpringApplication 构造

演示获取 Bean Definition 源

@Configuration
public class A39_1 {
    public static void main(String[] args) {
        System.out.println("1. 演示获取 Bean Definition 源");
        SpringApplication spring = new SpringApplication(A39_1.class);
        spring.setSources(Set.of("classpath:b01.xml"));//新增来源xml
        System.out.println("2. 演示推断应用类型");
        System.out.println("3. 演示 ApplicationContext 初始化器");
        System.out.println("4. 演示监听器与事件");
        System.out.println("5. 演示主类推断");

        //在run方法中创建并初始化spring容器
        ConfigurableApplicationContext context = spring.run(args);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println("name:" + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
        }
        context.close();
    }

    static class Bean1 {

    }

    static class Bean2 {

    }

    static class Bean3 {

    }

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }

    @Bean
    public TomcatServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }
}

image-20241020221531909

image-20241020221610600

image-20241020221757203

演示推断应用类型

image-20241020222022322

image-20241020222439282

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    System.out.println("1. 演示获取 Bean Definition 源");
    SpringApplication spring = new SpringApplication(A39_1.class);
    spring.setSources(Set.of("classpath:b01.xml"));//新增来源xml
    System.out.println("2. 演示推断应用类型");
    Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
    deduceFromClasspath.setAccessible(true);
    //static WebApplicationType deduceFromClasspath() {} 静态方法,无需使用对象,所以传null,同时也不需要参数
    System.out.println("\t应用类型为:" + deduceFromClasspath.invoke(null));
    System.out.println("3. 演示 ApplicationContext 初始化器");
    System.out.println("4. 演示监听器与事件");
    System.out.println("5. 演示主类推断");

    //在run方法中创建并初始化spring容器
    ConfigurableApplicationContext context = spring.run(args);
    for (String name : context.getBeanDefinitionNames()) {
        System.out.println("name:" + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
    }
    context.close();
}

image-20241020223349537

image-20241020223429912

image-20241020223619346

演示 ApplicationContext 初始化器

image-20241020224101670

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    System.out.println("1. 演示获取 Bean Definition 源");
    SpringApplication spring = new SpringApplication(A39_1.class);
    spring.setSources(Set.of("classpath:b01.xml"));//新增来源xml
    System.out.println("2. 演示推断应用类型");
    Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
    deduceFromClasspath.setAccessible(true);
    //static WebApplicationType deduceFromClasspath() {} 静态方法,无需使用对象,所以传null,同时也不需要参数
    System.out.println("\t应用类型为:" + deduceFromClasspath.invoke(null));
    System.out.println("3. 演示 ApplicationContext 初始化器");
    spring.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {//刚刚创建,但是没有refresh的applicationContext对象
            if (applicationContext instanceof GenericApplicationContext gac) { //转换类型
                gac.registerBean("bean3",Bean3.class);//bean1,bean2之前注册了
            }
        }
    });
    System.out.println("4. 演示监听器与事件");
    System.out.println("5. 演示主类推断");

    //在run方法中创建并初始化spring容器
    ConfigurableApplicationContext context = spring.run(args);

    // 创建 ApplicationContext
    // 调用初始化器 对 ApplicationContext 做扩展
    // ApplicationContext.refresh()

    for (String name : context.getBeanDefinitionNames()) {
        System.out.println("name:" + name + " 来源:" + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
    }
    context.close();
}

image-20241020224908063

image-20241020224946227

演示监听器与事件

image-20241020225251054

image-20241020225722758

演示主类推断

image-20241020225946898

image-20241020230416580

阶段二:执行 run 方法

事件发布器 (run 1)

image-20241022224144084

image-20241022224217253

image-20241022224253359

public class A39_2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //添加 app 监听器
        SpringApplication app = new SpringApplication();
        app.addListeners(e -> System.out.println(e.getClass()));

        // 获取事件发送器实现类名
        //参数1: 接口类型 , 参数2: ClassLoader  返回值:names:多个实现类的名字
        List<String> names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, A39_2.class.getClassLoader());
        for (String name : names) {
            //org.springframework.boot.context.event.EventPublishingRunListener: 拿到了实现类的名字
            System.out.println(name);
            Class<?> clazz = Class.forName(name);//拿到类对象
            Constructor<?> constructor = clazz.getConstructor(SpringApplication.class, String[].class);//拿到类构造器
            //事件发布器对象创建完成
            EventPublishingRunListener publisher = (EventPublishingRunListener) constructor.newInstance(app, args);//创建对象

            //发布事件
            DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
            publisher.starting(bootstrapContext); //spring boot 开始启动
            publisher.environmentPrepared(bootstrapContext,new StandardEnvironment()); //环境信息准备完毕
            GenericApplicationContext context = new GenericApplicationContext();
            publisher.contextPrepared(context); //在spring 容器创建,并调用初始化器之后,发送此事件
            publisher.contextLoaded(context); // 所有 bean definition 加载完毕
            context.refresh();
            publisher.started(context); //spring 容器初始化完成(refresh 方法调用完毕)
            publisher.running(context); //spring boot 启动完毕

            publisher.failed(context,new Exception("出错")); //spring boot 启动出错
        }
    }
}

image-20241022224412592

run 方法流程( 8 -11 )

配置类获取bean 定义

public class A39_3 {
    @SuppressWarnings("all")
    public static void main(String[] args) throws Exception {
        SpringApplication app = new SpringApplication();
        //新增一个初始化器
        app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
            @Override             //需要提前准备容器,所以需要在第9.步骤回调这个初始化方法
            public void initialize(ConfigurableApplicationContext applicationContext) {

            }
        });

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 args");

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 8. 创建容器");
        GenericApplicationContext context = createApplicationContext(WebApplicationType.SERVLET);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 9. 准备容器");
        for (ApplicationContextInitializer initializer : app.getInitializers()) {
            //回调上面的初始化器方法
            initializer.initialize(context);
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 10. 加载 bean 定义");
        //设置将来读取的bean definition 存储位置:getDefaultListableBeanFactory ,还没有开始读取bean definition
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context.getDefaultListableBeanFactory());
        //解析Config类中bean 定义,并加入到bean工厂
        reader.register(Config.class);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 11. refresh 容器");
        context.refresh();//必须要先刷新,才能验证

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println("name:" + name + " 来源: " + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 12. 执行 runner");

        /*
            学到了什么
            a. 创建容器、加载 bean 定义、refresh, 对应的步骤

         */
    }

    private static GenericApplicationContext createApplicationContext(WebApplicationType type) {
        GenericApplicationContext context = null;
        switch (type) {
            case SERVLET -> context = new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE -> context = new AnnotationConfigReactiveWebServerApplicationContext();
            case NONE -> context = new AnnotationConfigApplicationContext();
        }
        return context;
    }

    static class Bean4 {

    }

    static class Bean5 {

    }

    static class Bean6 {

    }

    @Configuration
    static class Config {
        @Bean
        public Bean5 bean5() {
            return new Bean5();
        }

        @Bean
        public ServletWebServerFactory servletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }

    }
}

image-20241022230447375

xml文件获取类定义

image-20241022230959645

//xml获取类定义
XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));

image-20241022231035364

包扫描获取类定义

ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com/feng/a39boot/sub");//扫描包路径下所有类,并解析bean定义,加入到bean工厂

image-20241024204627535

run (2,12)

@Configuration
static class Config {
    @Bean
    public Bean5 bean5() {
        return new Bean5();
    }

    @Bean
    public ServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }

    @Bean
    public CommandLineRunner commandLineRunner(){
        return new CommandLineRunner() {
            @Override
            public void run(String... args) throws Exception {
                System.out.println("commandLineRunner()...." + Arrays.toString(args));
            }
        };
    }

    @Bean
    public ApplicationRunner applicationRunner(){
        return new ApplicationRunner() {
            @Override
            public void run(ApplicationArguments args) throws Exception{
                System.out.println("applicationRunner()...." + Arrays.toString(args.getSourceArgs()));
                //传递的参数分成两类,一类是带-- , 另外一类是不带的
                System.out.println(args.getOptionNames());// 获取带--的key、
                System.out.println(args.getOptionValues("server.port"));// 获取带--的key对应的值
                System.out.println(args.getNonOptionArgs());// 获取不带--的参数
            }
        };
    }
}

image-20241024212301015

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 args");
//args: main()方法的参数
DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);

image-20241024212334780

image-20241024212455899

image-20241024212558818

image-20241024212629844

完整代码

public class A39_3 {
    @SuppressWarnings("all")
    public static void main(String[] args) throws Exception {
        SpringApplication app = new SpringApplication();
        //新增一个初始化器
        app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
            @Override             //需要提前准备容器,所以需要在第9.步骤回调这个初始化方法
            public void initialize(ConfigurableApplicationContext applicationContext) {

            }
        });

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 2. 封装启动 args");
        //args: main()方法的参数
        DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 8. 创建容器");
        GenericApplicationContext context = createApplicationContext(WebApplicationType.SERVLET);

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 9. 准备容器");
        for (ApplicationContextInitializer initializer : app.getInitializers()) {
            //回调上面的初始化器方法
            initializer.initialize(context);
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 10. 加载 bean 定义");
        //设置将来读取的bean definition 存储位置:getDefaultListableBeanFactory ,还没有开始读取bean definition
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        AnnotatedBeanDefinitionReader reader1 = new AnnotatedBeanDefinitionReader(beanFactory);
        //解析Config类中bean 定义,并加入到bean工厂
        reader1.register(Config.class);
        //xml获取类定义
        XmlBeanDefinitionReader reader2 = new XmlBeanDefinitionReader(beanFactory);
        reader2.loadBeanDefinitions(new ClassPathResource("b03.xml"));
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
        scanner.scan("com/feng/a39boot/sub");//扫描包路径下所有类,并解析bean定义,加入到bean工厂

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 11. refresh 容器");
        context.refresh();//必须要先刷新,才能验证

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println("name:" + name + " 来源: " + beanFactory.getBeanDefinition(name).getResourceDescription());
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>> 12. 执行 runner");
        /**
         * springboot执行结束时会回调CommandLineRunner,ApplicationRunner
         */
        //getBeansOfType: 根据类型去拿bean  , K: bean的名字  V: bean的实例对象
        Map<String, CommandLineRunner> beansOfType = context.getBeansOfType(CommandLineRunner.class);
        for (CommandLineRunner runner : beansOfType.values()) {
            runner.run(args);
        }

        for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class)
                .values()) {
            runner.run(arguments); //applicationArguments: 第二步封装好的参数
        }

        /*
            学到了什么
            a. 创建容器、加载 bean 定义、refresh, 对应的步骤

         */
    }

    private static GenericApplicationContext createApplicationContext(WebApplicationType type) {
        GenericApplicationContext context = null;
        switch (type) {
            case SERVLET -> context = new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE -> context = new AnnotationConfigReactiveWebServerApplicationContext();
            case NONE -> context = new AnnotationConfigApplicationContext();
        }
        return context;
    }

    static class Bean4 {

    }

    static class Bean5 {

    }

    static class Bean6 {

    }

    @Configuration
    static class Config {
        @Bean
        public Bean5 bean5() {
            return new Bean5();
        }

        @Bean
        public ServletWebServerFactory servletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }

        @Bean
        public CommandLineRunner commandLineRunner(){
            return new CommandLineRunner() {
                @Override
                public void run(String... args) throws Exception {
                    System.out.println("commandLineRunner()...." + Arrays.toString(args));
                }
            };
        }

        @Bean
        public ApplicationRunner applicationRunner(){
            return new ApplicationRunner() {
                @Override
                public void run(ApplicationArguments args) throws Exception{
                    System.out.println("applicationRunner()...." + Arrays.toString(args.getSourceArgs()));
                    //传递的参数分成两类,一类是带-- , 另外一类是不带的
                    System.out.println(args.getOptionNames());// 获取带--的key、
                    System.out.println(args.getOptionValues("server.port"));// 获取带--的key对应的值
                    System.out.println(args.getNonOptionArgs());// 获取不带--的参数
                }
            };
        }
    }
}

run 3

public class Step3 {
    public static void main(String[] args) throws IOException {
        ApplicationEnvironment env = new ApplicationEnvironment(); // 系统环境变量, properties, yaml
        for (PropertySource<?> ps : env.getPropertySources()) {//来源的集合
            /** 打印结果
             * PropertiesPropertySource {name='systemProperties'}  系统属性
             * SystemEnvironmentPropertySource {name='systemEnvironment'} 系统环境变量
             *
             * 如果找到的同名的key, 系统属性优先级高
             */
            System.out.println(ps);
        }

        System.out.println(env.getProperty("JAVA_HOME"));
    }
}

image-20241024213735789

image-20241024220149784

image-20241024220344166

添加配置文件来源

image-20241024220828318

添加命令行来源

image-20241024221256130

image-20241024221340684

public class Step3 {
    public static void main(String[] args) throws IOException {
        ApplicationEnvironment env = new ApplicationEnvironment(); // 系统环境变量, properties, yaml
        //优先级添加到最后
        env.getPropertySources().addLast(new ResourcePropertySource(new ClassPathResource("application.properties")));
        //命令行添加来源
        env.getPropertySources().addFirst(new SimpleCommandLinePropertySource(args));
        for (PropertySource<?> ps : env.getPropertySources()) {//来源的集合
            /** 打印结果
             * PropertiesPropertySource {name='systemProperties'}  系统属性
             * SystemEnvironmentPropertySource {name='systemEnvironment'} 系统环境变量
             *
             * 如果找到的同名的key, 系统属性优先级高
             */
            System.out.println(ps);
        }

        System.out.println(env.getProperty("JAVA_HOME"));
        System.out.println(env.getProperty("server.port"));
    }
}

run 4

public class Step4 {

    public static void main(String[] args) throws IOException, NoSuchFieldException {
        ApplicationEnvironment env = new ApplicationEnvironment();
        env.getPropertySources().addLast(
                new ResourcePropertySource("step4", new ClassPathResource("step4.properties"))
        );
        ConfigurationPropertySources.attach(env);//加入到evn中,优先级最高
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }

        System.out.println(env.getProperty("user.first-name"));
        System.out.println(env.getProperty("user.middle-name"));
        System.out.println(env.getProperty("user.last-name"));
    }
}

image-20241024222412066

run 5

后置处理器增强添加源

public class Step5 {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication();
        ApplicationEnvironment env = new ApplicationEnvironment();

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强前");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        //EnvironmentPostProcessor(后置处理器) 的实现类
        ConfigDataEnvironmentPostProcessor postProcessor1 = new ConfigDataEnvironmentPostProcessor(new DeferredLogs(), new DefaultBootstrapContext());
        postProcessor1.postProcessEnvironment(env, app);//添加一些propertySource
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后1");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        System.out.println(env.getProperty("server.port"));

        RandomValuePropertySourceEnvironmentPostProcessor postProcessor2 = new RandomValuePropertySourceEnvironmentPostProcessor(new DeferredLog());
        postProcessor2.postProcessEnvironment(env, app);//添加一些propertySource
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 增强后2");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        System.out.println(env.getProperty("random.int"));
        System.out.println(env.getProperty("random.int"));
        System.out.println(env.getProperty("random.int"));
        System.out.println(env.getProperty("random.uuid"));
        System.out.println(env.getProperty("random.uuid"));
    }

}

image-20241024224552467

image-20241024224631623

之前都是使用硬编码方法,springboot都是使用配置文件

image-20241024225624906

image-20241024225658353

image-20241024225745513

spring通过监听器实现上面步骤

image-20241024230131945

public class Step5 {
    public static void main(String[] args) {

        SpringApplication app = new SpringApplication();
        app.addListeners(new EnvironmentPostProcessorApplicationListener());

        /*List<String> names = SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, Step5.class.getClassLoader());
        for (String name : names) {
            System.out.println(name);
        }*/

        //run 的第3,4步,创建envirment对象,初始化一些属性,第5步才可以添加监听器,所以第5步,添加一个事件发布器触发监听事件
        EventPublishingRunListener publisher = new EventPublishingRunListener(app, args);
        ApplicationEnvironment env = new ApplicationEnvironment();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 发布事件前");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }
        publisher.environmentPrepared(new DefaultBootstrapContext(),env); //发布事件,触发上面的监听器,通过配置文件,添加后置处理器增强
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> 发布事件后");
        for (PropertySource<?> ps : env.getPropertySources()) {
            System.out.println(ps);
        }

    }

image-20241024231400041

run 6

image-20241028210315707

public class Step6 {
    // 绑定 spring.main 前缀的 key value 至 SpringApplication, 请通过 debug 查看
    public static void main(String[] args) throws IOException {
        SpringApplication application = new SpringApplication();
        ApplicationEnvironment env = new ApplicationEnvironment();
        env.getPropertySources().addLast(new ResourcePropertySource("step4", new ClassPathResource("step4.properties")));

        //env: 代表获取的源头  bind: 代表绑定,第一参数是properties中的key值前缀,第二个参数是实体类型  get:获取绑定赋值后的对象
        User user = Binder.get(env).bind("user", User.class).get();
        System.out.println(user);
    }

    static class User {
        private String firstName;
        private String middleName;
        private String lastName;
        public String getFirstName() {
            return firstName;
        }
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
        public String getMiddleName() {
            return middleName;
        }
        public void setMiddleName(String middleName) {
            this.middleName = middleName;
        }
        public String getLastName() {
            return lastName;
        }
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
        @Override
        public String toString() {
            return "User{" +
                   "firstName='" + firstName + '\'' +
                   ", middleName='" + middleName + '\'' +
                   ", lastName='" + lastName + '\'' +
                   '}';
        }
    }
}

image-20241028212406914

User user = new User();
Binder.get(env).bind("user", Bindable.ofInstance(user));//现有对象绑定
System.out.println(user);

image-20241028214316479

image-20241028215049836

// 绑定 spring.main 前缀的 key value 至 SpringApplication, 请通过 debug 查看
public static void main(String[] args) throws IOException {
    SpringApplication application = new SpringApplication();
    ApplicationEnvironment env = new ApplicationEnvironment();
    env.getPropertySources().addLast(new ResourcePropertySource("step4", new ClassPathResource("step4.properties")));

    //env: 代表获取的源头  bind: 代表绑定,第一参数是properties中的key值前缀,第二个参数是实体类型  get:获取绑定赋值后的对象
    /*User user = Binder.get(env).bind("user", User.class).get();
    System.out.println(user);*/

    /*User user = new User();
    Binder.get(env).bind("user", Bindable.ofInstance(user));//现有对象绑定
    System.out.println(user);*/

    env.getPropertySources().addLast(new ResourcePropertySource("step6", new ClassPathResource("step6.properties")));
    Binder.get(env).bind("spring.main", Bindable.ofInstance(application));
    System.out.println(application);
}

step6主要是把spring.main中的属性和配置文件中的值绑定

run 7

输出图片

image-20241028222342387

public class Step7 {
    public static void main(String[] args) {
        ApplicationEnvironment env = new ApplicationEnvironment();
        //如果没有设置图片,使用默认图片
        SpringApplicationBannerPrinter printer = new SpringApplicationBannerPrinter(
                new DefaultResourceLoader(),
                new SpringBootBanner()
        );
        // 测试文字 banner
//        env.getPropertySources().addLast(new MapPropertySource("custom", Map.of("spring.banner.location","banner1.txt")));
        // 测试图片 banner
//        env.getPropertySources().addLast(new MapPropertySource("custom", Map.of("spring.banner.image.location","banner2.png")));
        // 版本号的获取
        System.out.println(SpringBootVersion.getVersion());
        printer.print(env, Step7.class, System.out);
    }
}

run 方法小结

源码

image-20241028222824116

image-20241028223605258

image-20241029195759237

image-20241029200637354

public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    ConfigurableApplicationContext context = null;
    this.configureHeadlessProperty();
    //1步骤:根据spring.factory读取里面的事件发布器
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    //springboot开始启动了
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
        //2步骤:分装main参数,分成一个 --符号的选项参数 和一个非选项参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //查看下方源码方法
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        //7步骤:打印banner信息
        Banner printedBanner = this.printBanner(environment);
        //8步骤:创建spring容器,根据在构造方法中推断出来的容器类型,在三种类型中,选择一种容器实现
        context = this.createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        //查看下方源码
        this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        //调用容器refresh方法,调用各种bean工厂后处理器,准备各种bean的后处理器。然后初始化每个单例
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
        }
		//发布一个started事件
        listeners.started(context, timeTakenToStartup);
        //查看下方源码
        this.callRunners(context, applicationArguments);
    } catch (Throwable var12) {
        this.handleRunFailure(context, var12, listeners);
        throw new IllegalStateException(var12);
    }

    try {
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        //发布一个ready事件,表明springboot项目启动完成
        listeners.ready(context, timeTakenToReady);
        return context;
    } catch (Throwable var11) {
        //如果出现异常,发布一个失败事件
        this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var11);
    }
}


    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        //3步骤:创建environment环境   如果非web环境,创建applicationContext对象
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        //把参数信息封装成properties源数据对象,添加到environment中(environment从参数中获取一些键值(只能是选项参数--符号)信息)
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        //4步骤:把参数中key中以_分隔或者驼峰分隔的,转成-分隔
        ConfigurationPropertySources.attach((Environment)environment);
        //5步骤:事件的发布与响应,为environmen对象添加源
        listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
        DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
        Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
        //6步骤:把environment中spring.main前缀和springapplication对象绑定
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = this.convertEnvironment((ConfigurableEnvironment)environment);
        }

        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }


    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        //9步骤:应用初始化器,对applicationContext做功能增强
        this.applyInitializers(context);
        //发布contextPrepared事件,容器创建好,并初始化容器后,发布这个事件
        listeners.contextPrepared(context);
        bootstrapContext.close(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
            ((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }

        context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
        //第10步骤,获取所有beanDefination源
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        //把获取的源加载到容器,加载各种beanDefination
        this.load(context, sources.toArray(new Object[0]));
        //发布contextLoaded事件
        listeners.contextLoaded(context);
    }


    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList();
        //12步骤,调用所有实现了ApplicationRunner.class或者是CommandLineRunner.class的bean
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        Iterator var4 = (new LinkedHashSet(runners)).iterator();

        while(var4.hasNext()) {
            Object runner = var4.next();
            if (runner instanceof ApplicationRunner) {
                this.callRunner((ApplicationRunner)runner, args);
            }

            if (runner instanceof CommandLineRunner) {
                this.callRunner((CommandLineRunner)runner, args);
            }
        }

    }

boot启动过程总结

阶段一:SpringApplication 构造

  1. 记录 BeanDefinition 源
  2. 推断应用类型
  3. 记录 ApplicationContext 初始化器
  4. 记录监听器
  5. 推断主启动类

阶段二:执行 run 方法

  1. 得到 SpringApplicationRunListeners,名字取得不好,实际是事件发布器

    • 发布 application starting 事件1️⃣
  2. 封装启动 args

  3. 准备 Environment 添加命令行参数(*)

  4. ConfigurationPropertySources 处理(*)

    • 发布 application environment 已准备事件2️⃣
  5. 通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理(*)

    • application.properties,由 StandardConfigDataLocationResolver 解析
    • spring.application.json
  6. 绑定 spring.main 到 SpringApplication 对象(*)

  7. 打印 banner(*)

  8. 创建容器

  9. 准备容器

    • 发布 application context 已初始化事件3️⃣
  10. 加载 bean 定义

    • 发布 application prepared 事件4️⃣
  11. refresh 容器

    • 发布 application started 事件5️⃣
  12. 执行 runner

    • 发布 application ready 事件6️⃣

    • 这其中有异常,发布 application failed 事件7️⃣

带 * 的有独立的示例

演示 - 启动过程

a39.A39_1 对应 SpringApplication 构造

a39.A39_2 对应第1步,并演示 7 个事件

a39.A39_3 对应第2、8到12步

org.springframework.boot.Step3

org.springframework.boot.Step4

org.springframework.boot.Step5

org.springframework.boot.Step6

org.springframework.boot.Step7

收获💡

  1. SpringApplication 构造方法中所做的操作
    • 可以有多种源用来加载 bean 定义
    • 应用类型推断
    • 添加容器初始化器
    • 添加监听器
    • 演示主类推断
  2. 如何读取 spring.factories 中的配置
  3. 从配置中获取重要的事件发布器:SpringApplicationRunListeners
  4. 容器的创建、初始化器增强、加载 bean 定义等
  5. CommandLineRunner、ApplicationRunner 的作用
  6. 环境对象
    1. 命令行 PropertySource
    2. ConfigurationPropertySources 规范环境键名称
    3. EnvironmentPostProcessor 后处理增强
      • 由 EventPublishingRunListener 通过监听事件2️⃣来调用
    4. 绑定 spring.main 前缀的 key value 至 SpringApplication
  7. Banner

1.4 Tomcat 内嵌容器

Tomcat 基本结构

Server
└───Service
    ├───Connector (协议, 端口)
    └───Engine
        └───Host(虚拟主机 localhost)
            ├───Context1 (应用1, 可以设置虚拟路径, / 即 url 起始路径; 项目磁盘路径, 即 docBase )
            │   │   index.html
            │   └───WEB-INF
            │       │   web.xml (servlet, filter, listener) 3.0
            │       ├───classes (servlet, controller, service ...)
            │       ├───jsp
            │       └───lib (第三方 jar 包)
            └───Context2 (应用2)
                │   index.html
                └───WEB-INF
                        web.xml

演示1 - Tomcat 内嵌容器

HelloServlet

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write("""
                <h1>hello servlet</h1>
                """);
    }
}

image-20241029215204174

TestTomcat

public class TestTomcat {
    public static void main(String[] args) throws IOException, LifecycleException {
        // 1.创建 Tomcat 对象
        Tomcat tomcat = new Tomcat();
        //设置tomcat基础目录,tomcat生成的临时文件以及结构目录会放到基础目录中
        tomcat.setBaseDir("tomcat");//相对路径

        // 2.创建项目文件夹,即 docBase 文件夹(给下面创建的context使用)
        File docBase = Files.createTempDirectory("boot.").toFile();
        docBase.deleteOnExit();//临时目录,退出后自动删除

        // 3.创建 Tomcat 项目, 在 Tomcat 中称为 Context
        Context context = tomcat.addContext("", docBase.getAbsolutePath());//第一个参数是虚拟路径( 用空白即代表/ ),第二个参数是docBase

        // 4.编程添加 Servlet  这个方法是第5步,tomcat启动后,回调这个方法
        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
                ctx.addServlet("hello", new HelloServlet()).addMapping("/hello");
            }
        }, Collections.emptySet());

        // 5.启动 Tomcat
        tomcat.start();

        // 6.创建连接器,设置监听端口
        Connector connector = new Connector(new Http11Nio2Protocol());//指定连接器协议
        connector.setPort(8080);
        tomcat.setConnector(connector);

    }
}

image-20241029215243089

演示2 - 集成 Spring 容器

image-20241029222305993

public static WebApplicationContext getApplicationContext(){
    //AnnotationConfigServletWebServerApplicationContext: 内嵌tomcat
    //AnnotationConfigWebApplicationContext: 不包含tomcat
    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.register(Config.class);
    context.refresh();
    return context;

}

@Configuration
static class Config {
    @Bean
    public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    }

    @Bean
    // 这个例子中必须为 DispatcherServlet 提供 AnnotationConfigWebApplicationContext, 否则会选择 XmlWebApplicationContext 实现
    public DispatcherServlet dispatcherServlet(WebApplicationContext applicationContext) {
        return new DispatcherServlet(applicationContext);
    }

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
        //默认的HandlerAdapter没有json的消息转换器MappingJackson2HttpMessageConverter
        handlerAdapter.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));
        return handlerAdapter;
    }

    @RestController
    static class MyController {
        @GetMapping("hello2")
        public Map<String,Object> hello() {
            return Map.of("hello2", "hello2, spring!");
        }
    }
}

image-20241029222432001

使用注册bean方式

image-20241029223952305

image-20241029224041018

// 4.编程添加 Servlet  这个方法是第5步,tomcat启动后,回调这个方法
context.addServletContainerInitializer(new ServletContainerInitializer() {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        ctx.addServlet("hello", new HelloServlet()).addMapping("/hello");

        /*DispatcherServlet dispatcherServlet = springContext.getBean(DispatcherServlet.class);
        //把dispatcherServlet注册到tomcat中
        ctx.addServlet("dispatcherServlet", dispatcherServlet).addMapping("/");*/

        //后续会有许多的servlet,所以使用注册bean方式
        // k: bean的名称  v: 注册bean
        Map<String, ServletRegistrationBean> beansOfType = springContext.getBeansOfType(ServletRegistrationBean.class);
        for (ServletRegistrationBean registrationBean : beansOfType.values()) {
            //内部注册对应的servlet,并添加映射路径
            registrationBean.onStartup(ctx);
        }
    }
}, Collections.emptySet());

image-20241029224244191

image-20241029224845609

1.5 Boot 自动配置

自动配置原理

image-20241030203510149

image-20241030203323315

public class A41 {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        //解析 @Configuration @Import 注解
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Configuration //本项目的配置类
    //本项目配置类导入其他项目配置类
    @Import({AutoConfiguration1.class,AutoConfiguration2.class})
    static class Config {

    }

    @Configuration // 第三方的配置类
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1("第三方");
        }
    }

    static class Bean1 {
        private String name;

        public Bean1() {
        }

        public Bean1(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Bean1{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    @Configuration // 第三方的配置类
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean2 {

    }

}

image-20241030203557878

image-20241030205325260

结果

image-20241030205412099

\ 代表换行,方便查看

除了会读当前的项目中的spring.factories,也会读取其他依赖的spring.factories

image-20241030210730431

image-20241030210827805

@Configuration //本项目的配置类
//本项目配置类导入其他项目配置类
@Import(MyImportSelector.class)
static class Config {

}

static class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //方法返回值就是将来导入的配置类类名
        //return new String[]{AutoConfiguration1.class.getName(),AutoConfiguration2.class.getName()};

        //第一个参数MyImportSelector.class: 作为key值,根据key值获取对应的实现类
        List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
        return names.toArray(new String[0]);
    }
}

image-20241030210959049

如果本项目中的bean定义和第三方中的bean定义冲突,本项目的bean生效(但是第三方的优先级高,先加载)

image-20241030214306060

原因

1、解析时机不同

先通过@Import解析第三方的配置类,然后在解析Config中的配置类

2、覆盖

bean工厂默认后注册的bean会覆盖之前的bean

image-20241030220606861

修改加载时机,优先加载本类

image-20241030224741131

AopAutoConfiguration

image-20241101211246984

image-20241101211301887

TestAopAuto

public class TestAopAuto {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        StandardEnvironment env = new StandardEnvironment();
//        env.getPropertySources().addLast(new SimpleCommandLinePropertySource("--spring.aop.auto=false"));
        context.setEnvironment(env);
        // 注册常用的bean
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
        context.registerBean(Config.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(">>>>>>>>>>>>>>>");

        AnnotationAwareAspectJAutoProxyCreator creator = context.getBean("org.springframework.aop.config.internalAutoProxyCreator",
                AnnotationAwareAspectJAutoProxyCreator.class);
        System.out.println(creator.isProxyTargetClass());
    }

    @Configuration
    @Import(MyImportSelector.class)
    static class Config {

    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{AopAutoConfiguration.class.getName()};
        }
    }
}

image-20241101211554941

image-20241101211654186

收获

Spring Boot 是利用了自动配置类来简化了 aop 相关配置

  • AOP 自动配置类为 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
  • 可以通过 spring.aop.auto=false 禁用 aop 自动配置
  • AOP 自动配置的本质是通过 @EnableAspectJAutoProxy 来开启了自动代理,如果在引导类上自己添加了 @EnableAspectJAutoProxy 那么以自己添加的为准
  • @EnableAspectJAutoProxy 的本质是向容器中添加了 AnnotationAwareAspectJAutoProxyCreator 这个 bean 后处理器,它能够找到容器中所有切面,并为匹配切点的目标类创建代理,创建代理的工作一般是在 bean 的初始化阶段完成的

DataSourceAutoConfiguration

image-20241102174634994

image-20241102174648318

image-20241102174737537

public class TestDataSourceAuto {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        StandardEnvironment env = new StandardEnvironment();
        //下方导入的DataSourceAutoConfiguration需要这些信息
        env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
                "--spring.datasource.url=jdbc:mysql://localhost:3306/test",
                "--spring.datasource.username=root",
                "--spring.datasource.password=root"
        ));

        String packageName = TestDataSourceAuto.class.getPackageName();
        System.out.println("当前包名:" + packageName);
        AutoConfigurationPackages.register(context.getDefaultListableBeanFactory(),
                packageName);

        context.setEnvironment(env);
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
        context.registerBean(Config.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
            if (resourceDescription != null) {
                System.out.println(name + " 来源: " + resourceDescription);
            }
        }

        DataSourceProperties dataSourceProperties = context.getBean(DataSourceProperties.class);
        System.out.println(dataSourceProperties.getUrl());
        System.out.println(dataSourceProperties.getUsername());
        System.out.println(dataSourceProperties.getPassword());

    }

    @Configuration
    @Import(MyImportSelector.class)
    static class Config {

    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{
                    DataSourceAutoConfiguration.class.getName(),
                    MybatisAutoConfiguration.class.getName(),
                    DataSourceTransactionManagerAutoConfiguration.class.getName(),
                    TransactionAutoConfiguration.class.getName()
            };
        }
    }
}

收获

  • 对应的自动配置类为:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  • 它内部采用了条件装配,通过检查容器的 bean,以及类路径下的 class,来决定该 @Bean 是否生效

简单说明一下,Spring Boot 支持两大类数据源:

  • EmbeddedDatabase - 内嵌数据库连接池
  • PooledDataSource - 非内嵌数据库连接池

PooledDataSource 又支持如下数据源

  • hikari 提供的 HikariDataSource
  • tomcat-jdbc 提供的 DataSource
  • dbcp2 提供的 BasicDataSource
  • oracle 提供的 PoolDataSourceImpl

如果知道数据源的实现类类型,即指定了 spring.datasource.type,理论上可以支持所有数据源,但这样做的一个最大问题是无法订制每种数据源的详细配置(如最大、最小连接数等)

MybatisAutoConfiguration

image-20241102175048484

image-20241102175120868

@Configuration
//后面的这些类型,必须在类路径下能够找到(和mybatis整合了,mybatis的jar下有)
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
//容器中有且仅有一个DataSource时,条件成立(单一候选者)
@ConditionalOnSingleCandidate(DataSource.class)
//将来创建一个MybatisProperties属性对象,用于环境中的键值对象绑定(见下图)
@EnableConfigurationProperties({MybatisProperties.class})
//控制多个配置类的解析顺序的
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
    private final MybatisProperties properties;
    private final Interceptor[] interceptors;
    private final TypeHandler[] typeHandlers;
    private final LanguageDriver[] languageDrivers;
    private final ResourceLoader resourceLoader;
    private final DatabaseIdProvider databaseIdProvider;
    private final List<ConfigurationCustomizer> configurationCustomizers;

image-20241102175726395

image-20241102180455400

image-20241102180249097

sqlSessionTemplate使用位置(spring和mybatis整合时,需要用到它)

image-20241102180752284

image-20241102180837187

mapper扫描

image-20241102181548087

@Configuration
//注入下方的AutoConfiguredMapperScannerRegistrar
@Import({AutoConfiguredMapperScannerRegistrar.class})
//必须缺失bean成立
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
    public MapperScannerRegistrarNotFoundConfiguration() {
    }

    public void afterPropertiesSet() {
        MybatisAutoConfiguration.logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
    }
}

//作用:实现了ImportBeanDefinitionRegistrar:用编程的方式补充bean的definenation,这里补充的是mapper的definenation
//根据mapper接口类型,把每个mapper封装成MapperFactoryBean,作为bean definenation加到bean工厂
public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
    private BeanFactory beanFactory;

    public AutoConfiguredMapperScannerRegistrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        }
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
}

image-20241102183016330

image-20241102183132485

String packageName = TestDataSourceAuto.class.getPackageName();
System.out.println("当前包名:" + packageName);
//AutoConfigurationPackages:把springboot引导类的包名记录下来,
// 后面执行到MybatisAutoConfiguration中的AutoConfiguredMapperScannerRegistrar方法时,就可以确定mapper的扫描范围
AutoConfigurationPackages.register(context.getDefaultListableBeanFactory(),
        packageName);

收获

  • MyBatis 自动配置类为 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
  • 它主要配置了两个 bean
    • SqlSessionFactory - MyBatis 核心对象,用来创建 SqlSession
    • SqlSessionTemplate - SqlSession 的实现,此实现会与当前线程绑定
    • 用 ImportBeanDefinitionRegistrar 的方式扫描所有标注了 @Mapper 注解的接口
    • 用 AutoConfigurationPackages 来确定扫描的包
  • 还有一个相关的 bean:MybatisProperties,它会读取配置文件中带 mybatis. 前缀的配置项进行定制配置

@MapperScan 注解的作用与 MybatisAutoConfiguration 类似,会注册 MapperScannerConfigurer 有如下区别

  • @MapperScan 扫描具体包(当然也可以配置关注哪个注解)
  • @MapperScan 如果不指定扫描具体包,则会把引导类范围内,所有接口当做 Mapper 接口
  • MybatisAutoConfiguration 关注的是所有标注 @Mapper 注解的接口,会忽略掉非 @Mapper 标注的接口

这里有同学有疑问,之前介绍的都是将具体类交给 Spring 管理,怎么到了 MyBatis 这儿,接口就可以被管理呢?

  • 其实并非将接口交给 Spring 管理,而是每个接口会对应一个 MapperFactoryBean,是后者被 Spring 所管理,接口只是作为 MapperFactoryBean 的一个属性来配置

TransactionAutoConfiguration

image-20241102220751025

image-20241102220824197

image-20241102220949331

收获

  • 事务自动配置类有两个:
    • org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
    • org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
  • 前者配置了 DataSourceTransactionManager 用来执行事务的提交、回滚操作
  • 后者功能上对标 @EnableTransactionManagement,包含以下三个 bean
    • BeanFactoryTransactionAttributeSourceAdvisor 事务切面类,包含通知和切点
    • TransactionInterceptor 事务通知类,由它在目标方法调用前后加入事务操作
    • AnnotationTransactionAttributeSource 会解析 @Transactional 及事务属性,也包含了切点功能
  • 如果自己配置了 DataSourceTransactionManager 或是在引导类加了 @EnableTransactionManagement,则以自己配置的为准

mvc配置

image-20241102221644421

image-20241102221702793

image-20241102221717191

TestMvcAuto

public class TestMvcAuto {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
        context.registerBean(Config.class);
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            String source = context.getBeanDefinition(name).getResourceDescription();
            if (source != null) {
                System.out.println(name + " 来源:" + source);
            }
        }
        context.close();

    }

    @Configuration
    @Import(MyImportSelector.class)
    static class Config {

    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{
                    //配置内嵌tomcat服务工厂
                    ServletWebServerFactoryAutoConfiguration.class.getName(),
                    //配置DispatcherServlet以及DispatcherServlet注册bean
                    DispatcherServletAutoConfiguration.class.getName(),
                    //DispatcherServlet运行时需要个各种组件,例如处理器,映射器,适配器
                    WebMvcAutoConfiguration.class.getName(),
                    //配置错误处理
                    ErrorMvcAutoConfiguration.class.getName()
            };
        }
    }
}

ServletWebServerFactoryAutoConfiguration

  • 提供 ServletWebServerFactory

DispatcherServletAutoConfiguration

  • 提供 DispatcherServlet
  • 提供 DispatcherServletRegistrationBean

WebMvcAutoConfiguration

  • 配置 DispatcherServlet 的各项组件,提供的 bean 见过的有
    • 多项 HandlerMapping
    • 多项 HandlerAdapter
    • HandlerExceptionResolver

ErrorMvcAutoConfiguration

  • 提供的 bean 有 BasicErrorController

MultipartAutoConfiguration

  • 它提供了 org.springframework.web.multipart.support.StandardServletMultipartResolver
  • 该 bean 用来解析 multipart/form-data 格式的数据

HttpEncodingAutoConfiguration

  • POST 请求参数如果有中文,无需特殊设置,这是因为 Spring Boot 已经配置了 org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter
  • 对应配置 server.servlet.encoding.charset=UTF-8,默认就是 UTF-8
  • 当然,它只影响非 json 格式的数据

自定义自动配置类

image-20241102233202117

image-20241102233242713

image-20241102233441632

image-20241102233503063

image-20241102233631144

image-20241102233744535

public class A41_2 {

    @SuppressWarnings("all")
    public static void main(String[] args) throws IOException {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
        StandardEnvironment env = new StandardEnvironment();
        env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
                "--spring.datasource.url=jdbc:mysql://localhost:3306/test",
                "--spring.datasource.username=root",
                "--spring.datasource.password=root"
        ));
        context.setEnvironment(env);
        context.registerBean("config", Config.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
            if (resourceDescription != null)
                System.out.println(name + " 来源:" + resourceDescription);
        }
        context.close();
    }

    @Configuration // 本项目的配置类
//    @Import(MyImportSelector.class)
    @EnableAutoConfiguration
    static class Config {
        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }
    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null).toArray(new String[0]);
        }
    }

    @Configuration // 第三方的配置类
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    }

    @Configuration // 第三方的配置类
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {

    }
    static class Bean2 {

    }
}

image-20241102234013095

image-20241102233928485

关键代码

假设已有第三方的两个自动配置类

@Configuration // ⬅️第三方的配置类
static class AutoConfiguration1 {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }
}

@Configuration // ⬅️第三方的配置类
static class AutoConfiguration2 {
    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }
}

提供一个配置文件 META-INF/spring.factories,key 为导入器类名,值为多个自动配置类名,用逗号分隔

MyImportSelector=\
AutoConfiguration1,\
AutoConfiguration2

注意

  • 上述配置文件中 MyImportSelector 与 AutoConfiguration1,AutoConfiguration2 为简洁均省略了包名,自己测试时请将包名根据情况补全

引入自动配置

@Configuration // ⬅️本项目的配置类
@Import(MyImportSelector.class)
static class Config { }

static class MyImportSelector implements DeferredImportSelector {
    // ⬇️该方法从 META-INF/spring.factories 读取自动配置类名,返回的 String[] 即为要导入的配置类
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return SpringFactoriesLoader
            .loadFactoryNames(MyImportSelector.class, null).toArray(new String[0]);
    }
}

收获💡

  1. 自动配置类本质上就是一个配置类而已,只是用 META-INF/spring.factories 管理,与应用配置类解耦
  2. @Enable 打头的注解本质是利用了 @Import
  3. @Import 配合 DeferredImportSelector 即可实现导入,selectImports 方法的返回值即为要导入的配置类名
  4. DeferredImportSelector 的导入会在最后执行,为的是让其它配置优先解析

1.6 条件装配底层

image-20241103102935375

image-20241103102954937

image-20241103103017916

image-20241103103048065

public class A42_1 {

    @SuppressWarnings("all")
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Configuration // 本项目的配置类
    @Import(MyImportSelector.class)
    static class Config {
    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()};
        }
    }

    static class MyCondition1 implements Condition { //存在druid依赖
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            //存在druid依赖
            return ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
        }
    }

    static class MyCondition2 implements Condition { //不存在druid依赖
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            //是否存在druid依赖
            return !ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
        }
    }

    @Configuration // 第三方的配置类
    @Conditional(MyCondition1.class) //本身不具有if-else判断功能,需要把判断逻辑写在实现了condition接口的类中(只作为一个标记符)
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    }

    @Configuration // 第三方的配置类
    @Conditional(MyCondition2.class)
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {

    }

    static class Bean2 {

    }
}

自定义条件注解

image-20241103104848210

image-20241103104919808

image-20241103104940920

public class A42_1 {

    @SuppressWarnings("all")
    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Configuration // 本项目的配置类
    @Import(MyImportSelector.class)
    static class Config {
    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()};
        }
    }

    static class MyCondition1 implements Condition { //存在druid依赖
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            //获得ConditionalOnClass注解的所有属性
            Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());
            //获得属性值
            boolean exists = (boolean) attributes.get("exists");
            String className = attributes.get("className").toString();
            //判断类是否存在
            boolean present = ClassUtils.isPresent(className, null);
            return exists ? present : !present;
        }
    }

    //自定义条件注解
    @Retention(RetentionPolicy.RUNTIME)
    //TYPE:可以加到类上  METHOD:可以加到方法上
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Conditional(MyCondition1.class)
    @interface ConditionalOnClass {
        boolean exists(); //true:存在  false:不存在
        String className(); //要判断的类名
    }

    @Configuration // 第三方的配置类
    @ConditionalOnClass(exists = true, className = "com.alibaba.druid.pool.DruidDataSource")
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    }

    @Configuration // 第三方的配置类
    @ConditionalOnClass(exists = false, className = "com.alibaba.druid.pool.DruidDataSource")
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {

    }

    static class Bean2 {

    }
}

条件装配的底层是本质上是 @Conditional 与 Condition,这两个注解。引入自动配置类时,期望满足一定条件才能被 Spring 管理,不满足则不管理,怎么做呢?

比如条件是【类路径下必须有 dataSource】这个 bean ,怎么做呢?

首先编写条件判断类,它实现 Condition 接口,编写条件判断逻辑

static class MyCondition1 implements Condition { 
    // ⬇️如果存在 Druid 依赖,条件成立
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
    }
}

其次,在要导入的自动配置类上添加 @Conditional(MyCondition1.class),将来此类被导入时就会做条件检查

@Configuration // 第三方的配置类
@Conditional(MyCondition1.class) // ⬅️加入条件
static class AutoConfiguration1 {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }
}

分别测试加入和去除 druid 依赖,观察 bean1 是否存在于容器

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.17</version>
</dependency>

收获💡

  1. 学习一种特殊的 if - else

2 其它

2.1 FactoryBean

image-20241103114134748

image-20241103114229672

image-20241103114240984

image-20241103114308120

image-20241103114421011

@ComponentScan
public class A43 {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A43.class);

        Bean1 bean1 = (Bean1) context.getBean("bean1");
        Bean1 bean2 = (Bean1) context.getBean("bean1");
        Bean1 bean3 = (Bean1) context.getBean("bean1");
        System.out.println(bean1);
        System.out.println(bean2);
        System.out.println(bean3);

        //虽然工厂的名字是bean1,但是context.getBean("bean1")得到的产品.必须通过下面的方式获取工厂
        System.out.println(context.getBean(Bean1FactoryBean.class));
        System.out.println(context.getBean("&bean1"));

        context.close();

        /*
            学到了什么: 一个在 Spring 发展阶段中重要, 但目前已经很鸡肋的接口 FactoryBean 的使用要点
            说它鸡肋有两点:
                1. 它的作用是用制造创建过程较为复杂的产品, 如 SqlSessionFactory, 但 @Bean 已具备等价功能
                2. 使用上较为古怪, 一不留神就会用错
                    a. 被 FactoryBean 创建的产品
                        - 会认为创建、依赖注入、Aware 接口回调、前初始化这些都是 FactoryBean 的职责, 这些流程都不会走
                        - 唯有后初始化的流程会走, 也就是产品可以被代理增强
                        - 单例的产品不会存储于 BeanFactory 的 singletonObjects 成员中, 而是另一个 factoryBeanObjectCache 成员中
                    b. 按名字去获取时, 拿到的是产品对象, 名字前面加 & 获取的是工厂对象
            就说恶心不?

            但目前此接口的实现仍被大量使用, 想被全面废弃很难
         */
    }

}

收获💡

  1. 它的作用是用制造创建过程较为复杂的产品, 如 SqlSessionFactory, 但 @Bean 已具备等价功能
  2. 使用上较为古怪, 一不留神就会用错
    1. 被 FactoryBean 创建的产品
      • 会认为创建、依赖注入、Aware 接口回调、前初始化这些都是 FactoryBean 的职责, 这些流程都不会走
      • 唯有后初始化的流程会走, 也就是产品可以被代理增强
      • 单例的产品不会存储于 BeanFactory 的 singletonObjects 成员中, 而是另一个 factoryBeanObjectCache 成员中
    2. 按名字去获取时, 拿到的是产品对象, 名字前面加 & 获取的是工厂对象

2.2 @Indexed 原理

@Component
public class Bean1 {
}

@Component
public class Bean2 {
}

@Component
public class Bean3 {
}

image-20241103115556001

image-20241103115724022

image-20241103115748444

image-20241103115833951

/*
    做这个试验前, 先在 target/classes 创建 META-INF/spring.components, 内容为

    com.itheima.a44.Bean1=org.springframework.stereotype.Component
    com.itheima.a44.Bean2=org.springframework.stereotype.Component

    做完实现建议删除, 避免影响其它组件扫描的结果

    真实项目中, 这个步骤可以自动完成, 加入以下依赖
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <optional>true</optional>
    </dependency>
 */
public class A44 {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 组件扫描的核心类
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);

        scanner.scan(A44.class.getPackageName());

        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        /*
            学到了什么
                a. @Indexed 的原理, 在编译时就根据 @Indexed 生成 META-INF/spring.components 文件
                扫描时
                1. 如果发现 META-INF/spring.components 存在, 以它为准加载 bean definition
                2. 否则, 会遍历包下所有 class 资源 (包括 jar 内的)
         */
    }
}

示例代码

真实项目中,只需要加入以下依赖即可

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-indexer</artifactId>
    <optional>true</optional>
</dependency>

收获💡

  1. 在编译时就根据 @Indexed 生成 META-INF/spring.components 文件
  2. 扫描时
    • 如果发现 META-INF/spring.components 存在, 以它为准加载 bean definition
    • 否则, 会遍历包下所有 class 资源 (包括 jar 内的)
  3. 解决的问题,在编译期就找到 @Component 组件,节省运行期间扫描 @Component 的时间

2.3 代理进一步理解

image-20241103145120213

image-20241103145021910

image-20241103145047455

image-20241103151211975

@SpringBootApplication
public class A45 {
    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(A45.class, args);

        Bean1 proxy = context.getBean(Bean1.class);
        proxy.setBean2(new Bean2());
        proxy.init();

        /*
            1.演示 spring 代理的设计特点
                依赖注入和初始化影响的是原始对象
                代理与目标是两个对象,二者成员变量并不共用数据
         */

        /*
            2.演示 static 方法、final 方法、private 方法均无法增强
         */
        showProxyAndTarget(proxy);

        System.out.println("------------------------");
        System.out.println(proxy.getBean2());
        System.out.println(proxy.isInitialized());
        context.close();
    }


    public static void showProxyAndTarget(Bean1 proxy) throws Exception {
        System.out.println(">>>>> 代理中的成员变量");
        System.out.println("\tinitialized=" + proxy.initialized);
        System.out.println("\tbean2=" + proxy.bean2);

        if (proxy instanceof Advised advised) {
            System.out.println(">>>>> 目标中的成员变量");
            Bean1 target = (Bean1) advised.getTargetSource().getTarget();
            System.out.println("\tinitialized=" + target.initialized);
            System.out.println("\tbean2=" + target.bean2);
        }
    }

}

image-20241103150737921

image-20241103151021312

image-20241103151844434

image-20241103151905791

只有成员方法进行了增强

image-20241103151543150

收获💡

  1. spring 代理的设计特点

    • 依赖注入和初始化影响的是原始对象

      • 因此 cglib 不能用 MethodProxy.invokeSuper()
    • 代理与目标是两个对象,二者成员变量并不共用数据

  2. static 方法、final 方法、private 方法均无法增强

    • 进一步理解代理增强基于方法重写

2.4 @Value 装配底层

解析${}

image-20241103154324495

image-20241103154347294

解析值类型转换

image-20241103155449401

image-20241103155521968

解析注入bean

image-20241103160743196

image-20241103160848619

image-20241103160912087

底层默认解析组合数据

image-20241103161434526

image-20241103161526641

image-20241103161601051

按类型装配的步骤

  1. 查看需要的类型是否为 Optional,是,则进行封装(非延迟),否则向下走
  2. 查看需要的类型是否为 ObjectFactory 或 ObjectProvider,是,则进行封装(延迟),否则向下走
  3. 查看需要的类型(成员或参数)上是否用 @Lazy 修饰,是,则返回代理,否则向下走
  4. 解析 @Value 的值
    1. 如果需要的值是字符串,先解析 ${ },再解析 #
    2. 不是字符串,需要用 TypeConverter 转换
  5. 看需要的类型是否为 Stream、Array、Collection、Map,是,则按集合处理,否则向下走
  6. 在 BeanFactory 的 resolvableDependencies 中找有没有类型合适的对象注入,没有向下走
  7. 在 BeanFactory 及父工厂中找类型匹配的 bean 进行筛选,筛选时会考虑 @Qualifier 及泛型
  8. 结果个数为 0 抛出 NoSuchBeanDefinitionException 异常
  9. 如果结果 > 1,再根据 @Primary 进行筛选
  10. 如果结果仍 > 1,再根据成员名或变量名进行筛选
  11. 结果仍 > 1,抛出 NoUniqueBeanDefinitionException 异常

收获💡

  1. ContextAnnotationAutowireCandidateResolver 作用之一,获取 @Value 的值
  2. 了解 ${ } 对应的解析器
  3. 了解 #{ } 对应的解析器
  4. TypeConvert 的一项体现

2.5 @Autowired 装配底层

演示 - @Autowired 装配过程

根据成员变量的类型注入

image-20241103163659924

image-20241103163914261

根据参数的类型注入

image-20241103164310039

image-20241103164352766

结果包装为 Optional

image-20241103170000429

image-20241103170212423

结果包装为 ObjectProvider,ObjectFactory

image-20241103171049434

image-20241103171131440

对 @Lazy 的处理

image-20241103172001086

image-20241103172057687

@Configuration
public class A47_1 {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A47_1.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        // 1.根据成员变量的类型注入
        DependencyDescriptor dd1 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);
        System.out.println(beanFactory.doResolveDependency(dd1, "bean1", null, null));
        System.out.println("------------------------");
        // 2.根据参数的类型注入
        Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
        DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), false);
        System.out.println(beanFactory.doResolveDependency(dd2, "bean1", null, null));
        System.out.println("------------------------");
        // 3.结果包装为 Optional<Bean2>
        DependencyDescriptor dd3 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean3"), false);
        System.out.println(dd3.getDependencyType());//打印java.util.Optional
        if (dd3.getDependencyType() == Optional.class){
            dd3.increaseNestingLevel();//增加一层内嵌,找到内层的bean2
            System.out.println(dd3.getDependencyType());//打印com.feng.a47autowired.A47_1$Bean2
            Object bean2 = beanFactory.doResolveDependency(dd3, "bean1", null, null);
            //需要封装成optional
            System.out.println(Optional.ofNullable(bean2));
        }
        System.out.println("------------------------");

        // 4.结果包装为 ObjectProvider,ObjectFactory
        DependencyDescriptor dd4 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean4"), false);
        if (dd4.getDependencyType() == ObjectFactory.class){
            dd4.increaseNestingLevel();//增加一层内嵌,找到内层的bean2
            ObjectFactory objectFactory = new ObjectFactory() {
                @Override
                public Object getObject(){
                    //调用ObjectFactory的getObject方法时, 才去获取bean2
                    Object bean2 = beanFactory.doResolveDependency(dd4, "bean1", null, null);
                    return bean2;
                }
            };
            System.out.println(objectFactory.getObject());
        }
        System.out.println("------------------------");

        // 5. 对 @Lazy 的处理
        DependencyDescriptor dd5 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);

        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);
        //判断dd5上是否有@Lazy注解,如果有,则创建代理,反之返回真实对象
        Object proxy = resolver.getLazyResolutionProxyIfNecessary(dd5, "bean1");
        System.out.println(proxy);
        System.out.println(proxy.getClass());
        /*
            学到了什么
                1. Optional 及 ObjectFactory 对于内嵌类型的处理, 源码参考 ResolvableType
                2. ObjectFactory 懒惰的思想
                3. @Lazy 懒惰的思想
            下一节, 继续学习 doResolveDependency 内部处理
         */
    }

    static class Bean1 {
        @Autowired @Lazy
        private Bean2 bean2;

        @Autowired
        public void setBean2( Bean2 bean2) {
            this.bean2 = bean2;
        }
        @Autowired
        private Optional<Bean2> bean3;

        @Autowired
        private ObjectFactory<Bean2> bean4;
    }

    @Component("bean2")
    static class Bean2 {
        /*@Override
        public String toString() {
            return super.toString();
        }*/
    }
}

对数组的处理

image-20241103174449276

image-20241103174619063

image-20241103174757592

对集合的处理

image-20241103175430402

image-20241103175525265

处理ApplicationContext 等特殊类型

image-20241103182058207

image-20241103182123259

处理类型有泛型参数

image-20241103182848542

image-20241103182933124

image-20241103182956367

处理类型有 @Qualifier

image-20241103183340214

image-20241103183404167

@Configuration
public class A47_2 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A47_2.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 1. 数组类型");
        testArray(beanFactory);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 2. List 类型");
        testList(beanFactory);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 3. applicationContext");
        testApplicationContext(beanFactory);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 4. 泛型");
        testGeneric(beanFactory);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 5. @Qualifier");
        testQualifier(beanFactory);
        /*
            学到了什么
                1. 如何获取数组元素类型
                2. Spring 如何获取泛型中的类型
                3. 特殊对象的处理, 如 ApplicationContext, 并注意 Map 取值时的类型匹配问题 (另见  TestMap)
                4. 谁来进行泛型匹配 (另见 TestGeneric)
                5. 谁来处理 @Qualifier
                6. 刚开始都只是按名字处理, 等候选者确定了, 才会创建实例
         */
    }

    private static void testQualifier(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd5 = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);
        Class<?> type = dd5.getDependencyType();
        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);
        for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
            BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
            //                                                             @Qualifier("service2")
            if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd,name), dd5)) {
                System.out.println(name);
                System.out.println(dd5.resolveCandidate(name, type, beanFactory));//拿到最后的bean
            }
        }
    }
    private static void testGeneric(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd4 = new DependencyDescriptor(Target.class.getDeclaredField("dao"), true);
        Class<?> type = dd4.getDependencyType();
        //之前用过的@Value,以及@Lazy是否创建代理,这里进行泛型的对比
        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);

        for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
            //获取类型信息以及泛型信息
            BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
            // 对比 BeanDefinition 与 DependencyDescriptor 的泛型是否匹配
            if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd,name), dd4)) {
                System.out.println(name);
                System.out.println(dd4.resolveCandidate(name, type, beanFactory));//拿到最终的bean
            }
        }
    }
    private static void testApplicationContext(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException, IllegalAccessException {
        DependencyDescriptor dd3 = new DependencyDescriptor(Target.class.getDeclaredField("applicationContext"), true);
        //利用反射获取
        Field resolvableDependencies = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
        resolvableDependencies.setAccessible(true);
        Map<Class<?>, Object> dependencies = (Map<Class<?>, Object>) resolvableDependencies.get(beanFactory);
        /*dependencies.forEach((k, v) -> {
            System.out.println("key:" + k + " value: " + v);
        });*/
        for (Map.Entry<Class<?>, Object> entry : dependencies.entrySet()) {
            // 左边类型                      右边类型
            if (entry.getKey().isAssignableFrom(dd3.getDependencyType())) {//右边类型是否可以赋值给左边的类型
                System.out.println(entry.getValue());
                break;
            }
        }
    }
    private static void testList(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd2 = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), true);
        if (dd2.getDependencyType() == List.class) {
            //getResolvableType: 获得解析后的类型信息 getGeneric(): 获得泛型类型,不传索引下标,默认找第一个泛型参数类型
            Class<?> resolve = dd2.getResolvableType().getGeneric().resolve();
            System.out.println(resolve);//打印Service类型

            List<Object> list = new ArrayList<>();
            String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, resolve);
            for (String name : names) {
                //底层调用的是 return beanFactory.getBean(beanName) 根据beanName获得bean对象
                Object bean = dd2.resolveCandidate(name, resolve, beanFactory);
                list.add(bean);
            }
            System.out.println(list);
        }
    }
    private static void testArray(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("serviceArray"), true);
        if (dd1.getDependencyType().isArray()) {
            //获得数组元素类型
            Class<?> componentType = dd1.getDependencyType().getComponentType();
            System.out.println(componentType);
            //从当前容器以及父容器,祖先容器中根据type查找beanName
            String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, componentType);
            List<Object> beans = new ArrayList<>();
            for (String name : names) {
                System.out.println(name); //打印 service3 service2 service1
                //底层调用的是 return beanFactory.getBean(beanName)
                Object bean = dd1.resolveCandidate(name, componentType, beanFactory);
                beans.add(bean);
            }
            //调用spring默认的转换器,将集合转换成数组
            Object array = beanFactory.getTypeConverter().convertIfNecessary(beans, dd1.getDependencyType());
            System.out.println(array);
        }
    }
    static class Target {
        @Autowired private Service[] serviceArray;
        @Autowired private List<Service> serviceList;
        @Autowired private ConfigurableApplicationContext applicationContext;
        @Autowired private Dao<Teacher> dao;
        @Autowired @Qualifier("service2") private Service service;
    }

    interface Service {

    }

    @Component("service1")
    static class Service1 implements Service {

    }

    @Component("service2")
    static class Service2 implements Service {

    }

    @Component("service3")
    static class Service3 implements Service {

    }

    interface Dao<T> {

    }
    @Component("dao1") static class Dao1 implements Dao<Student> {
    }
    @Component("dao2") static class Dao2 implements Dao<Teacher> {
    }

    static class Student {

    }

    static class Teacher {

    }


}

@Primary用法

image-20241103184014564

image-20241103184058353

image-20241103184117042

处理默认情况

image-20241103184443483

image-20241103184512504

@Configuration
public class A47_3 {
    public static void main(String[] args) throws NoSuchFieldException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A47_3.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        //testPrimary(beanFactory);
        testDefault(beanFactory);

        /*
            学到了什么
                1. @Primary 的处理, 其中 @Primary 会在 @Bean 解析或组件扫描时被解析 (另见 TestPrimary)
                2. 最后的防线, 通过属性或参数名匹配
         */
    }

    private static void testDefault(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd = new DependencyDescriptor(Target2.class.getDeclaredField("service3"), false);
        Class<?> type = dd.getDependencyType();
        for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
            if(name.equals(dd.getDependencyName())) {
                System.out.println(name);
            }
        }
    }

    private static void testPrimary(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd = new DependencyDescriptor(Target1.class.getDeclaredField("service"), false);
        Class<?> type = dd.getDependencyType();
        for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
            if (beanFactory.getMergedBeanDefinition(name).isPrimary()) {
                System.out.println(name);
            }
        }
    }

    static class Target1 {
        @Autowired private Service service;
    }

    static class Target2 {
        @Autowired private Service service3;
    }

    interface Service {

    }
    @Component("service1") static class Service1 implements Service {

    }
    //@Primary: 优先获取
    @Component("service2") static class Service2 implements Service {

    }
    @Component("service3") static class Service3 implements Service {

    }
}

收获💡

  1. @Autowired 本质上是根据成员变量或方法参数的类型进行装配
  2. 如果待装配类型是 Optional,需要根据 Optional 泛型找到 bean,再封装为 Optional 对象装配
  3. 如果待装配的类型是 ObjectFactory,需要根据 ObjectFactory 泛型创建 ObjectFactory 对象装配
    • 此方法可以延迟 bean 的获取
  4. 如果待装配的成员变量或方法参数上用 @Lazy 标注,会创建代理对象装配
    • 此方法可以延迟真实 bean 的获取
    • 被装配的代理不作为 bean
  5. 如果待装配类型是数组,需要获取数组元素类型,根据此类型找到多个 bean 进行装配
  6. 如果待装配类型是 Collection 或其子接口,需要获取 Collection 泛型,根据此类型找到多个 bean
  7. 如果待装配类型是 ApplicationContext 等特殊类型
    • 会在 BeanFactory 的 resolvableDependencies 成员按类型查找装配
    • resolvableDependencies 是 map 集合,key 是特殊类型,value 是其对应对象
    • 不能直接根据 key 进行查找,而是用 isAssignableFrom 逐一尝试右边类型是否可以被赋值给左边的 key 类型
  8. 如果待装配类型有泛型参数
    • 需要利用 ContextAnnotationAutowireCandidateResolver 按泛型参数类型筛选
  9. 如果待装配类型有 @Qualifier
    • 需要利用 ContextAnnotationAutowireCandidateResolver 按注解提供的 bean 名称筛选
  10. 有 @Primary 标注的 @Component 或 @Bean 的处理
  11. 与成员变量名或方法参数名同名 bean 的处理

2.6 事件监听器

image-20241103222748755

image-20241103222850137

image-20241103222908748

// 事件解耦例子
@Configuration
public class A48_1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A48_1.class);
        context.getBean(MyService.class).doBusiness();
        context.close();
    }

    static class MyEvent extends ApplicationEvent{
        public MyEvent(Object source) {
            super(source);
        }
    }

    @Component
    static class MyService {
        private static final Logger log = LoggerFactory.getLogger(MyService.class);
        @Autowired
        private ApplicationEventPublisher publisher;// 就是applicationContext
        public void doBusiness() {
            log.info("主线业务");
            // 主线业务完成后需要做一些支线业务,下面是问题代码
            publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
            //log.info("发送短信");
            //log.info("发送邮件");
        }
    }

    @Component
    static class SmsApplicationListener implements ApplicationListener<MyEvent> {
        private static final Logger log = LoggerFactory.getLogger(SmsApplicationListener.class);
        @Override
        public void onApplicationEvent(MyEvent event) {
            log.info("发送短信");
        }
    }

    @Component
    static class EmailApplicationListener implements ApplicationListener<MyEvent> {
        private static final Logger log = LoggerFactory.getLogger(EmailApplicationListener.class);
        @Override
        public void onApplicationEvent(MyEvent event) {
            log.info("发送邮件");
        }
    }


}

另外一种方式监听事件

image-20241103223628172

image-20241103223753142

image-20241103223828984

完整代码

@Configuration
public class A48_2 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A48_2.class);
        context.getBean(MyService.class).doBusiness();
        context.close();
    }

    static class MyEvent extends ApplicationEvent {
        public MyEvent(Object source) {
            super(source);
        }
    }

    @Component
    static class MyService {
        private static final Logger log = LoggerFactory.getLogger(MyService.class);
        @Autowired
        private ApplicationEventPublisher publisher; // applicationContext
        public void doBusiness() {
            log.info("主线业务");
            // 主线业务完成后需要做一些支线业务,下面是问题代码
            publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
        }
    }

    @Component
    static class SmsService {
        private static final Logger log = LoggerFactory.getLogger(SmsService.class);
        @EventListener
        public void listener(MyEvent myEvent) {
            log.info("发送短信");
        }
    }

    @Component
    static class EmailService {
        private static final Logger log = LoggerFactory.getLogger(EmailService.class);
        @EventListener
        public void listener(MyEvent myEvent) {
            log.info("发送邮件");
        }
    }

    @Bean
    public ThreadPoolTaskExecutor executor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        return executor;
    }

    @Bean
    public SimpleApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor executor) {
        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
        multicaster.setTaskExecutor(executor);
        return multicaster;
    }

}

自定义注解实现@EventListener效果

image-20241103230312974

image-20241103230446212

image-20241103230433408

image-20241103230547773

@Configuration
public class A48_3 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A48_3.class);
        SmsService bean = context.getBean(SmsService.class);
        for (Method method : SmsService.class.getMethods()) {
            //判断方法上是否有某个注解
            if (method.isAnnotationPresent(MyListener.class)) {
                ApplicationListener listener = new ApplicationListener() {
                    @Override
                    public void onApplicationEvent(ApplicationEvent event) {
                        //调用被@MyListener标注的方法
                        try {
                            method.invoke(bean, event);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                };
                //把新生成的listener加入到容器
                context.addApplicationListener(listener);
            }
        }
        context.getBean(MyService.class).doBusiness();
        //context.close();
    }

    @Component
    static class MyService {
        private static final Logger log = LoggerFactory.getLogger(MyService.class);
        @Autowired
        private ApplicationEventPublisher publisher; // applicationContext

        public void doBusiness() {
            log.info("主线业务");
            // 主线业务完成后需要做一些支线业务,下面是问题代码
            publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
        }
    }

    @Component
    static class SmsService {
        private static final Logger log = LoggerFactory.getLogger(SmsService.class);

        @MyListener
        public void listener(MyEvent myEvent) {
            log.info("发送短信");
        }
    }

    @Component
    static class EmailService {
        private static final Logger log = LoggerFactory.getLogger(EmailService.class);

        @MyListener
        public void listener(MyEvent myEvent) {
            log.info("发送邮件");
        }
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @interface MyListener {
    }

    static class MyEvent extends ApplicationEvent {
        public MyEvent(Object source) {
            super(source);
        }
    }
}

image-20241103231454467

image-20241103231517402

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A48_3.class);
    SmsService bean = context.getBean(SmsService.class);
    for (Method method : SmsService.class.getMethods()) {
        //判断方法上是否有某个注解
        if (method.isAnnotationPresent(MyListener.class)) {
            ApplicationListener listener = new ApplicationListener() {
                @Override
                public void onApplicationEvent(ApplicationEvent event) {
                    // 0: 方法的第一个参数   @MyListener public void listener(MyEvent myEvent)
                    Class<?> eventType = method.getParameterTypes()[0];//监听器方法需要的事件类型
                    //event.getClass() 类型可以赋值给 eventType 说明类型匹配
                    if (eventType.isAssignableFrom(event.getClass())) {
                        //调用被@MyListener标注的方法
                        try {
                            method.invoke(bean, event);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            //把新生成的listener加入到容器
            context.addApplicationListener(listener);
        }
    }
    context.getBean(MyService.class).doBusiness();
    context.close();
}

对所有bean都进行判断

image-20241104213154068

image-20241104213305646

image-20241104213800917

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A48_3.class);

    context.getBean(MyService.class).doBusiness();
    context.close();
}
@Bean
public SmartInitializingSingleton smartInitializingSingleton(ConfigurableApplicationContext context) {
    //SmartInitializingSingleton 会在所有单例bean初始化完成之后执行
    return new SmartInitializingSingleton() {
        @Override
        public void afterSingletonsInstantiated() {
            for (String name : context.getBeanDefinitionNames()) {
                Object bean = context.getBean(name);
                for (Method method : bean.getClass().getMethods()) {
                    //判断方法上是否有某个注解
                    if (method.isAnnotationPresent(MyListener.class)) {
                        ApplicationListener listener = new ApplicationListener() {
                            @Override
                            public void onApplicationEvent(ApplicationEvent event) {
                                // 0: 方法的第一个参数   @MyListener public void listener(MyEvent myEvent)
                                Class<?> eventType = method.getParameterTypes()[0];//监听器方法需要的事件类型
                                //event.getClass() 类型可以赋值给 eventType 说明类型匹配
                                if (eventType.isAssignableFrom(event.getClass())) {
                                    //调用被@MyListener标注的方法
                                    try {
                                        method.invoke(bean, event);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        };
                        //把新生成的listener加入到容器
                        context.addApplicationListener(listener);
                    }
                }
            }
        }
    };
}

收获💡

事件监听器的两种方式

  1. 实现 ApplicationListener 接口
    • 根据接口泛型确定事件类型
  2. @EventListener 标注监听方法
    • 根据监听器方法参数确定事件类型
    • 解析时机:在 SmartInitializingSingleton(所有单例初始化完成后),解析每个单例 bean

2.7 事件发布器

image-20241104220939799

image-20241104221116829

image-20241104221145557

用匿名内部类实现的

@Bean
public ApplicationEventMulticaster applicationEventMulticaster(ConfigurableApplicationContext context, ThreadPoolTaskExecutor executor) {
    return new AbstractApplicationEventMulticaster() {
        private List<GenericApplicationListener> listeners = new ArrayList<>();

        // 收集监听器
        public void addApplicationListenerBean(String name) {
            ApplicationListener listener = context.getBean(name, ApplicationListener.class);
            System.out.println(listener);
            // 获取该监听器支持的事件类型
            ResolvableType type = ResolvableType.forClass(listener.getClass()).getInterfaces()[0].getGeneric();//获取实现的接口,拿到里面的泛型
            System.out.println(type);

            // 将原始的 listener 封装为支持事件类型检查的 listener
            GenericApplicationListener genericApplicationListener = new GenericApplicationListener() {
                // 是否支持某事件类型                真实的事件类型
                public boolean supportsEventType(ResolvableType eventType) {
                    return type.isAssignableFrom(eventType);
                }

                public void onApplicationEvent(ApplicationEvent event) {
                    executor.submit(() -> listener.onApplicationEvent(event));//用线程池发事件
                }
            };

            listeners.add(genericApplicationListener);
        }

        // 发布事件
        public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
            //listeners:存储的都是addApplicationListenerBean封装后的listener
            for (GenericApplicationListener listener : listeners) {
                //传递的event事件类型和listener中支持的泛型的事件类型对比
                if (listener.supportsEventType(ResolvableType.forClass(event.getClass()))) {
                    listener.onApplicationEvent(event);
                }
            }
        }
    };
}

完整代码

@Configuration
public class A49 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A49.class);
        context.getBean(MyService.class).doBusiness();
        context.close();
    }

    static class MyEvent extends ApplicationEvent {
        public MyEvent(Object source) {
            super(source);
        }
    }

    @Component
    static class MyService {
        private static final Logger log = LoggerFactory.getLogger(MyService.class);
        @Autowired
        private ApplicationEventPublisher publisher; // applicationContext

        public void doBusiness() {
            log.debug("主线业务");
            // 主线业务完成后需要做一些支线业务,下面是问题代码
            publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
        }
    }

    @Component
    static class SmsApplicationListener implements ApplicationListener<MyEvent> {
        private static final Logger log = LoggerFactory.getLogger(SmsApplicationListener.class);

        @Override
        public void onApplicationEvent(MyEvent event) {
            log.debug("发送短信");
        }
    }

    @Component
    static class EmailApplicationListener implements ApplicationListener<MyEvent> {
        private static final Logger log = LoggerFactory.getLogger(EmailApplicationListener.class);

        @Override
        public void onApplicationEvent(MyEvent event) {
            log.debug("发送邮件");
        }
    }

    @Bean
    public ThreadPoolTaskExecutor executor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        return executor;
    }

    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster(ConfigurableApplicationContext context, ThreadPoolTaskExecutor executor) {
        return new AbstractApplicationEventMulticaster() {
            private List<GenericApplicationListener> listeners = new ArrayList<>();

            // 收集监听器
            public void addApplicationListenerBean(String name) {
                ApplicationListener listener = context.getBean(name, ApplicationListener.class);
                System.out.println(listener);
                // 获取该监听器支持的事件类型
                ResolvableType type = ResolvableType.forClass(listener.getClass()).getInterfaces()[0].getGeneric();//获取实现的接口,拿到里面的泛型
                System.out.println(type);

                // 将原始的 listener 封装为支持事件类型检查的 listener
                GenericApplicationListener genericApplicationListener = new GenericApplicationListener() {
                    // 是否支持某事件类型                真实的事件类型
                    public boolean supportsEventType(ResolvableType eventType) {
                        return type.isAssignableFrom(eventType);
                    }

                    public void onApplicationEvent(ApplicationEvent event) {
                        executor.submit(() -> listener.onApplicationEvent(event));//用线程池发事件
                    }
                };

                listeners.add(genericApplicationListener);
            }

            // 发布事件
            public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
                //listeners:存储的都是addApplicationListenerBean封装后的listener
                for (GenericApplicationListener listener : listeners) {
                    //传递的event事件类型和listener中支持的泛型的事件类型对比
                    if (listener.supportsEventType(ResolvableType.forClass(event.getClass()))) {
                        listener.onApplicationEvent(event);
                    }
                }
            }
        };
    }

    abstract static class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster {

        @Override
        public void addApplicationListener(ApplicationListener<?> listener) {

        }

        @Override
        public void addApplicationListenerBean(String listenerBeanName) {

        }

        @Override
        public void removeApplicationListener(ApplicationListener<?> listener) {

        }

        @Override
        public void removeApplicationListenerBean(String listenerBeanName) {

        }

        @Override
        public void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate) {

        }

        @Override
        public void removeApplicationListenerBeans(Predicate<String> predicate) {

        }

        @Override
        public void removeAllListeners() {

        }

        @Override
        public void multicastEvent(ApplicationEvent event) {

        }

        @Override
        public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {

        }
    }
}

收获💡

事件发布器模拟实现

  1. addApplicationListenerBean 负责收集容器中的监听器
    • 监听器会统一转换为 GenericApplicationListener 对象,以支持判断事件类型
  2. multicastEvent 遍历监听器集合,发布事件
    • 发布前先通过 GenericApplicationListener.supportsEventType 判断支持该事件类型才发事件
    • 可以利用线程池进行异步发事件优化
  3. 如果发送的事件对象不是 ApplicationEvent 类型,Spring 会把它包装为 PayloadApplicationEvent 并用泛型技术解析事件对象的原始类型
    • 视频中未讲解
posted @ 2024-11-04 22:43  千夜ん  阅读(0)  评论(0编辑  收藏  举报