aoe1231

看得完吗

Spring高级 - 第3部分

17、SpringBoot 启动流程

17.1、Boot 代码示例

public class Test1_1 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//        ConfigurableApplicationContext context = SpringApplication.run(Test1_1.class, args);
        // 上面调用的 SpringApplication.run() 中做了如下几件事:
        // 1、演示 获取 Bean Definition 源
        System.out.println("---------------------------------");
        System.out.println("1、演示获取 Bean Definition 源");
        SpringApplication springApp = new SpringApplication(Test1_1.class);
//        springApp.setSources(Collections.singleton("classpath:b01.xml")); // 添加 .xml 的来源
        // 2、演示 推断应用类型
        System.out.println("---------------------------------");
        System.out.println("2、演示推断应用类型");
        Method deduceFromClasspathMethod = WebApplicationType.class.getDeclaredMethod("deduceFromClasspath"); // 静态方法
        deduceFromClasspathMethod.setAccessible(true);
        System.out.println("\t应用类型为:" + deduceFromClasspathMethod.invoke(null));
        // 3、演示 ApplicationContext 初始化器
        System.out.println("---------------------------------");
        System.out.println("3、演示 ApplicationContext 初始化器");
        // 添加 自定义初始化器
        springApp.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
            // 方法参数:刚刚创建,但尚未 refresh() 的 ApplicationContext
            @Override
            public void initialize(ConfigurableApplicationContext applicationContext) {
                if (applicationContext instanceof GenericApplicationContext) {
                    GenericApplicationContext context = (GenericApplicationContext) applicationContext;
                    context.registerBean("bean3", Bean3.class);
                }
            }
        });
        // 4、演示 监听器与事件
        System.out.println("---------------------------------");
        System.out.println("4、演示监听器与事件");
        springApp.addListeners(new ApplicationListener<ApplicationEvent>() {
            @Override
            public void onApplicationEvent(ApplicationEvent event) {
                System.out.println("\t事件为:" + event.getClass());
            }
        });
        // 5、演示 主类推断
        System.out.println("---------------------------------");
        System.out.println("5、演示主类推断");
        Method deduceMainApplicationClassMethod = SpringApplication.class.getDeclaredMethod("deduceMainApplicationClass");
        deduceMainApplicationClassMethod.setAccessible(true);
        System.out.println("\t主类是:" + deduceMainApplicationClassMethod.invoke(springApp));

        // 创建 ApplicationContext
        ConfigurableApplicationContext context = springApp.run(args);
        // 调用 初始化器 对 ApplicationContext 做扩展
        // ApplicationContext.refresh()

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println("[ bean name: " + beanDefinitionName +
                    "] ----> [ 来源:" + context.getBeanFactory().getBeanDefinition(beanDefinitionName).getResourceDescription()
                    + " ]"
            );
        }

        context.close();
    }

    static class Bean1 {
    }

    static class Bean2 {
    }

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

    static class Bean3 {
    }

    // 补充 web 容器工厂
    @Bean
    public TomcatServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }
}

结果:
---------------------------------
1、演示获取 Bean Definition 源
---------------------------------
2、演示推断应用类型
	应用类型为:SERVLET
---------------------------------
3、演示 ApplicationContext 初始化器
---------------------------------
4、演示监听器与事件
---------------------------------
5、演示主类推断
	主类是:class com.clp.test1.Test1_1
	事件为:class org.springframework.boot.context.event.ApplicationStartingEvent
	事件为:class org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
......
	事件为:class org.springframework.boot.context.event.ApplicationContextInitializedEvent
......
	事件为:class org.springframework.boot.context.event.ApplicationPreparedEvent
......
	事件为:class org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent
	事件为:class org.springframework.context.event.ContextRefreshedEvent
......
	事件为:class org.springframework.boot.context.event.ApplicationStartedEvent
	事件为:class org.springframework.boot.availability.AvailabilityChangeEvent
	事件为:class org.springframework.boot.context.event.ApplicationReadyEvent
	事件为:class org.springframework.boot.availability.AvailabilityChangeEvent
[ bean name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor] ----> [ 来源:null ]
[ bean name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor] ----> [ 来源:null ]
[ bean name: org.springframework.context.annotation.internalCommonAnnotationProcessor] ----> [ 来源:null ]
[ bean name: org.springframework.context.event.internalEventListenerProcessor] ----> [ 来源:null ]
[ bean name: org.springframework.context.event.internalEventListenerFactory] ----> [ 来源:null ]
[ bean name: bean3] ----> [ 来源:null ]
[ bean name: test1_1] ----> [ 来源:null ]
[ bean name: org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory] ----> [ 来源:null ]
[ bean name: bean2] ----> [ 来源:com.clp.test1.Test1_1 ]
[ bean name: servletWebServerFactory] ----> [ 来源:com.clp.test1.Test1_1 ]
	事件为:class org.springframework.boot.availability.AvailabilityChangeEvent
	事件为:class org.springframework.context.event.ContextClosedEvent
......

17.2、Boot 启动过程

1、创建 SpringApplication 对象:

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

2、执行 run() 方法:

  1. 得到 SpringApplicationRunListeners,名字取得不好,实际是事件发布器,发布 application starting 事件;
  2. 封装启动 args;
  3. 准备 Environment 添加命令行参数 (*);
  4. ConfigurationPropertySources 处理 (*):发布 application environment 已准备 事件;
  5. 通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理 (*):① application.properties,由 StandardConfigDataLocationResolver 解析;② spring.application.json。
  6. 绑定 spring.main 到 SpringApplication 对象 (*)。

代码演示1:

public class Test1_2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
            InstantiationException, IllegalAccessException {
        // 添加 app 监听器
        SpringApplication springApp = new SpringApplication();
        springApp.addListeners(e -> System.out.println(e.getClass()));
        
        // 获取事件发布器(SpringApplicationRunListener)的 实现类名
        List<String> names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, Test1_2.class.getClassLoader());
        for (String name : names) {
            System.out.println(name);
            Class<?> clazz = Class.forName(name);
            Constructor<?> constructor = clazz.getConstructor(SpringApplication.class, String[].class);
            SpringApplicationRunListener publisher = (SpringApplicationRunListener) constructor.newInstance(springApp, args);

            // 发布事件
            DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
            publisher.starting(bootstrapContext); // spring boot 开始启动
            publisher.environmentPrepared(bootstrapContext, new StandardEnvironment()); // 当环境信息准备完毕
            GenericApplicationContext applicationContext = new GenericApplicationContext();
            publisher.contextPrepared(applicationContext); // 在 spring 容器创建,并调用初始化器之后,发布此事件
            publisher.contextLoaded(applicationContext); // 所有 bean definition 加载完毕,发布此事件
            applicationContext.refresh();
            publisher.started(applicationContext); // spring 容器初始化完成(refresh()调用后)
            publisher.failed(applicationContext, new Exception("出错了")); // spring boot 启动过程中出现错误,发布此事件
            publisher.running(applicationContext); // spring boot 启动完毕
        }
    }
}

