原创

Spring框架学习-Spring Boot


Spring框架学习-Spring Boot

1.Spring Boot的启动过程

import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;

import java.lang.reflect.Method;
import java.util.Collections;

/
 * 测试Spring Boot执行流程
 *
 * @author lzlg
 * 2023/4/5 19:05
 */
@Configuration
public class TestSpringBoot {
    public static void main(String[] args) throws Exception {
        System.out.println("1.获取Bean Definition 源");
        // 创建Spring应用对象
        SpringApplication app = new SpringApplication(TestSpringBoot.class);
        // 添加Bean定义的xml文件
        app.setSources(Collections.singleton("classpath:bean02.xml"));

        System.out.println("2.推断应用类型");
        // 进行反射调用
        Method deduceFromClasspath = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath");
        deduceFromClasspath.setAccessible(true);
        System.out.println("当前应用类型为: " + deduceFromClasspath.invoke(null));

        System.out.println("3.添加一些初始化器");
        // 这些初始化器在创建ApplicationContext之后,在调用ApplicationContext.refresh()方法之前进行扩展
        app.setInitializers(Collections.singleton(
                (ApplicationContextInitializer<GenericApplicationContext>) context -> {
                    // 向容器中添加Bean3
                    context.registerBean("bean3", Bean3.class);
                }));

        System.out.println("4.添加事件监听器");
        app.setListeners(Collections.singleton(
                (ApplicationListener<ApplicationEvent>) event -> {
                    System.out.println("事件类型为: " + event.getClass());
                }));


        System.out.println("5.启动类推断");
        // 进行反射调用
        Method deduceMainApplicationClass = SpringApplication.class.getDeclaredMethod("deduceMainApplicationClass");
        deduceMainApplicationClass.setAccessible(true);
        System.out.println("启动类为: " + deduceMainApplicationClass.invoke(app));


        // 调用run方法,启动Spring应用
        ConfigurableApplicationContext context = app.run(args);

        // 打印容器中的Bean名称
        for (String name : context.getBeanDefinitionNames()) {
            // 根据Bean名称获取来源描述信息
            String description = context.getBeanFactory().getBeanDefinition(name)
                    .getResourceDescription();
            System.out.println("Bean名称是: " + name + ", 来源: " + description);
        }
        context.close();
    }

    static class Bean1 {

    }

    static class Bean2 {

    }

    static class Bean3 {

    }

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

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

import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.boot.context.event.EventPublishingRunListener;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.support.SpringFactoriesLoader;

import java.lang.reflect.Constructor;
import java.util.List;

/
 * 测试Spring Boot启动流程中的事件发布
 *
 * @author lzlg
 * 2023/4/5 19:37
 */
public class TestSpringBootEvent {
    public static void main(String[] args) throws Exception {
        // 创建Spring应用对象
        SpringApplication app = new SpringApplication();
        GenericApplicationContext genericContext = new GenericApplicationContext();
        // 打印其中的事件发布类名称
        app.addListeners(event -> System.out.println(event.getClass()));
        // 从META-INF中的spring.factories配置文件中获取事件发布器
        List<String> listeners = SpringFactoriesLoader.loadFactoryNames(
                SpringApplicationRunListener.class,
                TestSpringBootEvent.class.getClassLoader());
        // EventPublishingRunListener是接口SpringApplicationRunListener的实现
        // 在META-INF中的spring.factories配置文件中配置

        for (String listener : listeners) {
            System.out.println(listener);

            // 创建EventPublishingRunListener实例
            Constructor<EventPublishingRunListener> constructor = EventPublishingRunListener
                    .class.getConstructor(SpringApplication.class, String[].class);
            EventPublishingRunListener runListener = constructor.newInstance(app, args);

            // 测试用的Context
            DefaultBootstrapContext context = new DefaultBootstrapContext();
            // 1.Spring Boot开始启动时事件发布
            runListener.starting(context);
            // 3.Spring Boot环境信息准备完毕事件发布
            runListener.environmentPrepared(context, new StandardEnvironment());
            // 4.Spring Boot容器创建完毕,调用初始化器后事件发布
            runListener.contextPrepared(genericContext);
            // 5.Spring Boot容器中所有Bean定义加载完成事件发布
            runListener.contextLoaded(genericContext);

            // 刷新容器
            genericContext.refresh();

            // 6.容器启动后事件发布,容器初始化完成,refresh方法调用完毕
            runListener.started(genericContext);
            // 7.Spring Boot启动完毕事件发布
            runListener.running(genericContext);

            // 8.Spring Boot启动失败事件发布
            runListener.failed(genericContext, new Exception("启动应用出现异常"));
        }
    }
}

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.*;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;

import java.util.Arrays;

/
 * 测试Spring Boot启动流程
 *
 * @author lzlg
 * 2023/4/5 20:13
 */
public class TestSpringBootRunStep {
    @SuppressWarnings("all")
    public static void main(String[] args) throws Exception {
        // 创建Spring Boot应用
        SpringApplication app = new SpringApplication();
        // 添加初始化器
        app.addInitializers(context ->
                System.out.println("创建容器后,在调用ApplicationContext.refresh()方法之前,执行初始化器..."));

        System.out.println(">>>>>>>>>>2.封装启动的参数args<<<<<<<<<<<<");
        DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);

        System.out.println(">>>>>>>>>>8.创建容器<<<<<<<<<<<<");
        // 获取应用类型
        WebApplicationType webApplicationType = app.getWebApplicationType();
        // 容器获取
        GenericApplicationContext context;
        // 根据应用类型返回对应的容器类型
        switch (webApplicationType) {
            case SERVLET:
                context = new AnnotationConfigServletWebServerApplicationContext();
                break;
            case REACTIVE:
                context = new AnnotationConfigReactiveWebServerApplicationContext();
                break;
            default:
                context = new AnnotationConfigApplicationContext();
        }

        System.out.println(">>>>>>>>>>9.准备容器<<<<<<<<<<<<");
        // 执行初始化器逻辑
        for (ApplicationContextInitializer initializer : app.getInitializers()) {
            initializer.initialize(context);
        }

        System.out.println(">>>>>>>>>>10.加载Bean定义<<<<<<<<<<<<");
        // Bean工厂
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        // 从配置类中加载
        AnnotatedBeanDefinitionReader annotatedReader = new AnnotatedBeanDefinitionReader(beanFactory);
        annotatedReader.register(Config.class);

        // 从配置文件xml中加载
        XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(beanFactory);
        xmlReader.loadBeanDefinitions(new ClassPathResource("bean03.xml"));

        // 从扫描包中进行加载
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
        scanner.scan("com.lzlg.study.spring.boot.bean");


        System.out.println(">>>>>>>>>>11.refresh容器<<<<<<<<<<<<");
        context.refresh();

        // 打印容器中的Bean名称和来源
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println("name:" + name + " 来源: " + beanFactory.getBeanDefinition(name)
                    .getResourceDescription());
        }

        System.out.println(">>>>>>>>>>12.执行命令行Runner中的方法<<<<<<<<<<<<");
        // 命令行参数
        for (CommandLineRunner runner : context.getBeansOfType(CommandLineRunner.class).values()) {
            runner.run(args);
        }
        // 应用运行参数
        for (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class).values()) {
            runner.run(arguments);
        }

    }


    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 args -> System.out.println("命令行参数: " + Arrays.toString(args));
        }

        @Bean
        public ApplicationRunner applicationRunner() {
            return args -> {
                System.out.println("命令行所有参数: " + Arrays.toString(args.getSourceArgs()));
                System.out.println("命令行选项参数(比如--server.port=8080): " + args.getOptionNames());
                System.out.println("命令行非选项参数: " + args.getNonOptionArgs());
            };
        }
    }
}

启动过程源码解读:

/
 * @author lzlg
 * 2023/4/6 18:57
 */
public class SpringBootRunStepDesc {
    public static void main(String[] args) {

    }

    /
    public ConfigurableApplicationContext run(String... args) {
     // 计时器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
     // 创建Bootstrap根容器
        DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        ConfigurableApplicationContext context = null;
     // Headless 模式是系统的一种配置模式,在系统可能缺少显示设备,键盘或鼠标这些输入输出外设的情况下可以使用该模式
     // 设置Headless默认为true
        configureHeadlessProperty();
     // 获取Spring应用监听器,从META-INF中的Spring.Factories文件中获取
     // 核心方法: SpringFactoriesLoader.loadFactoryNames(type, classLoader);
        SpringApplicationRunListeners listeners = getRunListeners(args);
     // 发布应用启动事件
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
     // 将参数封装为ApplicationArguments(分为选项参数:前缀是--的属性,其他属性)
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
     // 准备环境配置信息
            ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
     // 配置spring.beaninfo.ignore顺序(默认为true)
            configureIgnoreBeanInfo(environment);
     // 打印Spring应用的Banner信息
            Banner printedBanner = printBanner(environment);
     // 创建应用容器,根据应用的类别创建对应的ApplicationContext
            context = createApplicationContext();
     // 设置应用启动步骤信息
            context.setApplicationStartup(this.applicationStartup);
     // 准备Spring Boot应用需要的配置
            prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
     // 刷新Spring容器,完成Bean的创建,即调用context.refresh()方法
            refreshContext(context);
     // 留给子类扩展的刷新后方法
            afterRefresh(context, applicationArguments);

            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
     // 发布容器启动的事件
            listeners.started(context);
     // 调用实现CommandLineRunner和ApplicationRunner接口的对象的run方法
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
     // 处理容器启动失败
            handleRunFailure(context, ex, listeners);
            throw new IllegalStateException(ex);
        }

     // 发布容器启动中的事件
        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
     */

    /
    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                                                       DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        // 根据应用类型获取环境对象
        ConfigurableEnvironment environment = getOrCreateEnvironment();
     // 从applicationArguments中读取信息配置环境属性,配置属性来源,配置属性文件
        configureEnvironment(environment, applicationArguments.getSourceArgs());
     // 将不规范的命名统一规范
        ConfigurationPropertySources.attach(environment);
     // 发布环境准备好的事件
        listeners.environmentPrepared(bootstrapContext, environment);
        DefaultPropertiesPropertySource.moveToEnd(environment);
        Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
                "Environment prefix cannot be set via properties.");
     // 把spring.main前缀的属性信息绑定到environment中指定的字段中
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
     // 配置完成后,进行移除
        ConfigurationPropertySources.attach(environment);
        return environment;
    }
     */

    /
    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
                                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                                ApplicationArguments applicationArguments, Banner printedBanner) {
     // 给容器添加环境信息
        context.setEnvironment(environment);
     // 添加应用需要的Bean名称生成器,数据类型转换器等...
        postProcessApplicationContext(context);
     // 应用初始化器,调用初始化器的初始化方法
        applyInitializers(context);
     // 发布容器准备好的事件
        listeners.contextPrepared(context);
     // 发布bootstrapContext关闭事件
        bootstrapContext.close(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
     // 如果是懒加载,则添加懒加载的Bean工厂后置处理器
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // 获取所有的资源对象信息
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
     // 加载所有的资源对象信息
        load(context, sources.toArray(new Object[0]));
     // 发布容器加载完毕的事件
        listeners.contextLoaded(context);
    }
     */
}

2.内嵌Tomcat容器

import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.http11.Http11Nio2Protocol;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
import java.nio.file.Files;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/
 * 测试Spring和Tomcat的整合
 *
 * @author lzlg
 * 2023/4/6 19:54
 */
public class TestSpringTomcat {
    /*
    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
 */
    public static void main(String[] args) throws Exception {
        // ============在context.refresh()方法中的onFresh()步骤中执行
        // 1.创建Tomcat容器
        Tomcat tomcat = new Tomcat();
        // 设置基础目录,存放tomcat相关的文件
        tomcat.setBaseDir("tomcat");

        // 2.创建项目文件夹,项目磁盘路径, 即 docBase
        // 这里创建临时文件夹
        File docBase = Files.createTempDirectory("boot.").toFile();

        // 3.创建Tomcat项目,在Tomcat中称为Context
        Context context = tomcat.addContext("", docBase.getAbsolutePath());

        // Spring的web容器
        WebApplicationContext springContext = webApplicationContext();

        // 4.编程添加Servlet
        // 在Servlet容器初始化器中添加Servlet
        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            @SuppressWarnings("all")
            public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
                // 添加Servlet
                ctx.addServlet("helloServlet", new HelloServlet()).addMapping("/hello1");

                // 通过ServletRegistrationBean添加
                for (ServletRegistrationBean registrationBean : springContext.getBeansOfType(
                        ServletRegistrationBean.class).values()) {
                    registrationBean.onStartup(ctx);
                }

            }
        }, Collections.emptySet());

        // ================在context.refresh()方法中的finishRefresh()步骤中执行
        // 5.启动Tomcat,启动时回调ServletContainerInitializer中的方法,将Servlet添加
        tomcat.start();

        // 6.保留端口,接受服务
        Connector connector = new Connector(new Http11Nio2Protocol());
        connector.setPort(8080);
        tomcat.setConnector(connector);
    }

    /
     * 获取web应用容器
     */
    private static WebApplicationContext webApplicationContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        // 注册配置类
        context.register(MyConfig.class);
        // 刷新容器,生成Bean
        context.refresh();
        return context;
    }


    @Configuration
    static class MyConfig {

        /
         * DispatcherServlet
         */
        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }

        /
         * 注册Servlet容器
         */
        @Bean
        public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        /
         * 请求路径处理器适配器
         */
        @Bean
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
            handlerAdapter.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
            return handlerAdapter;
        }

        @RestController
        static class MyController {

            @GetMapping("/hello2")
            public Map<String, Object> hello2() {
                Map<String, Object> map = new HashMap<>(1);
                map.put("hello", "Spring + Tomcat !");
                return map;
            }
        }
    }
}

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/
 * @author lzlg
 * 2023/4/6 20:08
 */
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("<h3>Hello HelloServlet</h3>");
    }
}