org.springframework.boot.context.event.EventPublishingRunListener
class org.springframework.boot.context.event.ApplicationStartingEvent
class org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
class org.springframework.boot.context.event.ApplicationContextInitializedEvent
class org.springframework.boot.context.event.ApplicationPreparedEvent
class org.springframework.context.event.ContextRefreshedEvent
class org.springframework.boot.context.event.ApplicationFailedEvent

代码演示2:

/**
 * springApplication.run() 方法的执行流程
 */
public class Test1_3 {

    @SuppressWarnings("all")
    public static void main(String[] args) throws Exception {
        SpringApplication app = new SpringApplication();
        System.out.println(">>>>>>>>>>>>>>>>>>>> 1、获取事件发布器,添加初始化器");
        EventPublishingRunListener publisher = new EventPublishingRunListener(app, args); // 事件发布器
        app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
            @Override
            public void initialize(ConfigurableApplicationContext applicationContext) {
                System.out.println("执行初始化器增强...");
            }
        });

        System.out.println(">>>>>>>>>>>>>>>>>>>> 2、封装启动命令行参数 args,并作为PropertySource添加到环境变量中");
        DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);

        System.out.println(">>>>>>>>>>>>>>>>>>>> 3、封装环境变量(添加PropertySource)");
        // 属性源的属性查找根据属性源加入的先后顺序查找,如命令行来源优先于application.properties来源
        ApplicationEnvironment env = new ApplicationEnvironment();
        for (PropertySource<?> propertySource : env.getPropertySources()) {
            // PropertiesPropertySource、SystemEnvironmentPropertySource ...
            System.out.println(propertySource.getClass().getSimpleName() + " {name='" + propertySource.getName() + "'}");
        }
        System.out.println(env.getProperty("JAVA_HOME")); // 打印: D:\jdks\jdk1.8
        // 添加属性源 - 系统环境变量
        env.getPropertySources().addFirst(new SimpleCommandLinePropertySource(args)); // 命令行来源

        System.out.println(">>>>>>>>>>>>>>>>>>>> 4、添加特殊的PropertySource");
        ConfigurationPropertySources.attach(env); // 添加 ConfigurationPropertySources ,统一不规范的名字
        for (PropertySource<?> propertySource : env.getPropertySources()) {
            System.out.println(propertySource.getClass().getSimpleName() + " {name='" + propertySource.getName() + "'}");
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>> 5、进一步补充新的PropertySource (通过EnvironmentPostProcessor)");
        System.out.println("增强前...");
        for (PropertySource<?> propertySource : env.getPropertySources()) {
            System.out.println(propertySource.getClass().getSimpleName() + " {name='" + propertySource.getName() + "'}");
        }
        // 当第4步的属性源环境加载完毕后会触发下面的事件(通过事件发布器)
        // 事件发布器,发布事件后会调用对应的监听器,监听器会从 spring.factories 中加载 EnvironmentPostProcessor 实现
        app.addListeners(new EnvironmentPostProcessorApplicationListener()); // 添加 环境准备好 事件的监听器
        publisher.environmentPrepared(new DefaultBootstrapContext(), env); // 发布事件
        // 下面注释的代码就是上面的大致过程:
//        env.getPropertySources().addLast(new ResourcePropertySource(new ClassPathResource("application.properties"))); // application.properties 来源
//        // processor1 补充 application.properties 的 PropertySource
//        ConfigDataEnvironmentPostProcessor processor1 = new ConfigDataEnvironmentPostProcessor(new DeferredLogs(), new DefaultBootstrapContext());
//        processor1.postProcessEnvironment(env, app);
//        // processor2 补充 随机属性值的 PropertySource
//        RandomValuePropertySourceEnvironmentPostProcessor processor2 = new RandomValuePropertySourceEnvironmentPostProcessor(new DeferredLog());
//        processor2.postProcessEnvironment(env, app); // env.getProperty("random.int") 获取随机整数
//        System.out.println("----- spring.factories...");
//        // 一般 EnvironmentPostProcessor 接口的实现类不写死在代码中,而是写在 spring.factories 中,通过 SpringFactoriesLoader 加载
//        List<String> names = SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, Test1_3.class.getClassLoader());
//        for (String name : names) {
//            System.out.println(name);
//        }
        System.out.println("增强后...");
        for (PropertySource<?> propertySource : env.getPropertySources()) {
            System.out.println(propertySource.getClass().getSimpleName() + " {name='" + propertySource.getName() + "'}");
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>> 6、将 环境属性、配置文件 中的键值绑定到对象属性中");
        /**
         * resources/user.properties 内容:
         *  user.first-name=George
         *  user.middle-name=Walker
         *  user.last-name=Bush
         */
        // 即 @ConfigurationProperties 相同的功能
        env.getPropertySources().addLast(new ResourcePropertySource("user", new ClassPathResource("user.properties")));
        BindResult<User> bindResult = Binder.get(env).bind("user", User.class); // 可以处理不规范命名
        System.out.println(bindResult.get());
//        // 也可以绑定到已有的对象
//        User user = new User();
//        BindResult<User> bindResult = Binder.get(env).bind("user", Bindable.ofInstance(user));
//        System.out.println(bindResult);
//        // 可以通过该方式修改环境属性值,比如 SpringApplication 中的属性
//        // step6.properties : spring.main.banner-mode=off
//        env.getPropertySources().addLast(new ResourcePropertySource("step6", new ClassPathResource("step6.properties")));
//        BindResult<SpringApplication> bindResult = Binder.get(env).bind("spring.main", Bindable.ofInstance(app));
//        System.out.println(bindResult);

        System.out.println(">>>>>>>>>>>>>>>>>>>> 7、输出 SpringBoot 的 banner 信息");
        SpringApplicationBannerPrinter printer = new SpringApplicationBannerPrinter(new DefaultResourceLoader(), new SpringBootBanner());
        // 测试文字banner
        env.getPropertySources().addLast(new MapPropertySource("custom", Collections.singletonMap("spring.banner.location", "banner1.txt")));
//        // 测试图片banner
//        env.getPropertySources().addLast(new MapPropertySource("custom", Collections.singletonMap("spring.banner.image.location", "banner2.jpg")));
        // 版本号的获取
        System.out.println(SpringBootVersion.getVersion());
        printer.print(env, Test1_3.class, System.out);

        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 定义");
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(context.getDefaultListableBeanFactory()); // 注解来源
//        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context.getDefaultListableBeanFactory()); // xml 来源
//        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context.getDefaultListableBeanFactory()); // 组件扫描来源
        reader.register(Config.class); // 解析 Config 配置类里的 bean 定义并加入 bean 工厂

        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 对象的 run()方法");
        for (CommandLineRunner commandLineRunner : context.getBeansOfType(CommandLineRunner.class).values()) {
            commandLineRunner.run(args);
        }
        for (ApplicationRunner applicationRunner : context.getBeansOfType(ApplicationRunner.class).values()) {
            applicationRunner.run(arguments);
        }

    }

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

    static class Bean4 {}

    static class Bean5 {}

    static class Bean6 {}


    @Data
    static class User {
        private String firstName;
        private String middleName;
        private String lastName;
    }

    @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()));
                }
            };
        }
    }
}

结果:
>>>>>>>>>>>>>>>>>>>> 1、获取事件发布器,添加初始化器
>>>>>>>>>>>>>>>>>>>> 2、封装启动命令行参数 args,并作为PropertySource添加到环境变量中
>>>>>>>>>>>>>>>>>>>> 3、封装环境变量(添加PropertySource)
PropertiesPropertySource {name='systemProperties'}
SystemEnvironmentPropertySource {name='systemEnvironment'}
......
D:\jdks\jdk1.8
>>>>>>>>>>>>>>>>>>>> 4、添加特殊的PropertySource
ConfigurationPropertySourcesPropertySource {name='configurationProperties'}
SimpleCommandLinePropertySource {name='commandLineArgs'}
PropertiesPropertySource {name='systemProperties'}
SystemEnvironmentPropertySource {name='systemEnvironment'}
>>>>>>>>>>>>>>>>>>>> 5、进一步补充新的PropertySource (通过EnvironmentPostProcessor)
增强前...
ConfigurationPropertySourcesPropertySource {name='configurationProperties'}
SimpleCommandLinePropertySource {name='commandLineArgs'}
PropertiesPropertySource {name='systemProperties'}
SystemEnvironmentPropertySource {name='systemEnvironment'}
......
增强后...
ConfigurationPropertySourcesPropertySource {name='configurationProperties'}
SimpleCommandLinePropertySource {name='commandLineArgs'}
PropertiesPropertySource {name='systemProperties'}
OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}
RandomValuePropertySource {name='random'}
OriginTrackedMapPropertySource {name='Config resource 'class path resource [application.properties]' via location 'optional:classpath:/''}
>>>>>>>>>>>>>>>>>>>> 6、将 环境属性、配置文件 中的键值绑定到对象属性中
Test1_3.User(firstName=George, middleName=Walker, lastName=Bush)
>>>>>>>>>>>>>>>>>>>> 7、输出 SpringBoot 的 banner 信息
2.6.13
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> this is a banner >>>>>>>>>>>>>>>>
?????????????????????????????????????????????????

>>>>>>>>>>>>>>>>>>>>  8、创建容器
>>>>>>>>>>>>>>>>>>>>  9、准备容器(会执行添加的初始化器)
执行初始化器增强...
>>>>>>>>>>>>>>>>>>>>  10、从各种来源加载 bean 定义
>>>>>>>>>>>>>>>>>>>>  11、调用 refresh()
......
name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor 来源:null
name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor 来源:null
name: org.springframework.context.annotation.internalCommonAnnotationProcessor 来源:null
name: org.springframework.context.event.internalEventListenerProcessor 来源:null
name: org.springframework.context.event.internalEventListenerFactory 来源:null
name: test1_3.Config 来源:null
name: org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory 来源:null
name: bean5 来源:org.springframework.boot.Test1_3$Config
name: servletWebServerFactory 来源:org.springframework.boot.Test1_3$Config
name: commandLineRunner 来源:org.springframework.boot.Test1_3$Config
name: applicationRunner 来源:org.springframework.boot.Test1_3$Config
>>>>>>>>>>>>>>>>>>>>  12、执行 runner 对象的 run()方法
commandLineRunner()...[]
applicationRunner()...[]

18、Tomcat 内嵌容器

18.1、内嵌容器的基本使用

代码演示:

/**
 *  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
 *                              classes
 *                              jsp
 *                              lib
 *                      ......
 */
public class TestTomcat {
    public static void main(String[] args) throws IOException, LifecycleException {
        // 1、创建 Tomcat 对象
        Tomcat tomcat = new Tomcat();
        tomcat.setBaseDir("tomcat"); // 相对于 工作目录
        // 2、创建项目文件夹,即 docBase 文件夹
        File docBase = Files.createTempDirectory("boot.").toFile();
        docBase.deleteOnExit();
        // 3、创建 Tomcat 项目,在 Tomcat 中称为 Context
        Context context = tomcat.addContext("", docBase.getAbsolutePath());
        // 4、编程方式添加 Servlet
        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
                HelloServlet helloServlet = new HelloServlet();
                ctx.addServlet("aaa", helloServlet).addMapping("/hello"); // 设置servlet名称、映射路径
            }
        }, Collections.emptySet());
        // 5、启动 Tomcat
        tomcat.start();
        // 6、创建连接器,设置监听端口
        Connector connector = new Connector(new Http11Nio2Protocol());
        connector.setPort(8080);
        tomcat.setConnector(connector);
    }
}

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().print(
                "<h3>hello</h3>");
    }
}

结果(访问 localhost:8080/hello):
hello

18.2、内嵌 Tomcat 与 Spring 整合

代码演示:

public class TestTomcat {
    public static void main(String[] args) throws IOException, LifecycleException {
        // 1、创建 Tomcat 对象
        Tomcat tomcat = new Tomcat();
        tomcat.setBaseDir("tomcat"); // 相对于 工作目录
        // 2、创建项目文件夹,即 docBase 文件夹
        File docBase = Files.createTempDirectory("boot.").toFile();
        docBase.deleteOnExit();
        // 3、创建 Tomcat 项目,在 Tomcat 中称为 Context
        Context context = tomcat.addContext("", docBase.getAbsolutePath());
        // 创建 Spring 容器
        WebApplicationContext springContext = getApplicationContext();
        // 4、编程方式添加 Servlet
        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
                // 手动添加 HelloServlet
                HelloServlet helloServlet = new HelloServlet();
                ctx.addServlet("aaa", helloServlet).addMapping("/hello"); // 设置servlet名称、映射路径
//                // 添加 DispatcherServlet
//                DispatcherServlet dispatcherServlet = springContext.getBean(DispatcherServlet.class);
//                ctx.addServlet("dispatcherServlet", dispatcherServlet).addMapping("/");
                // 上面的添加实际上是通过 ServletRegistrationBean 给 context 注册 servlet 的
                for (ServletRegistrationBean registrationBean : springContext.getBeansOfType(ServletRegistrationBean.class).values()) {
                    registrationBean.onStartup(ctx);
                }
            }
        }, Collections.emptySet());
        // 5、启动 Tomcat
        tomcat.start();
        // 6、创建连接器,设置监听端口
        Connector connector = new Connector(new Http11Nio2Protocol());
        connector.setPort(8080);
        tomcat.setConnector(connector);
    }

    public static WebApplicationContext getApplicationContext() {
//        AnnotationConfigServletWebServerApplicationContext : 支持内嵌 Tomcat 的 Spring 容器
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); // 不支持内嵌 Tomcat 的 容器,方便我们使用自己的 tomcat
        context.register(Config.class);
        context.refresh();
        return context;
    }

    @Configuration
    static class Config {
        @Bean
        // 该注册bean用于注册 DispatcherServlet
        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.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
            return handlerAdapter;
        }

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