3.Spring Boot自动配置

import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.AnnotationMetadata;

/
 * 测试Spring Boot Aop自动代理原理
 *
 * @author lzlg
 * 2023/4/8 19:28
 */
public class TestSpringAopAutoConfig {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        StandardEnvironment env = new StandardEnvironment();
        // 控制AopAutoConfiguration自动装配是否生效的属性(命令行)
        env.getPropertySources().addLast(new SimpleCommandLinePropertySource("--spring.aop.auto=true"));
        env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
                "--spring.datasource.url=jdbc:mysql://localhost:3306/test",
                "--spring.datasource.username=root",
                "--spring.datasource.password=root"));
        context.setEnvironment(env);
        // 添加常用的注解配置后置处理器,如ConfigurationClassPostProcessor
        // AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
        // 注册自定义的配置类
        context.registerBean(MyConfig.class);

        // 设置当前类扫描的包路径名称
        String packageName = TestSpringAopAutoConfig.class.getPackage().getName();
        System.out.println("当前包名: " + packageName);
        // 将自动装配的包名置为: 当前类扫描的包,这样可以看到通过@Mapper注解扫描到的Mapper接口
        AutoConfigurationPackages.register(context, packageName);
        // 注解@ConditionalOnSingleCandidate(DataSource.class),当只有一个DataSource时才生效
        // @AutoConfigureAfter注解,可以在那些自动装配之后进行自动装配

        context.refresh();
        // 通过MyImportSelector注入的AopAutoConfiguration配置类,通过条件装配注解
        // 向容器中添加了AspectJAutoProxyingConfiguration和CglibAutoProxyConfiguration
        // 注解@EnableAspectJAutoProxy(proxyTargetClass = true)中
        // proxyTargetClass为true,无论如何都使用CGLIB进行代理,为false,则优先使用JDK动态代理
        // @EnableAspectJAutoProxy注解实际上是复合注解,通过@Import(AspectJAutoProxyRegistrar.class)
        // 导入AspectJAutoProxyRegistrar这个来导入Aop注解需要的后置处理器AnnotationAwareAspectJAutoProxyCreator
        for (String name : context.getBeanDefinitionNames()) {
            String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
            if (resourceDescription != null) {
                System.out.println(name + "\t 来源: " + resourceDescription);
            }
        }
        AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(
                AnnotationAwareAspectJAutoProxyCreator.class);
        System.out.println(">>>>>>>>>>>>>ProxyTargetClass配置: ");
        System.out.println(creator.isProxyTargetClass());

        System.out.println(">>>>>>>>>>>>>DataSourceProperties配置: ");
        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 MyConfig {

    }

    static class MyImportSelector implements DeferredImportSelector {

        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{
                    // Aop注解配置类
                    AopAutoConfiguration.class.getName(),
                    // 数据源自动配置类
                    DataSourceAutoConfiguration.class.getName(),
                    // MyBatis自动配置类
                    MybatisAutoConfiguration.class.getName(),
                    // 数据源事务管理器自动配置类
                    DataSourceTransactionManagerAutoConfiguration.class.getName(),
                    // 事务自定配置类,声明事务管理,这里有@EnableTransactionManagement注解
                    TransactionAutoConfiguration.class.getName(),

                    // ServletWebServerFactory自动配置类
                    ServletWebServerFactoryAutoConfiguration.class.getName(),
                    // DispatcherServlet自动配置类
                    DispatcherServletAutoConfiguration.class.getName(),
                    // WebMvc自动配置类
                    WebMvcAutoConfiguration.class.getName(),
                    // 对错误页面进行处理的自动配置类
                    ErrorMvcAutoConfiguration.class.getName(),
            };
        }
    }
}

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;

import java.util.List;

/
 * 测试Spring Boot中自动装配
 *
 * @author lzlg
 * 2023/4/8 18:47
 */
public class TestSpringAutoConfig {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        // 设置允许Bean定义覆盖,如果给false,出现相同的Bean则会抛出异常
        context.setAllowBeanDefinitionOverriding(false);
//        context.registerBean(MyConfig.class);
        // 注册配置类
        context.registerBean(ImportSelectorConfig.class);
        // 配置类Bean工厂后置处理器,解析@Configuration注解的,@Bean注解,@Import注解
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        // 1.通过@Import注解导入第三方的配置类
        // 缺点: 1) 硬编码方式,不灵活; 2) 本项目配置的同类型的Bean会覆盖第三方的Bean
        Bean1 bean = context.getBean(Bean1.class);
        System.out.println(bean.name);

        // 2.通过注解@Import注解导入实现ImportSelector接口或DeferredImportSelector接口(延迟)
        // 注解@ConditionalOnMissingBean当没有此Bean时,此Bean才生效
        // 实现DeferredImportSelector接口,则本项目的Bean优先级较高(先生效),第三方的Bean优先级低(后生效)
        // 实现ImportSelector接口,导入的配置类中定义的Bean优先级高
    }

    /
     * 本项目的配置类
     */
    @Configuration
    @Import({MyImportSelector.class})
    static class ImportSelectorConfig {
        @Bean
        @ConditionalOnMissingBean
        public Bean1 bean1() {
            return new Bean1("本项目");
        }
    }

    /
     * 自定义导入选择器
     */
    static class MyImportSelector implements DeferredImportSelector {

        /
         * @return 返回值是导入的类的全名称(字符串)
         */
        @Override
        public String[] selectImports(AnnotationMetadata metadata) {
            // 通过读取类路径下META-INF下的spring.factories文件
            List<String> factoryNames = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
            for (String factoryName : factoryNames) {
                System.out.println(factoryName);
            }
            return factoryNames.toArray(new String[0]);
        }
    }

    /
     * 本项目的配置类
     */
    @Configuration
    @Import({OneAutoConfig.class, TwoAutoConfig.class})
    static class MyConfig {
        @Bean
        public Bean1 bean1() {
            return new Bean1("本项目");
        }
    }

    /
     * 第三方的配置类1
     */
    @Configuration
    static class OneAutoConfig {
        @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 + '\'' +
                    '}';
        }
    }

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

    static class Bean2 {

    }
}

import org.apache.ibatis.annotations.Mapper;

/
 * @author lzlg
 * 2023/4/8 20:12
 */
@Mapper
public interface Mapper1 {
}