结果(访问 localhost:8080/hello2):
hello2:"hello2, spring!"

19、自动配置类

19.1、自动配置类原理

代码演示:

spring.factories 内容:
    com.clp.test3.Test3$MyImportSelector=\
      com.clp.test3.Test3.AutoConfiguration1,\
      com.clp.test3.Test3.AutoConfiguration2

public class Test3 {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        // 设置是否允许覆盖bean (设为true:后注册的bean会覆盖先注册的bean,设为false:不允许覆盖)
        context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(context.getBean(Bean1.class));
    }

    // 本项目的配置类
    @Configuration
    @Import(MyImportSelector.class)
    static class Config {
//        @Bean // 使用第三方自动配置(@ConditionalOnMissingBean注解生效)
        public Bean1 bean1() {
            return new Bean1("本项目bean1");
        }
    }

    // 实现 ImportSelector 会 先解析 这个并导入,自己定义的bean后添加
    // DeferredImportSelector 是 ImportSelector 的子接口,实现该接口不会先解析导入这个,而是先添加自己定义的bean
    static class MyImportSelector implements DeferredImportSelector {
        // 方法的返回值就是要导入的配置类的类名
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            List<String> classNames = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
            return classNames.toArray(new String[0]);
        }
    }

    // 第三方配置类1
    @Configuration
    static class AutoConfiguration1 {
        @Bean
        @ConditionalOnMissingBean // 当缺失了这个bean则添加
        public Bean1 bean1() {
            return new Bean1("第三方bean1");
        }
    }

    @Data
    @AllArgsConstructor
    static class Bean1 {
        private String name;
    }

    // 第三方配置类2
    @Configuration
    static class AutoConfiguration2 {

    }

    static class Bean2 {}
}

结果:
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
com.clp.test3.Test3$AutoConfiguration1
bean1
com.clp.test3.Test3$AutoConfiguration2
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Test3.Bean1(name=第三方bean1)

19.2、AopAutoConfiguration

代码演示:

public class Test4 {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        StandardEnvironment env = new StandardEnvironment();
        // 模拟命令行属性源
        env.getPropertySources().addLast(new SimpleCommandLinePropertySource("--spring.aop.auto=true"));
        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(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        // 最终目的是创建并添加 代理创建器 bean
        InfrastructureAdvisorAutoProxyCreator creator = context.getBean(
                "org.springframework.aop.config.internalAutoProxyCreator", InfrastructureAdvisorAutoProxyCreator.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()};
        }
    }
}

结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
com.clp.test4.Test4$Config
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$ClassProxyingConfiguration
forceAutoProxyCreatorToUseClassProxying
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.aop.config.internalAutoProxyCreator
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
true

19.3、DataSource 自动配置

代码演示:

@SpringBootApplication
public class Test4_2 {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        StandardEnvironment env = new StandardEnvironment();
        // 模拟命令行属性源
        env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
                "--spring.datasource.url=jdbc:mysql://localhost:3306/test",
                "--spring.datasource.username=root",
                "--spring-datasource=password=123456"
        ));
        context.setEnvironment(env);
        // 利用注册工具向bean工厂注册常用的后处理器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
        context.registerBean(Config.class);

        // Mybatis 自动配置类要求加 @Mapper 注解
        String packageName = Test4_2.class.getPackage().getName();
        AutoConfigurationPackages.register(context.getDefaultListableBeanFactory(), packageName); // 在本类的包以及子包扫描 mapper(加@Mapper的接口)
        System.out.println("当前包名:" + packageName);
        
        context.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
            if (resourceDescription != null) {
                System.out.println(name + " 来源:" + resourceDescription);
            }
        }
    }

    @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(), // mybatis 相关bean 自动配置
                    DataSourceTransactionManagerAutoConfiguration.class.getName(), // 数据源事务管理器 自动配置
                    TransactionAutoConfiguration.class.getName() // 声明式事务管理 自动配置
            };
        }
    }
}

19.4、MVC 自动配置

代码演示:

public class Test4_3 {
    public static void main(String[] args) {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
        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);
            }
        }
        context.close();
    }

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

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

20、条件装配底层

代码演示:

public class Test5 {
    public static void main(String[] args) {
        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 {
    }

    // 实现 ImportSelector 会 先解析 这个并导入,自己定义的bean后添加
    // DeferredImportSelector 是 ImportSelector 的子接口,实现该接口不会先解析导入这个,而是先添加自己定义的bean
    static class MyImportSelector implements DeferredImportSelector {
        // 方法的返回值就是要导入的配置类的类名
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{
                    AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()
            };
        }
    }

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

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

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Conditional(OnClassCondition.class)
    @interface ConditionOnClass {
        boolean exists(); // true 判断存在; false 判断不存在
        String className(); // 要判断的类的名称
    }

    static class OnClassCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
            String className = attributes.get("className").toString();
            boolean exists = (boolean) attributes.get("exists");
            boolean isPresent = ClassUtils.isPresent(className, null);
            return exists == isPresent;
        }
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    static class Bean1 {
        private String name;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    static class Bean2 {
        private String name;
    }
}

结果:
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
com.clp.test5.Test5$AutoConfiguration2
bean2

21、FactoryBean

代码演示:

/**
 * 一个在 Spring 发展阶段中需要,但目前已经很鸡肋的接口 FactoryBean 的使用要点:
 *      1、它的作用是用制造创建过程较为复杂的产品,如 SqlSessionFactory,但 @Bean 已具备等价功能;
 *      2、使用上较为古怪,一不留神就会用错:
 *          1)被 FactoryBean 创建的产品:
 *              - 会认为创建、依赖注入、Aware 接口回调、前初始化这些都是 FactoryBean 的职责,这些流程都不会走;
 *              - 唯有后初始化的流程会走,也就是产品可以被代理增强;
 *              - 单例的产品不会存储于 BeanFactory 的 singletonObjects 成员中,而是另一个 factoryBeanObjectCache 成员中。
 *          2)按名称去获取时,拿到的是产品对象,名字前面加 & 获取的是工厂对象
 * 目前此接口的实现仍被大量使用,想被全面废弃很难。
 */