import org.apache.ibatis.annotations.Mapper;

/
 * @author lzlg
 * 2023/4/8 20:12
 */
@Mapper
public interface Mapper2 {
}

/
 * @author lzlg
 * 2023/4/8 20:12
 */
public interface Mapper3 {
}

自动装配配置文件: 类路径下的META-INF中的spring.factories

com.lzlg.study.spring.autoconfig.TestSpringAutoConfig$MyImportSelector=\
  com.lzlg.study.spring.autoconfig.TestSpringAutoConfig.OneAutoConfig,\
  com.lzlg.study.spring.autoconfig.TestSpringAutoConfig.TwoAutoConfig

AopAutoConfiguration

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

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

DataSourceAutoConfiguration

  • 对应的自动配置类为: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

  • 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

  • 事务自动配置类有两个:

    • org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
    • org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
  • 前者配置了 DataSourceTransactionManager 用来执行事务的提交、回滚操作

  • 后者功能上对标 @EnableTransactionManagement,包含以下三个 bean

    • BeanFactoryTransactionAttributeSourceAdvisor 事务切面类,包含通知和切点
    • TransactionInterceptor 事务通知类,由它在目标方法调用前后加入事务操作
    • AnnotationTransactionAttributeSource 会解析 @Transactional 及事务属性,也包含了切点功能
  • 如果自己配置了 DataSourceTransactionManager 或是在引导类加了 @EnableTransactionManagement,则以自己配置的为准

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 格式的数据

4.条件装配

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
import java.util.Objects;

/
 * 测试Spring Boot中条件装配原理
 *
 * @author lzlg
 * 2023/4/8 21:01
 */
public class TestSpringConditional {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        // 注册常用的注解后置处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
        // 注册配置类
        context.registerBean(MyConfig.class);

        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        // 判断到底是哪个配置生效了
        MyBean bean = context.getBean(MyBean.class);
        System.out.println(bean);
    }

    @Configuration
    @Import({OneAutoConfig.class, TwoAutoConfig.class})
    static class MyConfig {

    }

    /
     * 自定义Condition条件装配判断
     */
    static class MyCondition implements Condition {

        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            // 获取注解上的数据信息
            Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClazz.class.getName());
            if (Objects.nonNull(attributes)) {
                // 获取exists属性字段的值
                boolean exists = (boolean) attributes.getOrDefault("exists", false);
                // 获取clzArray属性字段的值
                Object clzArrayObj = attributes.get("clzArray");
                if (Objects.nonNull(clzArrayObj)) {
                    Class<?>[] clzArray = (Class<?>[]) clzArrayObj;
                    for (Class<?> clz : clzArray) {
                        boolean present = ClassUtils.isPresent(clz.getName(), null);
                        // 如果需要存在指定的类
                        if (exists) {
                            // 但不存在,则返回false
                            if (!present) {
                                return false;
                            }
                        } else {
                            // 如果不需要存在指定的类,但存在了,则返回false
                            if (present) {
                                return false;
                            }
                        }
                    }
                    // 符合匹配条件,返回true
                    return true;
                }
            }

            return false;
        }
    }

    @Retention(RetentionPolicy.RUNTIME) // 运行期间生效
    @Target({ElementType.METHOD, ElementType.TYPE}) // 在方法和类上使用
    // 组合注解,指明条件装配的条件,需实现Condition接口
    @Conditional(MyCondition.class)
    @interface ConditionalOnClazz {
        // 判断类是否存在
        boolean exists() default false;

        // 需要判断的类集合
        Class<?>[] clzArray();
    }

    @Configuration
    // 如果存在DruidDataSource类则此配置生效
    @ConditionalOnClazz(exists = false, clzArray = {DruidDataSource.class})
    static class OneAutoConfig {
        @Bean
        public MyBean bean1() {
            return new MyBean("one");
        }
    }

    @Configuration
    // 如果不存在DruidDataSource类则此配置生效
    @ConditionalOnClazz(exists = true, clzArray = {DruidDataSource.class})
    static class TwoAutoConfig {
        @Bean
        public MyBean bean2() {
            return new MyBean("two");
        }
    }

    static class MyBean {

        private String name;

        public MyBean() {
        }

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

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

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

5.注解@Indexed原理

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;

/
 * 测试Spring中@Indexed注解优化启动原理
 * 注解@Component上的@Indexed注解在编译的时候
 * 会在META-INF下生成spring.components文件,缓存容器中的Bean
 * 组件扫描时
 * 1.如果发现 META-INF/spring.components存在,以它为准加载bean definition
 * 2.否则,会遍历包下所有class资源(包括jar内的)
 *
 * 可通过添加依赖自动实现
 *  <dependency>
 *      <groupId>org.springframework</groupId>
 *      <artifactId>spring-context-indexer</artifactId>
 *      <optional>true</optional>
 *  </dependency>
 *
 * @author lzlg
 * 2023/4/8 22:00
 */
public class TestSpringIndexed {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
        scanner.scan(TestSpringIndexed.class.getPackage().getName());

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

    }
}

6.注解@Value装配低层

import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

/
 * 测试Spring中@Value注解的解析过程
 *
 * @author lzlg
 * 2023/4/9 20:26
 */
@Configuration
public class TestSpringValueResolver {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
                TestSpringValueResolver.class);

        // Spring通过ContextAnnotationAutowireCandidateResolver来解析@Value注解
        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(context);

        // 1.测试解析@Value注解中的${}属性
//        testValue$Resolver(context, resolver);

        // 2.测试解析@Value的类型转换
//        testValueTypeConverter(context, resolver);

        // 3.测试解析@Value中Spring EL表达式的解析