@ComponentScan
public class Test6 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test6.class);

        Bean1 bean1 = (Bean1) context.getBean("bean1");
        System.out.println(bean1);
        Bean1FactoryBean bean1FactoryBean = (Bean1FactoryBean) context.getBean("&bean1");
        System.out.println(bean1FactoryBean);

        context.close();
    }

    @Slf4j
    @Component("bean1")
    static class Bean1FactoryBean implements FactoryBean<Bean1> {
        @Override // 决定了根据 【类型】 获取或依赖注入能否成功
        public Class<?> getObjectType() {
            return Bean1.class;
        }

        @Override // 决定了 getObject() 方法被调用一次还是多次
        public boolean isSingleton() {
            return true;
        }

        @Override
        public Bean1 getObject() throws Exception {
            Bean1 bean1 = new Bean1();
            log.debug("create bean: {}", bean1);
            return bean1;
        }
    }

    @Slf4j
    static class Bean1 implements BeanFactoryAware {
        private Bean2 bean2;

        @Autowired
        public void setBean2(Bean2 bean2) {}

        public Bean2 getBean2() {
            return bean2;
        }

        @PostConstruct
        public void init() {
            log.debug("init");
        }

        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            log.debug("setBeanFactory({})", beanFactory);
        }
    }

    @Slf4j
    @Component
    static class Bean1PostProcessor implements BeanPostProcessor {
        @Override // 不会走进来
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals("bean1") && bean instanceof Bean1) {
                log.debug("before [{}] init", beanName);
            }
            return bean;
        }

        @Override // 会走进来
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals("bean1") && bean instanceof Bean1) {
                log.debug("after [{}] init", beanName);
            }
            return bean;
        }
    }

    static class Bean2 {}
}

结果:
......
16:26:08.919 [main] DEBUG com.clp.test06.Test6$Bean1FactoryBean - create bean: com.clp.test06.Test6$Bean1@6ee12bac
16:26:08.920 [main] DEBUG com.clp.test06.Test6$Bean1PostProcessor - after [bean1] init
com.clp.test06.Test6$Bean1@6ee12bac
......
com.clp.test06.Test6$Bean1FactoryBean@400cff1a

22、@Indexed 的原理

代码演示:

/**
 * @Indexed 的原理,在编译时就根据 @Indexed 生成 META-INF/spring.components 文件
 * 扫描时:
 *      1、如果发现 META-INF /spring.components 存在,以它为准加载 bean definition
 *      2、否则,会遍历包下所有 class 资源(包括 jar 内的)
 */
public class Test7 {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

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

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

23、Spring 代理的特点

代码演示:

/**
 * 演示 spring 代理的设计特点:
 *      1、依赖注入和初始化影响的是原始对象;
 *      2、代理与目标是两个对象,二者成员变量并不公用数组。因此只能调用代理对象的方法,而不能访问成员变量
 *      3、static、final、private 方法均无法增强
 */
@Slf4j
@SpringBootApplication
public class Test8 {
    public static void main(String[] args) throws Exception {
        log.info("----------------------------------------------------");
        ConfigurableApplicationContext context = SpringApplication.run(Test8.class, args);
        Bean1 proxy = context.getBean(Bean1.class);

        log.info("----------------------------------------------------");
        proxy.setBean2(new Bean2()); // 此时调用代理对象的增强方法

        log.info("----------------------------------------------------");
        showProxyAndTarget(proxy);

        log.info("----------------------------------------------------");
        proxy.publicMethod();
        log.info("----------------------------------------------------");
        proxy.finalMethod();
        log.info("----------------------------------------------------");
        proxy.staticMethod();
        log.info("----------------------------------------------------");
        proxy.privateMethod();

        context.close();
    }

    @Slf4j
    @Aspect
    @Component
    static class MyAspect {
        // 故意对所有方法增强
        @Before("execution(* com.clp.test08.Test8.Bean1.*(..))")
        public void before() {
            log.info("++++++++++ before");
        }
    }

    @Data
    @Slf4j
    @Component
    static class Bean1 {
        protected Bean2 bean2;

        protected boolean isInitialized;

        @Autowired // 没有被增强,但是通过代理对象调用时还是增强方法
        public void setBean2(Bean2 bean2) {
            log.info("---------- setBean2(Bean2 bean2)");
            this.bean2 = bean2;
        }

        @PostConstruct // 没有被增强
        public void init() {
            log.info("---------- init");
            isInitialized = true;
        }

        public void publicMethod() {
            log.info("调用 public 方法");
        }

        final void finalMethod() {
            log.info("调用 final 方法");
        }

        static void staticMethod() {
            log.info("调用 static 方法");
        }

        private void privateMethod() {
            log.info("调用 private 方法");
        }
    }

    @Component
    static class Bean2 {}