//        testValueSpringELResolver(context, resolver);

        // 4.测试解析@Value综合测试
        testValueAllResolver(context, resolver);

        context.close();
    }

    /
     * 测试解析@Value综合测试
     */
    private static void testValueAllResolver(AnnotationConfigApplicationContext context,
                                             ContextAnnotationAutowireCandidateResolver resolver)
            throws NoSuchFieldException {
        // 通过DependencyDescriptor来获取需要解析的字段的信息
        Field field = Bean4.class.getDeclaredField("hello");
        // 第一个参数是要解析的字段信息,第二个是否必须,如果为false,则可以返回null
        DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
        // resolver中获取到的是@Value中的字符串信息
        String content = resolver.getSuggestedValue(descriptor).toString();
        System.out.println(content);

        // 通过环境对象来解析属性值即${}
        String value = context.getEnvironment().resolvePlaceholders(content);
        System.out.println(value + "\t" + value.getClass());

        // 通过容器中的ExpressionResolver来解析#{}即Spring EL表达式
        Object evaluate = context.getBeanFactory().getBeanExpressionResolver().evaluate(value,
                new BeanExpressionContext(context.getBeanFactory(), null));

        // 类型转换
        Object result = context.getBeanFactory().getTypeConverter()
                .convertIfNecessary(evaluate, descriptor.getDependencyType());
        System.out.println(result);

    }

    /
     * 测试解析@Value中Spring EL表达式的解析
     */
    private static void testValueSpringELResolver(AnnotationConfigApplicationContext context,
                                                  ContextAnnotationAutowireCandidateResolver resolver)
            throws NoSuchFieldException {
        // 通过DependencyDescriptor来获取需要解析的字段的信息
        Field field = Bean2.class.getDeclaredField("bean3");
        // 第一个参数是要解析的字段信息,第二个是否必须,如果为false,则可以返回null
        DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
        // resolver中获取到的是@Value中的字符串信息
        String content = resolver.getSuggestedValue(descriptor).toString();
        System.out.println(content);

        // 通过环境对象来解析属性值即${}
        String value = context.getEnvironment().resolvePlaceholders(content);
        System.out.println(value + "\t" + value.getClass());

        // 通过容器中的ExpressionResolver来解析#{}即Spring EL表达式
        Object evaluate = context.getBeanFactory().getBeanExpressionResolver().evaluate(value,
                new BeanExpressionContext(context.getBeanFactory(), null));

        // 类型转换
        Object result = context.getBeanFactory().getTypeConverter()
                .convertIfNecessary(evaluate, descriptor.getDependencyType());
        System.out.println(result);

    }

    /
     * 测试解析@Value的类型转换
     */
    private static void testValueTypeConverter(AnnotationConfigApplicationContext context,
                                               ContextAnnotationAutowireCandidateResolver resolver)
            throws NoSuchFieldException {
        // 通过DependencyDescriptor来获取需要解析的字段的信息
        Field field = Bean1.class.getDeclaredField("age");
        // 第一个参数是要解析的字段信息,第二个是否必须,如果为false,则可以返回null
        DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
        // resolver中获取到的是@Value中的字符串信息
        String content = resolver.getSuggestedValue(descriptor).toString();
        System.out.println(content);

        // 通过环境对象来解析属性值即${}
        String value = context.getEnvironment().resolvePlaceholders(content);
        System.out.println(value + "\t" + value.getClass());

        // 通过类型转换器来进行类型转换,可以看到age已经转换为Integer类型
        Object result = context.getBeanFactory().getTypeConverter()
                .convertIfNecessary(value, descriptor.getDependencyType());
        System.out.println(result + "\t" + result.getClass());

    }

    /
     * 测试解析@Value注解中的${}属性
     */
    private static void testValue$Resolver(AnnotationConfigApplicationContext context,
                                           ContextAnnotationAutowireCandidateResolver resolver)
            throws NoSuchFieldException {
        // 通过DependencyDescriptor来获取需要解析的字段的信息
        Field field = Bean1.class.getDeclaredField("home");
        // 第一个参数是要解析的字段信息,第二个是否必须,如果为false,则可以返回null
        DependencyDescriptor descriptor = new DependencyDescriptor(field, false);
        // resolver中获取到的是@Value中的字符串信息
        String content = resolver.getSuggestedValue(descriptor).toString();
        System.out.println(content);

        // 通过环境对象来解析属性值即${}
        String value = context.getEnvironment().resolvePlaceholders(content);
        System.out.println(value);
    }

    static class Bean1 {
        @Value("${JAVA_HOME}")
        private String home;
        @Value("18")
        private int age;
    }

    static class Bean2 {
        @Value("#{@bean3}")
        private Bean3 bean3;
    }

    static class Bean4 {
        @Value("#{'hello' + ' ${JAVA_HOME}'}")
        private String hello;
    }

    @Component("bean3")
    static class Bean3 {

    }
}

7.注解@Autowired装配低层

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;

import java.util.Optional;

/
 * 测试Spring中依赖注入
 *
 * @author lzlg
 * 2023/4/9 21:02
 */
@Configuration
public class TestSpringDependencyInject {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
                TestSpringDependencyInject.class);
        // 获取Bean工厂
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        // 1.字段依赖注入: 通过resolveDependency方法进行依赖解析并注入
        DependencyDescriptor dd1 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);
        Object bean1 = beanFactory.resolveDependency(dd1, "bean1", null, null);
        System.out.println(bean1);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 2.setter方法依赖注入: 通过MethodParameter
        DependencyDescriptor dd2 = new DependencyDescriptor(
                new MethodParameter(Bean1.class.getMethod("setBean2", Bean2.class), 0),
                false);
        Object bean2 = beanFactory.resolveDependency(dd2, "bean1", null, null);
        System.out.println(bean2);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 3.Optional依赖注入
        DependencyDescriptor dd3 = new DependencyDescriptor(Bean1.class.getDeclaredField("optional"), false);
        // 如果是Optional类,则提示一级内嵌解析
        if (dd3.getDependencyType() == Optional.class) {
            dd3.increaseNestingLevel();
        }
        // 如果直接调用则返回的是Optional
        Object bean3 = beanFactory.resolveDependency(dd3, "bean1", null, null);
        System.out.println(bean3);
        Optional<Object> optional = Optional.ofNullable(bean3);
        System.out.println(optional);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 4.ObjectFactory依赖注入
        DependencyDescriptor dd4 = new DependencyDescriptor(Bean1.class.getDeclaredField("objectFactory"), false);
        // 如果是ObjectFactory类,则提示一级内嵌解析
        if (dd4.getDependencyType() == ObjectFactory.class) {
            dd4.increaseNestingLevel();
        }
        // 如果直接调用则返回的是ObjectFactory,可封装成ObjectFactory,在调用Object方法时才进行依赖解析
        ObjectFactory objectFactory = new ObjectFactory() {
            @Override
            public Object getObject() throws BeansException {
                return beanFactory.resolveDependency(dd4, "bean1", null, null);
            }
        };
        System.out.println(objectFactory.getObject());
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 5.@Lazy注解会产生代理对象
        DependencyDescriptor dd5 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean3"), false);
        // 需要通过ContextAnnotationAutowireCandidateResolver来进行代理对象的处理
        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);
        // 如果加了@Lazy注解则会返回代理对象
        Object proxy = resolver.getLazyResolutionProxyIfNecessary(dd5, "bean1");
        System.out.println(proxy);
        // 可以看到已经被CGLIB增强了
        System.out.println(proxy.getClass());

        context.close();
    }

    static class Bean1 {
        @Autowired
        private Bean2 bean2;

        @Autowired
        @Lazy
        private Bean2 bean3;

        @Autowired
        public void setBean2(Bean2 bean2) {
            this.bean2 = bean2;
        }

        @Autowired
        private Optional<Bean2> optional;

        @Autowired
        private ObjectFactory<Bean2> objectFactory;
    }

    @Component
    static class Bean2 {

    }
}

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/
 * 测试Spring中依赖注入选择原理
 *
 * @author lzlg
 * 2023/4/9 21:48
 */
@Configuration
public class TestSpringDependencyInjectSelect {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
                TestSpringDependencyInjectSelect.class);
        // Bean工厂
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        System.out.println("1.数组类型的依赖注入>>>>>>>>>>>>>>>>>>>>>>>>>");
        testArray(beanFactory);

        System.out.println("2.List类型的依赖注入>>>>>>>>>>>>>>>>>>>>>>>>>");
        testList(beanFactory);

        System.out.println("3.ApplicationContext类型的依赖注入>>>>>>>>>>>>>>>>>>>>>>>>>");
        // 在容器refresh方法中的prepareBeanFactory阶段就把ApplicationContext注入了
        // beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        // beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        // beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        // beanFactory.registerResolvableDependency(ApplicationContext.class, this);
        testApplicationContext(beanFactory);

        System.out.println("4.Dao泛型类型的依赖注入>>>>>>>>>>>>>>>>>>>>>>>>>");
        testDaoGeneric(beanFactory);

        System.out.println("5.@Qualifier注解的依赖注入>>>>>>>>>>>>>>>>>>>>>>>>>");
        testQualifier(beanFactory);

        context.close();
    }


    /
     * 注解@Qualifier的依赖注入
     */
    private static void testQualifier(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        // 获取依赖描述信息
        DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);
        // 获取需要注入的类型
        Class<?> type = dd.getDependencyType();
        // 需要借助ContextAnnotationAutowireCandidateResolver
        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);
        // 根据类型获取Bean名称,注意此时有多个
        for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
            // 根据名称获取Bean定义信息,注意使用getMergedBeanDefinition
            BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
            // isAutowireCandidate里会根据注解Qualifier判断是否是需要注入的类型
            if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd, name), dd)) {
                System.out.println(name);
                // 然后获取Bean
                Object bean = dd.resolveCandidate(name, type, beanFactory);
                System.out.println(bean);
            }
        }
    }

    /
     * Dao泛型类型的依赖注入
     */
    private static void testDaoGeneric(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        // 获取依赖描述信息
        DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("dao"), true);
        // 这里dd.getDependencyType()返回的只是Dao接口的类型
        Class<?> type = dd.getDependencyType();
        // 需要借助ContextAnnotationAutowireCandidateResolver
        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);
        // 根据Dao接口类型获取Bean名称,注意此时有多个
        for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
            // 根据名称获取Bean定义信息
            BeanDefinition bd = beanFactory.getBeanDefinition(name);
            // 判断是否是需要注入的泛型类型
            if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd, name), dd)) {
                System.out.println(name);
                // 然后获取Bean
                Object bean = dd.resolveCandidate(name, type, beanFactory);
                System.out.println(bean);
            }
        }
    }

    /
     * ApplicationContext类型的依赖注入
     */
    @SuppressWarnings("unchecked")
    private static void testApplicationContext(DefaultListableBeanFactory beanFactory) throws Exception {
        // 获取依赖描述信息
        DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("applicationContext"), true);
        // 通过反射调用获取ApplicationContext存放的Map集合:resolvableDependencies
        Field field = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
        field.setAccessible(true);
        Map<Class<?>, Object> dependencies = (Map<Class<?>, Object>) field.get(beanFactory);
        // 遍历集合
        for (Map.Entry<Class<?>, Object> entry : dependencies.entrySet()) {
            System.out.println(entry.getKey());
            // 判断dd的类型是否能够是entry.getKey()类型的衍生类型
            if (entry.getKey().isAssignableFrom(dd.getDependencyType())) {

                // 如果是则value就是ApplicationContext
                System.out.println(entry.getValue());
                break;
            }
        }
    }

    /
     * List类型的依赖注入
     */
    private static void testList(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        // 获取依赖描述信息
        DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), true);
        // 判断是否是List类型
        if (dd.getDependencyType() == List.class) {
            // 获取List中泛型的类型信息
            Class<?> type = dd.getResolvableType().getGeneric().resolve();
            // 获取该类型下的,容器中的Bean名称列表
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type);
            // 通过resolveCandidate从容器中获取Bean,本质上还是调用beanFactory.getBean(beanName)
            List<Object> list = Arrays.stream(beanNames)
                    .map(name -> dd.resolveCandidate(name, type, beanFactory))
                    .collect(Collectors.toList());
            // 进行类型转换,返回结果
            Object result = beanFactory.getTypeConverter().convertIfNecessary(list, dd.getDependencyType());
            System.out.println(result);
        }
    }

    /
     * 数组类型的依赖注入
     */
    private static void testArray(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        // 获取依赖描述信息
        DependencyDescriptor dd = new DependencyDescriptor(Target.class.getDeclaredField("serviceArray"), true);
        // 判断是否是数组类型
        if (dd.getDependencyType().isArray()) {
            // 获取数组中元素的类型
            Class<?> type = dd.getDependencyType().getComponentType();
            // 获取该类型下的,容器中的Bean名称列表
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type);
            // 根据名称从容器中获取Bean
            Object[] beans = new Object[beanNames.length];
            for (int i = 0; i < beanNames.length; i++) {
                // 通过resolveCandidate从容器中获取Bean,本质上还是调用beanFactory.getBean(beanName)
                beans[i] = dd.resolveCandidate(beanNames[i], type, beanFactory);
            }
            // 进行类型转换,返回结果
            Object result = beanFactory.getTypeConverter().convertIfNecessary(beans, dd.getDependencyType());
            System.out.println(result);
        }
    }

    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 Dao<T> {

    }

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

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

    static class Student {

    }

    static class Teacher {

    }

    interface Service {

    }

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

    }

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

    }

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

    }
}

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

/
 * 测试Spring中依赖注入的其余方式
 * 1.通过注解@Primary来确定需要注入的bean
 * 2.通过字段的名称和bean的名称匹配来注入
 *
 * @author lzlg
 * 2023/4/9 21:50
 */
@Configuration
public class TestSpringIOCElse {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestSpringIOCElse.class);
        // Bean工厂
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        // 1.通过@Primary注解选定需要注解的类型
        testPrimary(beanFactory);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 2.通过字段的名称来确定注入的Bean
        testDefaultByFieldName(beanFactory);

        context.close();
    }

    /
     * 通过字段的名称来确定注入的Bean
     */
    private static void testDefaultByFieldName(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        // 2.通过字段的名称来确定注入的Bean
        DependencyDescriptor dd = new DependencyDescriptor(Target2.class.getDeclaredField("service3"), true);
        // 获取类型信息
        Class<?> type = dd.getDependencyType();
        for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
            // 通过比对Bean名称和依赖项字段的名称来确定
            if (name.equals(dd.getDependencyName())) {
                System.out.println(name);
                // 获取Bean
                System.out.println(dd.resolveCandidate(name, type, beanFactory));
            }
        }
    }

    /
     * 注解@Primary确定注入的bean
     */
    private static void testPrimary(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        // 1.通过@Primary注解选定需要注解的类型
        DependencyDescriptor dd = new DependencyDescriptor(Target1.class.getDeclaredField("service"), true);
        // 获取类型信息
        Class<?> type = dd.getDependencyType();
        for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
            // 通过bean定义信息中primary属性判断加了@Primary的类
            if (beanFactory.getMergedBeanDefinition(name).isPrimary()) {
                System.out.println(name);
                // 获取Bean
                System.out.println(dd.resolveCandidate(name, type, beanFactory));
            }
        }
    }

    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 {

    }
}