    public static void showProxyAndTarget(Bean1 proxy) throws Exception {
        log.info(">>>>>> 代理中的成员变量");
        log.info("\tisInitialized=" + proxy.isInitialized); // 直接通过属性获取值获取的是代理自己的属性值,而自己的属性并不使用
        log.info("\tbean2=" + proxy.bean2);

        if (proxy instanceof Advised) {
            Advised advised = (Advised) proxy;
            log.info(">>>>>> 目标中的成员变量");
            Bean1 target = (Bean1) advised.getTargetSource().getTarget();
            log.info(target.toString());
            log.info("\tinitialized=" + target.isInitialized);
            log.info("\tbean2=" + target.bean2);
        }
    }
}

结果:
17:14:18.377 [main] INFO com.clp.test08.Test8 - ----------------------------------------------------
......
2023-05-16 17:14:19.878  INFO 8648 --- [           main] com.clp.test08.Test8$Bean1               : ---------- setBean2(Bean2 bean2)
2023-05-16 17:14:19.878  INFO 8648 --- [           main] com.clp.test08.Test8$Bean1               : ---------- init
......
2023-05-16 17:14:20.130  INFO 8648 --- [           main] com.clp.test08.Test8                     : ----------------------------------------------------
2023-05-16 17:14:20.132  INFO 8648 --- [           main] com.clp.test08.Test8$MyAspect            : ++++++++++ before
2023-05-16 17:14:20.141  INFO 8648 --- [           main] com.clp.test08.Test8$Bean1               : ---------- setBean2(Bean2 bean2)
2023-05-16 17:14:20.141  INFO 8648 --- [           main] com.clp.test08.Test8                     : ----------------------------------------------------
2023-05-16 17:14:20.141  INFO 8648 --- [           main] com.clp.test08.Test8                     : >>>>>> 代理中的成员变量
2023-05-16 17:14:20.141  INFO 8648 --- [           main] com.clp.test08.Test8                     : 	isInitialized=false
2023-05-16 17:14:20.141  INFO 8648 --- [           main] com.clp.test08.Test8                     : 	bean2=null
2023-05-16 17:14:20.141  INFO 8648 --- [           main] com.clp.test08.Test8                     : >>>>>> 目标中的成员变量
2023-05-16 17:14:20.141  INFO 8648 --- [           main] com.clp.test08.Test8                     : Test8.Bean1(bean2=com.clp.test08.Test8$Bean2@78c1a023, isInitialized=true)
2023-05-16 17:14:20.142  INFO 8648 --- [           main] com.clp.test08.Test8                     : 	initialized=true
2023-05-16 17:14:20.142  INFO 8648 --- [           main] com.clp.test08.Test8                     : 	bean2=com.clp.test08.Test8$Bean2@78c1a023
2023-05-16 17:14:20.142  INFO 8648 --- [           main] com.clp.test08.Test8                     : ----------------------------------------------------
2023-05-16 17:14:20.142  INFO 8648 --- [           main] com.clp.test08.Test8$MyAspect            : ++++++++++ before
2023-05-16 17:14:20.142  INFO 8648 --- [           main] com.clp.test08.Test8$Bean1               : 调用 public 方法
2023-05-16 17:14:20.142  INFO 8648 --- [           main] com.clp.test08.Test8                     : ----------------------------------------------------
2023-05-16 17:14:20.142  INFO 8648 --- [           main] com.clp.test08.Test8$Bean1               : 调用 final 方法
2023-05-16 17:14:20.142  INFO 8648 --- [           main] com.clp.test08.Test8                     : ----------------------------------------------------
2023-05-16 17:14:20.142  INFO 8648 --- [           main] com.clp.test08.Test8$Bean1               : 调用 static 方法
2023-05-16 17:14:20.143  INFO 8648 --- [           main] com.clp.test08.Test8                     : ----------------------------------------------------
2023-05-16 17:14:20.143  INFO 8648 --- [           main] com.clp.test08.Test8$Bean1               : 调用 private 方法
......

24、@Value 注入底层

代码演示:

public class Test9 {
    public static void main(String[] args) throws NoSuchFieldException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test9.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);

        // 获取 @Value 的内容
        System.out.println("-------------------------------------------------------------");
        Field field1 = Bean1.class.getDeclaredField("home");
        DependencyDescriptor descriptor1 = new DependencyDescriptor(field1, false);
        String value1 = resolver.getSuggestedValue(descriptor1).toString();
        System.out.println(value1); // ${JAVA_HOME} : String
        // 解析 ${}
        value1 = context.getEnvironment().resolvePlaceholders(value1);
        System.out.println(value1); // D:\jdks\jdk1.8 : String

        // 获取 @Value 的内容
        System.out.println("-------------------------------------------------------------");
        Field field2 = Bean1.class.getDeclaredField("age");
        DependencyDescriptor descriptor2 = new DependencyDescriptor(field2, false);
        String value2 = resolver.getSuggestedValue(descriptor2).toString();
        System.out.println(value2);
        // 解析 ${}
        value2 = context.getEnvironment().resolvePlaceholders(value2);
        System.out.println(value2);
        // 类型转换 - 基本数据类型
        Object age = context.getBeanFactory().getTypeConverter().convertIfNecessary(value2, descriptor2.getDependencyType());
        System.out.println("age=" + age + ", class=" + age.getClass());

        // 获取 @Value 的内容
        System.out.println("-------------------------------------------------------------");
        context.registerBean("bean3", Bean3.class);
        Field field3 = Bean2.class.getDeclaredField("bean3");
        DependencyDescriptor descriptor3 = new DependencyDescriptor(field3, false);
        String value3 = resolver.getSuggestedValue(descriptor3).toString();
        System.out.println(value3);
        // 解析 ${}
        value3 = context.getEnvironment().resolvePlaceholders(value3);
        // 解析 #{} (在解析 ${} 之后)
        Object bean3Obj = context.getBeanFactory().getBeanExpressionResolver().evaluate(value3, new BeanExpressionContext(context.getBeanFactory(), null));
        // 类型转换
        Object bean3 = context.getBeanFactory().getTypeConverter().convertIfNecessary(bean3Obj, descriptor3.getDependencyType());
        System.out.println(bean3);

        // 获取 @Value 的内容
        System.out.println("-------------------------------------------------------------");
        Field field4 = Bean4.class.getDeclaredField("value");
        DependencyDescriptor descriptor4 = new DependencyDescriptor(field4, false);
        String value4 = resolver.getSuggestedValue(descriptor4).toString();
        System.out.println(value4);
        // 解析 ${}
        value4 = context.getEnvironment().resolvePlaceholders(value4);
        System.out.println(value4);
        // 解析 #{} (在解析 ${} 之后)
        Object bean4Obj = context.getBeanFactory().getBeanExpressionResolver().evaluate(value4, new BeanExpressionContext(context.getBeanFactory(), null));
        // 类型转换
        Object bean4 = context.getBeanFactory().getTypeConverter().convertIfNecessary(bean4Obj, descriptor4.getDependencyType());
        System.out.println(bean4);
    }

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

    static class Bean2 {
        @Value("#{@bean3}") // 类似于 @Autowire,但是需要配置 SpringEL 表达式使用,@表示要根据bean的名字找
        private Bean3 bean3;
    }

    static class Bean3 {}

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

结果:
-------------------------------------------------------------
${JAVA_HOME}
09:08:23.571 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
D:\jdks\jdk1.8
-------------------------------------------------------------
18
18
age=18, class=class java.lang.Integer
-------------------------------------------------------------
#{@bean3}
09:08:23.612 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean3'
com.clp.test09.Test9$Bean3@4d339552
-------------------------------------------------------------
#{'hello, ' + '${JAVA_HOME}'}
09:08:23.615 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Found key 'JAVA_HOME' in PropertySource 'systemEnvironment' with value of type String
#{'hello, ' + 'D:\jdks\jdk1.8'}
hello, D:\jdks\jdk1.8

25、@Autowire 注入底层

代码演示1:

@Configuration
public class Test10 {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test10.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        // 1、根据成员变量的类型注入
        System.out.println("------------------------------------------------------------");
        DependencyDescriptor dd1 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);
        Object bean2 = beanFactory.doResolveDependency(dd1, "bean1", null, null);
        System.out.println(bean2);
        // 2、根据参数的类型注入
        System.out.println("------------------------------------------------------------");
        Method setBean2Method = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
        DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2Method, 0), false);
        bean2 = beanFactory.doResolveDependency(dd2, "bean1", null, null);
        System.out.println(bean2);
        // 3、结果包装为 Optional<Bean2>
        System.out.println("------------------------------------------------------------");
        DependencyDescriptor dd3 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean3"), false);
        System.out.println(dd3.getDependencyType());
        dd3.increaseNestingLevel();
        System.out.println(dd3.getDependencyType());
        // 4、结果包装为 ObjectProvider
        System.out.println("------------------------------------------------------------");
        DependencyDescriptor dd4 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean4"), false);
        if (dd4.getDependencyType() == ObjectFactory.class) {
            dd4.increaseNestingLevel();
            ObjectFactory factory = new ObjectFactory() {
                @Override
                public Object getObject() throws BeansException {
                    return beanFactory.doResolveDependency(dd4, "bean1", null, null);
                }
            };
            System.out.println(factory.getObject());
        }
        // 5、对 @Lazy 的处理
        System.out.println("------------------------------------------------------------");
        DependencyDescriptor dd5 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);
        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);
        Object proxy = resolver.getLazyResolutionProxyIfNecessary(dd5, "bean1");
        System.out.println(proxy);
        System.out.println(proxy.getClass());
    }

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

        @Autowired
        private void setBean2(@Lazy 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();
        }
    }
}

结果:
------------------------------------------------------------
com.clp.test10.Test10$Bean2@6b19b79
------------------------------------------------------------
com.clp.test10.Test10$Bean2@6b19b79
------------------------------------------------------------
class java.util.Optional
class com.clp.test10.Test10$Bean2
------------------------------------------------------------
com.clp.test10.Test10$Bean2@6b19b79
------------------------------------------------------------
com.clp.test10.Test10$Bean2@6b19b79
class com.clp.test10.Test10$Bean2$$EnhancerBySpringCGLIB$$b860d359

代码演示2:

/**
 * 优先级顺序:
 *      1、@Qualifier
 *      2、@Primary
 *      3、成员变量名字
 */
@Configuration
public class Test10_2 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Test10_2.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        System.out.println("---------------------------------------------------------------");
        testArray(beanFactory);

        System.out.println("---------------------------------------------------------------");
        testList(beanFactory);

        System.out.println("---------------------------------------------------------------");
        testConfigurableApplicationContext(beanFactory);

        System.out.println("---------------------------------------------------------------");
        testGeneric(beanFactory);

        System.out.println("---------------------------------------------------------------");
        testQualifier(beanFactory);

        System.out.println("---------------------------------------------------------------");
        testPrimary(beanFactory);

        System.out.println("---------------------------------------------------------------");
        testDefault(beanFactory);
    }

    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);
            String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, componentType);
            List<Object> beans = new ArrayList<>();
            for (String name : names) {
                System.out.println(name);
                Object bean = dd1.resolveCandidate(name, componentType, beanFactory);
                beans.add(bean);
            }
            Object array = beanFactory.getTypeConverter().convertIfNecessary(beans, dd1.getDependencyType());
            System.out.println(array);
        }
    }

    private static void testList(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd2 = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), true);
        if (dd2.getDependencyType() == List.class) {
            Class<?> resolve = dd2.getResolvableType().getGeneric().resolve();
            System.out.println(resolve);
            String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, resolve);
            List<Object> beans = new ArrayList<>();
            for (String name : names) {
                Object bean = dd2.resolveCandidate(name, resolve, beanFactory);
                beans.add(bean);
            }
            System.out.println(beans);
        }
    }

    @SuppressWarnings("unchecked")
    private static void testConfigurableApplicationContext(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException, IllegalAccessException {
        DependencyDescriptor dd3 = new DependencyDescriptor(Target.class.getDeclaredField("applicationContext"), true);

        Field resolvableDependenciesField = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
        resolvableDependenciesField.setAccessible(true);
        Map<Class<?>, Object> dependencies = (Map<Class<?>, Object>) resolvableDependenciesField.get(beanFactory);
        for (Map.Entry<Class<?>, Object> entry : dependencies.entrySet()) {
            // class1.isAssignableFrom(class2) :class1 是否可以继承 class2
            if (entry.getKey().isAssignableFrom(dd3.getDependencyType())) {
                System.out.println(entry.getValue());
                break;
            }
        }
    }

    private static void testGeneric(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd4 = new DependencyDescriptor(Target.class.getDeclaredField("dao"), true);
        Class<?> type = dd4.getDependencyType(); // Dao.class
        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);
                Object bean = dd4.resolveCandidate(name, type, beanFactory);
                System.out.println(bean);
            }
        }
    }

    private static void testQualifier(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        DependencyDescriptor dd5 = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);
        Class<?> type = dd5.getDependencyType(); // Service.class
        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), dd5)) {
                System.out.println(name);
                Object bean = dd5.resolveCandidate(name, type, beanFactory);
                System.out.println(bean);
            }
        }
    }

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

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

    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;
    }

    static class Target2 {
        @Autowired
        private Service service; // 测试 : 根据 @Primary 注解匹配注入
    }

    static class Target3 {
        @Autowired
        private Service service3; // 测试 : 根据名字匹配注入
    }

    interface Service {}

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

    @Component("service2") @Primary // 用于 Target2 的默认注入,只能加一个
    static class Service2 implements Service {}

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

    interface Dao<T> {}

    static class Student {}

    static class Teacher {}

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

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