8.Spring Boot事件

事件监听器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/
 * 测试Spring事件监听器原理
 * 通过两种方式实现事件发布
 * 1.实现ApplicationListener接口,并加入到context中
 * 2.使用注解@EventListener
 *
 * @author lzlg
 * 2023/4/10 20:04
 */
@Configuration
public class TestSpringEventListener {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
                TestSpringEventListener.class);
        MyService myService = context.getBean(MyService.class);
        myService.doBusiness();

        context.close();
    }

    /
     * 自定义事件
     */
    static class MyEvent extends ApplicationEvent {

        public MyEvent(Object source) {
            // source是事件消息
            super(source);
        }
    }

    @Component
    static class MyService {

        @Autowired
        // 容器refresh()方法中的prepareBeanFactory方法已经加入到容器中,故可注入
        private ApplicationEventPublisher publisher;

        /
         * 业务方法
         */
        public void doBusiness() {
            System.out.println("执行业务方法: doBusiness()...");
            publisher.publishEvent(new MyEvent("开始执行发送短信/邮件的业务..."));
//            System.out.println("发送短信...");
//            System.out.println("发送邮件...");
        }
    }

    /
     * 发送短信的监听器
     */
    @Component
    static class SmsListener implements ApplicationListener<MyEvent> {

        @Override
        public void onApplicationEvent(MyEvent event) {
            System.out.println(event.getSource());
            System.out.println("发送短信...");
        }
    }

    /
     * 发送邮件的监听器
     */
    @Component
    static class EmailListener implements ApplicationListener<MyEvent> {

        @Override
        public void onApplicationEvent(MyEvent event) {
            System.out.println(event.getSource());
            System.out.println("发送邮件...");
        }
    }

}

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.concurrent.ThreadPoolExecutor;

/
 * 测试Spring事件监听器原理
 * 通过两种方式实现事件发布
 * 1.实现ApplicationListener接口,并加入到context中
 * 2.使用注解@EventListener
 *
 * @author lzlg
 * 2023/4/10 20:04
 */
@Configuration
public class TestSpringAnnotationEventListener {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
                TestSpringAnnotationEventListener.class);
        MyService myService = context.getBean(MyService.class);
        myService.doBusiness();

        context.close();
    }

    /
     * 自定义线程池
     */
    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {
        // 自定义线程池
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(20);
        // 任务超出极限的处理策略,调用线程池的线程处理
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    /
     * Spring Boot中的默认事件发布器,方法名需为applicationEventMulticaster
     */
    @Bean
    public SimpleApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor taskExecutor) {
        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
        // 配置线程池
        multicaster.setTaskExecutor(taskExecutor);
        return multicaster;
    }


    /
     * 自定义事件
     */
    static class MyEvent extends ApplicationEvent {

        public MyEvent(Object source) {
            // source是事件消息
            super(source);
        }
    }

    @Component
    static class MyService {

        @Autowired
        // 容器refresh()方法中的prepareBeanFactory方法已经加入到容器中,故可注入
        private ApplicationEventPublisher publisher;

        /
         * 业务方法
         */
        public void doBusiness() {
            System.out.println("执行业务方法: doBusiness()...");
            publisher.publishEvent(new MyEvent("开始执行发送短信/邮件的业务..."));
//            System.out.println("发送短信...");
//            System.out.println("发送邮件...");
        }
    }

    /
     * 发送短信的监听器
     */
    @Component
    static class SmsService {
        @EventListener
        public void sms(MyEvent myEvent) {
            System.out.println(Thread.currentThread().getName() + "\t" + myEvent.getSource());
            System.out.println(Thread.currentThread().getName() + "\t" + "发送短信...");
        }
    }

    /
     * 发送邮件的监听器
     */
    @Component
    static class EmailService {
        @EventListener
        public void email(MyEvent myEvent) {
            System.out.println(Thread.currentThread().getName() + "\t" + myEvent.getSource());
            System.out.println(Thread.currentThread().getName() + "\t" + "发送邮件...");
        }
    }

}

事件发布器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.ResolvableType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/
 * 测试Spring中的事件发布器
 *
 * @author lzlg
 * 2023/4/10 20:47
 */
@Configuration
public class TestSpringEventPublisher {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
                TestSpringEventPublisher.class);
        // 调用业务方法
        MyService myService = context.getBean(MyService.class);
        myService.doBusiness();
        context.close();
    }

    /
     * 注入自己定义的事件发布器
     */
    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster(ConfigurableApplicationContext context,
                                                                   ThreadPoolTaskExecutor taskExecutor) {
        return new AbstractApplicationEventMulticaster() {
            // 监听器的集合
            private List<GenericApplicationListener> listeners = new ArrayList<>();

            // 添加监听器的方法
            @Override
            public void addApplicationListenerBean(String listenerBeanName) {
                // Spring会把所有实现ApplicationListener接口的监听器的名称返回
                System.out.println(listenerBeanName);
                // 根据Bean名称获取监听器
                ApplicationListener listener = context.getBean(listenerBeanName, ApplicationListener.class);
                // 获取Bean实例的接口中的泛型(事件类型)
                ResolvableType type = ResolvableType.forClass(listener.getClass())
                        .getInterfaces()[0].getGeneric();
                System.out.println(type);

                // 将ApplicationListener封装为GenericApplicationListener,更好通过事件类型进行调用
                GenericApplicationListener genericListener = new GenericApplicationListener() {
                    /
                     * 判断是否是支持的事件类型
                     */
                    @Override
                    public boolean supportsEventType(ResolvableType eventType) {

                        // 如果右边的类型可赋给监听器中的泛型事件类型,则此监听器生效
                        return type.isAssignableFrom(eventType);
                    }

                    /
                     * 事件处理方法
                     */
                    @Override
                    public void onApplicationEvent(ApplicationEvent event) {
                        // 异步执行调用
                        taskExecutor.execute(() -> listener.onApplicationEvent(event));

                    }
                };
                listeners.add(genericListener);
            }

            // 调用监听器的方法
            @Override
            public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
                // 遍历所有的监听器
                for (GenericApplicationListener listener : listeners) {
                    // 判断是否支持的事件类型,通过事件event的类型
                    if (listener.supportsEventType(ResolvableType.forClass(event.getClass()))) {
                        // 是支持的事件类型才进行回调
                        listener.onApplicationEvent(event);
                    }
                }
            }
        };
    }

    @Component
    static class MyService {
        @Autowired
        private ApplicationEventPublisher publisher;

        public void doBusiness() {
            System.out.println(Thread.currentThread().getName() + "\t" + "执行主线业务");
            // 发布事件
            publisher.publishEvent(new MyEvent("开始执行发送短信/邮件的业务..."));
        }
    }

    @Component
    static class SmsApplicationListener implements ApplicationListener<MyEvent> {
        @Override
        public void onApplicationEvent(MyEvent event) {
            System.out.println(Thread.currentThread().getName() + "\t" + event.getSource());
            System.out.println(Thread.currentThread().getName() + "\t" + "发送短信...");
        }
    }

    @Component
    static class EmailApplicationListener implements ApplicationListener<MyEvent> {
        @Override
        public void onApplicationEvent(MyEvent event) {
            System.out.println(Thread.currentThread().getName() + "\t" + event.getSource());
            System.out.println(Thread.currentThread().getName() + "\t" + "发送邮件...");
        }
    }

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

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

}

自定义事件监听器

import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

/
 * 实现自定义的事件监听器
 *
 * @author lzlg
 * 2023/4/10 20:27
 */
@Configuration
public class TestSpringMyEventListener {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
                TestSpringMyEventListener.class);

        // 测试只在一个监听类上的监听方法
//        testSmsListener(context);

        // 测试所有的监听器
//        testAllEventListener(context);

        // 调用业务方法
        MyService myService = context.getBean(MyService.class);
        myService.doBusiness();
        context.close();
    }

    /
     * 将监听器的逻辑封装在SmartInitializingSingleton中,这样通过后置处理器可调用到
     * 是EventListenerMethodProcessor
     * 在bean初始化完成后进行调用的
     */
    @Bean
    public SmartInitializingSingleton smartInitializingSingleton(ConfigurableApplicationContext context) {
        return () -> {
            // 遍历所有的Bean
            for (String beanName : context.getBeanDefinitionNames()) {
                // 获取Bean对象
                Object bean = context.getBean(beanName);
                // 遍历Bean上的所有方法
                for (Method method : bean.getClass().getMethods()) {
                    // 看方法上是否有注解@MyEventListener
                    if (method.isAnnotationPresent(MyEventListener.class)) {
                        // 如果有,则创建监听器,并加入到Spring容器中
                        ApplicationListener listener = event -> {
                            // 需要分辨事件是否是需要监听的事件
                            Class<?> parameterClass = method.getParameterTypes()[0];
                            // 如果参数上的类能和event中的类匹配,才进行反射调用
                            if (parameterClass.isAssignableFrom(event.getClass())) {
                                // 反射调用监听方法
                                try {
                                    method.invoke(bean, event);
                                } catch (Exception e) {
                                    throw new RuntimeException(e);
                                }
                            }

                        };
                        context.addApplicationListener(listener);
                    }
                }
            }
        };
    }

    /
     * 测试所有的监听器
     */
    private static void testAllEventListener(AnnotationConfigApplicationContext context) {
        // 遍历所有的Bean
        for (String beanName : context.getBeanDefinitionNames()) {
            // 获取Bean对象
            Object bean = context.getBean(beanName);
            // 遍历Bean上的所有方法
            for (Method method : bean.getClass().getMethods()) {
                // 看方法上是否有注解@MyEventListener
                if (method.isAnnotationPresent(MyEventListener.class)) {
                    // 如果有,则创建监听器,并加入到Spring容器中
                    ApplicationListener listener = event -> {
                        // 需要分辨事件是否是需要监听的事件
                        Class<?> parameterClass = method.getParameterTypes()[0];
                        // 如果参数上的类能和event中的类匹配,才进行反射调用
                        if (parameterClass.isAssignableFrom(event.getClass())) {
                            // 反射调用监听方法
                            try {
                                method.invoke(bean, event);
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        }

                    };
                    context.addApplicationListener(listener);
                }
            }
        }
    }

    /
     * 测试只在一个监听类上的监听方法
     */
    private static void testSmsListener(AnnotationConfigApplicationContext context) {
        // 遍历SmsService上的所有方法
        for (Method method : SmsService.class.getMethods()) {
            // 看方法上是否有注解@MyEventListener
            if (method.isAnnotationPresent(MyEventListener.class)) {
                SmsService service = context.getBean(SmsService.class);
                // 如果有,则创建监听器,并加入到Spring容器中
                ApplicationListener listener = new ApplicationListener() {
                    @Override
                    public void onApplicationEvent(ApplicationEvent event) {
                        // 需要分辨事件是否是需要监听的事件
                        Class<?> parameterClass = method.getParameterTypes()[0];
                        System.out.println(parameterClass);
                        // 如果参数上的类能和event中的类匹配,才进行反射调用
                        if (parameterClass.isAssignableFrom(event.getClass())) {
                            // 反射调用监听方法
                            try {
                                method.invoke(service, event);
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        }

                    }
                };
                context.addApplicationListener(listener);
            }
        }
    }

    @Component
    static class MyService {
        @Autowired
        private ApplicationEventPublisher publisher;

        public void doBusiness() {
            System.out.println(Thread.currentThread().getName() + "\t" + "执行业务方法...");
            publisher.publishEvent(new MyEvent("开始执行发送短信/邮件的业务..."));
        }
    }

    @Component
    static class SmsService {

        @MyEventListener
        public void sms(MyEvent myEvent) {
            System.out.println(Thread.currentThread().getName() + "\t" + myEvent.getSource());
            System.out.println(Thread.currentThread().getName() + "\t" + "发送短信...");
        }
    }

    @Component
    static class EmailService {

        @MyEventListener
        public void email(MyEvent myEvent) {
            System.out.println(Thread.currentThread().getName() + "\t" + myEvent.getSource());
            System.out.println(Thread.currentThread().getName() + "\t" + "发送邮件...");
        }
    }

    /
     * 自定义事件监听器注解
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyEventListener {

    }

    /
     * 自定义事件
     */
    static class MyEvent extends ApplicationEvent {

        public MyEvent(Object source) {
            super(source);
        }
    }
}

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.core.ResolvableType;

import java.util.function.Predicate;

/
 * 抽象的事件发布器
 *
 * @author lzlg
 * 2023/4/10 20:58
 */
public 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) {

    }
}
Spring Boot
程序员内功
码出好代码
  • 作者:lzlg520
  • 发表时间:2023-04-11 17:45
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 公众号转载:请在文末添加作者公众号二维码