结果:
---------------------------------------------------------------
interface com.clp.test10.Test10_2$Service
service3
service2
service1
[Lcom.clp.test10.Test10_2$Service;@4278a03f
---------------------------------------------------------------
interface com.clp.test10.Test10_2$Service
[com.clp.test10.Test10_2$Service3@7a4ccb53, com.clp.test10.Test10_2$Service2@309e345f, com.clp.test10.Test10_2$Service1@56a6d5a6]
---------------------------------------------------------------
org.springframework.context.annotation.AnnotationConfigApplicationContext@626b2d4a, started on Wed May 17 11:11:07 CST 2023
---------------------------------------------------------------
dao2
com.clp.test10.Test10_2$Dao2@6dbb137d
---------------------------------------------------------------
service2
com.clp.test10.Test10_2$Service2@309e345f
---------------------------------------------------------------
service2
---------------------------------------------------------------
service3

26、事件 - 监听器

代码演示:

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

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

    @Slf4j
    @Component
    static class MyService {
        @Autowired
        private ApplicationEventPublisher publisher; // 事件发布器,实际就是 ApplicationContext
        
        public void doBusiness() {
            log.debug("主线业务");
//            // 主线业务完成后需要做一些支持业务,下面是问题代码
//            log.debug("发送短信");
//            log.debug("发送邮件");
            // 将上面问题代码修改为 事件监听 机制
            publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
        }

        @PostConstruct
        private void init() {
            doBusiness();
        }
    }

    // ---------------------------- 监听器实现方式1 --------------------------------- //
    @Slf4j
    @Component
    static class SmsApplicationListener implements ApplicationListener<MyEvent> {
        @Override
        public void onApplicationEvent(MyEvent event) {
            log.debug("发送短信");
        }
    }

    @Slf4j
    @Component
    static class EmailApplicationListener implements ApplicationListener<MyEvent> {
        @Override
        public void onApplicationEvent(MyEvent event) {
            log.debug("发送邮件");
        }
    }

    // ---------------------------- 监听器实现方式2 --------------------------------- //
    @Slf4j
    @Component
    static class SmsService {
        @EventListener
        public void listener(MyEvent event) {
            log.debug("发送短信");
        }
    }

    @Slf4j
    @Component
    static class EmailService {
        @EventListener
        public void listener(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 SimpleApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor executor) {
        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
        multicaster.setTaskExecutor(executor);
        return multicaster;
    }
}

结果:
11:28:36.583 [main] DEBUG com.clp.test11.Test11$MyService - 主线业务
11:28:36.595 [executor-1] DEBUG com.clp.test11.Test11$EmailApplicationListener - 发送邮件
11:28:36.599 [executor-2] DEBUG com.clp.test11.Test11$SmsApplicationListener - 发送短信

代码演示2:

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

        context.getBean(MyService.class).doBusiness();
        context.close();
    }

    // 该接口的方法会在 初始化单例 后被回调
    @Bean
    public SmartInitializingSingleton smartInitializingSingleton(ConfigurableApplicationContext context) {
        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(MyEventListener.class)) {
                            ApplicationListener listener = new ApplicationListener() {
                                @Override
                                public void onApplicationEvent(ApplicationEvent event) {
                                    try {
                                        Class<?> eventType = method.getParameterTypes()[0];// 监听器方法需要的事件类型
                                        if (eventType.isAssignableFrom(event.getClass())) {
                                            method.invoke(bean, event);
                                        }
                                    } catch (IllegalAccessException | InvocationTargetException e) {
                                        e.printStackTrace();
                                    }
                                }
                            };
                            context.addApplicationListener(listener);
                        }
                    }
                }
            }
        };
    }

    @Slf4j
    @Component
    static class MyService {
        @Autowired
        private ApplicationEventPublisher publisher; // 事件发布器,实际就是 ApplicationContext

        public void doBusiness() {
            log.debug("主线业务");
//            // 主线业务完成后需要做一些支持业务,下面是问题代码
//            log.debug("发送短信");
//            log.debug("发送邮件");
            // 将上面问题代码修改为 事件监听 机制
            publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
        }
    }

    @Slf4j
    @Component
    static class SmsService {
        @MyEventListener
        public void listener(MyEvent event) {
            log.debug("发送短信");
        }
    }

    @Slf4j
    @Component
    static class EmailService {
        @MyEventListener
        public void listener(MyEvent event) {
            log.debug("发送邮件");
        }
    }

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

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

结果:
11:54:53.371 [main] DEBUG com.clp.test12.Test12$MyService - 主线业务
11:54:53.372 [main] DEBUG com.clp.test12.Test12$EmailService - 发送邮件
11:54:53.372 [main] DEBUG com.clp.test12.Test12$SmsService - 发送短信

27、事件 - 发布器

代码演示:

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

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

    @Slf4j
    @Component
    static class MyService {
        @Autowired
        private ApplicationEventPublisher publisher; // 事件发布器,实际就是 ApplicationContext

        public void doBusiness() {
            log.debug("主线业务");
//            // 主线业务完成后需要做一些支持业务,下面是问题代码
//            log.debug("发送短信");
//            log.debug("发送邮件");
            // 将上面问题代码修改为 事件监听 机制
            publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
        }

        @PostConstruct
        private void init() {
            doBusiness();
        }
    }

    // ---------------------------- 监听器实现方式1 --------------------------------- //
    @Slf4j
    @Component
    static class SmsApplicationListener implements ApplicationListener<MyEvent> {
        @Override
        public void onApplicationEvent(MyEvent event) {
            log.debug("发送短信");
        }
    }

    @Slf4j
    @Component
    static class EmailApplicationListener implements ApplicationListener<MyEvent> {
        @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<>();

            @Override // 收集监听器
            public void addApplicationListenerBean(String listenerBeanName) {
                ApplicationListener listener = context.getBean(listenerBeanName, ApplicationListener.class);
                System.out.println(listener);
                // 获取该监听器支持的事件类型
                ResolvableType type = ResolvableType.forClass(listener.getClass()).getInterfaces()[0].getGeneric();
                System.out.println(type);
                // 将原始的 listener 封装为支持类型检查的listener(GenericApplicationListener)
                GenericApplicationListener genericApplicationListener = new GenericApplicationListener() {
                    @Override // 是否支持某事件类型,参数为真实的事件类型
                    public boolean supportsEventType(ResolvableType eventType) {
                        return type.isAssignableFrom(eventType);
                    }

                    @Override // 调用原始 listener 的 onApplicationEvent()
                    public void onApplicationEvent(ApplicationEvent event) {
                        executor.submit(() -> listener.onApplicationEvent(event));
                    }
                };
                listeners.add(genericApplicationListener);
            }

            @Override // (发)广播事件
            public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
                for (GenericApplicationListener listener : listeners) {
                    if (listener.supportsEventType(ResolvableType.forClass(event.getClass()))) {
                        listener.onApplicationEvent(event);
                    }
                }
            }
        };
    }

    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) {

        }
    }
}

结果:
com.clp.test11.Test11$EmailApplicationListener@4c1d9d4b
com.clp.test11.Test11$MyEvent
......
com.clp.test11.Test11$SmsApplicationListener@d706f19
com.clp.test11.Test11$MyEvent
......
13:58:05.160 [main] DEBUG com.clp.test11.Test11$MyService - 主线业务
13:58:05.161 [main] DEBUG com.clp.test11.Test11$EmailApplicationListener - 发送邮件
13:58:05.161 [main] DEBUG com.clp.test11.Test11$SmsApplicationListener - 发送短信
......

 

posted on 2023-05-17 14:02  啊噢1231  阅读(86)  评论(0编辑  收藏  举报

导航

回到顶部