• Spring Boot 入门


    37) Boot 骨架项目

    https://start.spring.io/pom.xml

    38) Boot War项目

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

    步骤2:编写控制器

    1. @Controller
    2. public class MyController {
    3.    @RequestMapping("/hello")
    4.    public String abc() {
    5.        System.out.println("进入了控制器");
    6.        return "hello";
    7.   }
    8. }

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

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

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

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

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

    测试

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

    • 必须添加如下依赖,因为此时用的还是内嵌 tomcat,而内嵌 tomcat 默认不带 jasper(用来解析 jsp)

    1. <dependency>
    2.    <groupId>org.apache.tomcat.embedgroupId>
    3.    <artifactId>tomcat-embed-jasperartifactId>
    4.    <scope>providedscope>
    5. dependency>

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

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

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

    39) Boot 启动过程

    阶段一:SpringApplication 构造

    1. public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    2. this.resourceLoader = resourceLoader;
    3. Assert.notNull(primarySources, "PrimarySources must not be null");
    4. // 记录 BeanDefinition 源
    5. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    6. // 推断应用类型
    7. this.webApplicationType = WebApplicationType.deduceFromClasspath();
    8. this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
    9. // 记录 ApplicationContext 初始化器
    10. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    11. // 记录监听器
    12. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    13. // 推断主启动类
    14. this.mainApplicationClass = deduceMainApplicationClass();
    15. }
    1. 记录 BeanDefinition

    2. 推断应用类型

    3. 记录 ApplicationContext 初始化器

    4. 记录监听器

    5. 推断主启动类

    阶段二:执行 run 方法

    1. public ConfigurableApplicationContext run(String... args) {
    2. // ...
    3. // 1.得到 SpringApplicationRunListeners,名字取得不好,实际是事件发布器
    4. SpringApplicationRunListeners listeners = getRunListeners(args);
    5. // 发布 application starting 事件1️⃣,代表Spring Boot 应用开始启动了
    6. listeners.starting(bootstrapContext, this.mainApplicationClass);
    7. try {
    8. // 2.封装启动 args 把参数分为选项参与非选项参数
    9. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    10. // 3.
    11. ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    12. configureIgnoreBeanInfo(environment);
    13. // 7.打印 banner(*)
    14. Banner printedBanner = printBanner(environment);
    15. // 8.创建容器
    16. context = createApplicationContext();
    17. context.setApplicationStartup(this.applicationStartup);
    18. // 9.准备容器
    19. prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    20. // 11.refresh 容器
    21. refreshContext(context);
    22. afterRefresh(context, applicationArguments);
    23. stopWatch.stop();
    24. if (this.logStartupInfo) {
    25. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    26. }
    27. // 11.1 发布 application started 事件5️⃣
    28. listeners.started(context);
    29. // 12.执行 runner
    30. callRunners(context, applicationArguments);
    31. }catch (Throwable ex) {
    32. handleRunFailure(context, ex, listeners);
    33. throw new IllegalStateException(ex);
    34. }
    35. try {
    36. // 持续运行
    37. listeners.running(context);
    38. }
    39. // ....
    40. }
    41. // 3.
    42. private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
    43. DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    44. // Create and configure the environment
    45. ConfigurableEnvironment environment = getOrCreateEnvironment();
    46. // 根据参数信息封装成一个propertiesSource并添加到 Environment -基于命令行的参数
    47. // 4.ConfigurationPropertySources 处理(*)
    48. configureEnvironment(environment, applicationArguments.getSourceArgs());
    49. // 对命名不规范的键统一处理成 ‘-’分割
    50. ConfigurationPropertySources.attach(environment);
    51. // 4.1发布 application environment 已准备事件2️⃣
    52. // 5.通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理(*)
    53. //* application.properties,由 StandardConfigDataLocationResolver 解析
    54. // * spring.application.json
    55. listeners.environmentPrepared(bootstrapContext, environment);
    56. DefaultPropertiesPropertySource.moveToEnd(environment);
    57. Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
    58. "Environment prefix cannot be set via properties.");
    59. // 6.绑定 spring.main 到 SpringApplication 对象(*)
    60. bindToSpringApplication(environment);
    61. if (!this.isCustomEnvironment) {
    62. environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
    63. deduceEnvironmentClass());
    64. }
    65. ConfigurationPropertySources.attach(environment);
    66. return environment;
    67. }
    68. // 9.
    69. private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
    70. ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
    71. ApplicationArguments applicationArguments, Banner printedBanner) {
    72. context.setEnvironment(environment);
    73. postProcessApplicationContext(context);
    74. // 9.1发布 application context 已初始化事件3️⃣
    75. applyInitializers(context);
    76. listeners.contextPrepared(context);
    77. bootstrapContext.close(context);
    78. if (this.logStartupInfo) {
    79. logStartupInfo(context.getParent() == null);
    80. logStartupProfileInfo(context);
    81. }
    82. // Add boot specific singleton beans
    83. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    84. beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    85. if (printedBanner != null) {
    86. beanFactory.registerSingleton("springBootBanner", printedBanner);
    87. }
    88. if (beanFactory instanceof DefaultListableBeanFactory) {
    89. ((DefaultListableBeanFactory) beanFactory)
    90. .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    91. }
    92. if (this.lazyInitialization) {
    93. context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    94. }
    95. // Load the sources
    96. // 10.加载 bean 定义
    97. Set sources = getAllSources();
    98. Assert.notEmpty(sources, "Sources must not be empty");
    99. load(context, sources.toArray(new Object[0]));
    100. // 10.1发布 application prepared 事件4️⃣
    101. listeners.contextLoaded(context);
    102. }
    103. // 11.1初始化每一个单例
    104. protected void refresh(ConfigurableApplicationContext applicationContext) {
    105. applicationContext.refresh();
    106. }
    107. // 12.1 调用实现了 ApplicationRunner或者CommandLineRunner接口的Bean
    108. private void callRunners(ApplicationContext context, ApplicationArguments args) {
    109. List runners = new ArrayList<>();
    110. runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    111. runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    112. AnnotationAwareOrderComparator.sort(runners);
    113. for (Object runner : new LinkedHashSet<>(runners)) {
    114. if (runner instanceof ApplicationRunner) {
    115. callRunner((ApplicationRunner) runner, args);
    116. }
    117. if (runner instanceof CommandLineRunner) {
    118. callRunner((CommandLineRunner) runner, args);
    119. }
    120. }
    121. }
      1. 得到 SpringApplicationRunListeners,名字取得不好,实际是事件发布器

        • 发布 application starting 事件1️⃣

      2. 封装启动 args

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

      4. ConfigurationPropertySources 处理(*)

        • 发布 application environment 已准备事件2️⃣

      5. 通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理(*)

        • application.properties,由 StandardConfigDataLocationResolver 解析

        • spring.application.json

      6. 绑定 spring.main 到 SpringApplication 对象(*)

      7. 打印 banner(*)

      8. 创建容器

      9. 准备容器

        • 发布 application context 已初始化事件3️⃣

      10. 加载 bean 定义

        • 发布 application prepared 事件4️⃣

      11. refresh 容器

        • 发布 application started 事件5️⃣

      12. 执行 runner

        • 发布 application ready 事件6️⃣

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

      41) Boot 自动配置

      1. public static void main(String[] args) throws IOException {
      2. GenericApplicationContext context = new GenericApplicationContext();
      3. // 设置是否应该通过注册具有相同名称的不同定义(自动替换前者)来覆盖bean定义
      4. context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);
      5. context.registerBean("config", Config.class);
      6. // 用于@Configuration类的引导处理
      7. context.registerBean(ConfigurationClassPostProcessor.class);
      8. context.refresh();
      9. for (String name : context.getBeanDefinitionNames()) {
      10. System.out.println(name);
      11. }
      12. System.out.println(context.getBean(Bean1.class));
      13. }
      14. @Configuration // 本项目的配置类,如果配置重复,优先处理本项目的Bean
      15. @Import(MyImportSelector.class)
      16. static class Config {
      17. @Bean
      18. public Bean1 bean1() {
      19. return new Bean1("本项目");
      20. }
      21. }
      22. // 在处理完所有@Configuration bean后运行的ImportSelector
      23. static class MyImportSelector implements DeferredImportSelector {
      24. // SpringFactoriesLoader 会扫描当前项目下 src/main/resources/META-INF/spring.factories 的配置信息
      25. @Override
      26. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
      27. // System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
      28. // for (String name : SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, null)) {
      29. // System.out.println(name);
      30. // }
      31. // System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
      32. List names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
      33. return names.toArray(new String[0]);
      34. }
      35. }

      使用@Import + ImportSelector.class  结合 SpringFactoriesLoader.class 扫描所有依赖包 src/main/resources/META-INF/spring.factories 的配置信息 可以轻松的引入第三方的配置。

              对于Bean类型重复的问题 需要检查 org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

      检查DefaultListableBeanFactor的属性  allowBeanDefinitionOverriding(默认允许覆盖),可通过配置。

      1. # 不允许注册具有相同名称的bean
      2. spring.main.allow-bean-definition-overriding = false

              当不许同名覆盖注册时,若想要优先本项目中的配置文件生效则可以配合使用 DeferredImportSelector.class 与 @Conditional 开头的注解配合使用。

       BeanDefinitionRegistryPostProcessor如何动态注册Bean到Spring_java_脚本之家 (jb51.net)https://www.jb51.net/article/242116.htm

      Aop 自动配置

      1. @Configuration
      2. @Import(MyImportSelector.class)
      3. static class Config {
      4. }
      5. static class MyImportSelector implements DeferredImportSelector {
      6. @Override
      7. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
      8. return new String[]{AopAutoConfiguration.class.getName()};
      9. }
      10. }

      解析spring.factries 以spring.aop开头的配置信息

      1. spring.aop.auto=true 或者缺失了spring.aop.auto 会进入以下逻辑
      2. AopAutoConfiguration
      3. # 类路径下是否存在 org.aspectj.weaver.Advice.class; springBoot加入了AOP的功能
      4. AspectJAutoProxyingConfiguration
      5. # spring.aop.proxy-target-class = false 使用JDK代理
      6. JdkDynamicAutoProxyConfiguration
      7. # spring.aop.proxy-target-class = true 或者缺失此键 使用Cglib代理
      8. CglibAutoProxyConfiguration
      9. #缺失 org.aspectj.weaver.Advice.class
      10. ClassProxyingConfiguration
      11. forceAutoProxyCreatorToUseClassProxying
      12. 使用InfrastructureAdvisorAutoProxyCreator 或者 org.springframework.aop.config.internalAutoProxyCreator

      在引入第三方配置时候

      其他自动配置类

      1. org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
      2. org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
      3. org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
      4. org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
      5. #MVC
      6. org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
      7. org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
      8. org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
      9. org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration

      总结

      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 1

        • SqlSessionTemplate - SqlSession 的实现,此实现会与当前线程绑定 2

        • 用 ImportBeanDefinitionRegistrar 的方式扫描所有标注了 @Mapper 注解的接口 3

        • 用 AutoConfigurationPackages 来确定扫描的包 4

      • 还有一个相关的 bean:MybatisProperties,它会读取配置文件中带 mybatis. 前缀的配置项进行定制配置 5

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

      自动配置类实现步骤

              不能直接使用 @Import(AutoConfigurationImportSelector.class) 通过使用 @EnableAutoConfiguration,或者使用其子类 ImportAutoConfigurationImportSelector

      1. AutoConfigurationImportSelector
      2. AutoConfigurationImportSelector#selectImports -ImportSelector的重要方法
      3. AutoConfigurationImportSelector#getAutoConfigurationEntry
      4. AutoConfigurationImportSelector#getCandidateConfigurations
      5. SpringFactoriesLoader#loadFactoryNames -扫描 META-INF/spring.factories

      42) 条件装配底层

              条件装基础注解时@Conditional,其value() 必须是Condition类。Condition类的matches方法可对ConditionContext AnnotatedTypeMetadata 进行判断。

      我们可以引入自定义的注解在类上标注@Conditional(自己实现的Condition)参考org.springframework.boot.autoconfigure.condition.ConditionalOnClass

      1. static class MyCondition implements Condition { // 存在 Druid 依赖
      2. @Override
      3. public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
      4. Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());
      5. String className = attributes.get("className").toString();
      6. boolean exists = (boolean) attributes.get("exists");
      7. boolean present = ClassUtils.isPresent(className, null);
      8. return exists == present;
      9. }
      10. }
      11. @Retention(RetentionPolicy.RUNTIME)
      12. @Target({ElementType.METHOD, ElementType.TYPE})
      13. @Conditional(MyCondition.class)
      14. @interface ConditionalOnClass {
      15. boolean exists(); // true 判断存在 false 判断不存在
      16. String className(); // 要判断的类名
      17. }
      18. @Configuration // 第三方的配置类
      19. @ConditionalOnClass(className = "com.alibaba.druid.pool.DruidDataSource", exists = false)
      20. static class AutoConfiguration1 {
      21. @Bean
      22. public Bean1 bean1() {
      23. return new Bean1();
      24. }
      25. }

      43) FactoryBean

      44) @Indexed 原理

      45) 代理进一步理解

      收获💡

      1. spring 代理的设计特点

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

          • 因此 cglib 不能用 MethodProxy.invokeSuper()

        • 代理与目标是两个对象,二者成员变量并不共用数据,代理立对象不能直接访问属性,代理的方法内原始对象 属性信息是完整的

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

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

      1. @Component
      2. public class Bean1 {
      3. private static final Logger log = LoggerFactory.getLogger(Bean1.class);
      4. protected Bean2 bean2;
      5. protected boolean initialized;
      6. @Autowired
      7. public void setBean2(Bean2 bean2) {
      8. log.debug("setBean2(Bean2 bean2)");
      9. this.bean2 = bean2;
      10. }
      11. @PostConstruct
      12. public void init() {
      13. log.debug("init");
      14. initialized = true;
      15. }
      16. public Bean2 getBean2() {
      17. log.debug("getBean2()");
      18. return bean2;
      19. }
      20. public boolean isInitialized() {
      21. log.debug("isInitialized()");
      22. return initialized;
      23. }
      24. public void m1() {
      25. System.out.println("m1() 成员方法");
      26. }
      27. final public void m2() {
      28. System.out.println("m2() final 方法");
      29. }
      30. static public void m3() {
      31. System.out.println("m3() static 方法");
      32. }
      33. private void m4() {
      34. System.out.println("m4() private 方法");
      35. }
      36. }
      37. @SpringBootApplication
      38. public class A45 {
      39. public static void main(String[] args) throws Exception {
      40. ConfigurableApplicationContext context = SpringApplication.run(A45.class, args);
      41. displayAllBeans(context);
      42. Bean1 proxy = context.getBean(Bean1.class);
      43. /*
      44. 1.演示 spring 代理的设计特点
      45. 依赖注入和初始化影响的是原始对象
      46. 代理与目标是两个对象,二者成员变量并不共用数据
      47. */
      48. showProxyAndTarget(proxy);
      49. System.out.println(">>>>>>>>>>>>>>>>>>>");
      50. System.out.println(proxy.getBean2());
      51. System.out.println(proxy.isInitialized());
      52. /*
      53. 2.演示 static 方法、final 方法、private 方法均无法增强
      54. */
      55. proxy.m1();
      56. proxy.m2();
      57. Bean1.m3();
      58. Method m4 = Bean1.class.getDeclaredMethod("m1");
      59. m4.setAccessible(true);
      60. m4.invoke(proxy);
      61. context.close();
      62. }
      63. public static void showProxyAndTarget(Bean1 proxy) throws Exception {
      64. System.out.println(">>>>> 代理中的成员变量");
      65. System.out.println("\tinitialized=" + proxy.initialized);
      66. System.out.println("\tbean2=" + proxy.bean2);
      67. if (proxy instanceof Advised) {
      68. System.out.println(">>>>> 目标中的成员变量");
      69. Bean1 target = (Bean1) ((Advised) proxy).getTargetSource().getTarget();
      70. System.out.println("\tinitialized=" + target.initialized);
      71. System.out.println("\tbean2=" + target.bean2);
      72. }
      73. }
      74. }

      46) @Value 装配底层

      1. 如果需要的值是字符串,先解析 ${ },再解析 #{ }

      2. 不是字符串,需要用 TypeConverter 转换

      ContextAnnotationAutowireCandidateResolver 获取标注了@Value的内容(org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#getSuggestedValue),先通过Environment 解析#{}占位符(org.springframework.core.env.PropertyResolver#resolvePlaceholders),再通过BeanExpressionResolver计算(org.springframework.beans.factory.config.BeanExpressionResolver#evaluate)。
      1. public class Bean2 {
      2. @Value("#{@bean3}") // SpringEL #{SpEL}
      3. private Bean3 bean3;
      4. }
      5. @Component("bean3")
      6. public class Bean3 {
      7. }
      8. static class Bean4 {
      9. @Value("#{'hello, ' + '${JAVA_HOME}'}")
      10. private String value;
      11. }
      12. /
      13. public static void main(String[] args) throws Exception {
      14. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A46.class);
      15. DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
      16. ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
      17. resolver.setBeanFactory(beanFactory);
      18. test1(context, resolver, Bean1.class.getDeclaredField("home"));
      19. test2(context, resolver, Bean1.class.getDeclaredField("age"));
      20. test3(context, resolver, Bean2.class.getDeclaredField("bean3"));
      21. test3(context, resolver, Bean4.class.getDeclaredField("value"));
      22. }
      23. private static void test3(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
      24. DependencyDescriptor dd1 = new DependencyDescriptor(field, false);
      25. // 获取 @Value 的内容
      26. String value = resolver.getSuggestedValue(dd1).toString();
      27. System.out.println(value);
      28. // 解析 ${}
      29. value = context.getEnvironment().resolvePlaceholders(value);
      30. System.out.println(value);
      31. System.out.println(value.getClass());
      32. // 解析 #{} @bean3
      33. Object bean3 = context.getBeanFactory().getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(context.getBeanFactory(), null));
      34. // 类型转换
      35. Object result = context.getBeanFactory().getTypeConverter().convertIfNecessary(bean3, dd1.getDependencyType());
      36. System.out.println(result);
      37. }

      47) @Autowired 装配底层

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

       1-4

      1. @Configuration
      2. public class A47_1 {
      3. public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
      4. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A47_1.class);
      5. // 1. 根据成员变量的类型注入
      6. DependencyDescriptor dd1 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);
      7. beanFactory.doResolveDependency(dd1, "bean1", null, null);
      8. // 2. 根据参数的类型注入
      9. Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
      10. DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), false);
      11. beanFactory.doResolveDependency(dd2, "bean1", null, null);
      12. // 3. 结果包装为 Optional
      13. DependencyDescriptor dd3 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean3"), false);
      14. if (dd3.getDependencyType() == Optional.class) {
      15. dd3.increaseNestingLevel();
      16. Object result = beanFactory.doResolveDependency(dd3, "bean1", null, null);
      17. Optional.ofNullable(result);
      18. }
      19. // 4. 结果包装为 ObjectProvider,ObjectFactory
      20. DependencyDescriptor dd4 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean4"), false);
      21. if (dd4.getDependencyType() == ObjectFactory.class) {
      22. dd4.increaseNestingLevel();
      23. ObjectFactory objectFactory = new ObjectFactory() {
      24. @Override
      25. public Object getObject() throws BeansException {
      26. return beanFactory.doResolveDependency(dd4, "bean1", null, null);
      27. }
      28. };
      29. // 需要的时候才去加载
      30. objectFactory.getObject();
      31. }
      32. // 5. 对 @Lazy 的处理
      33. DependencyDescriptor dd5 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);
      34. ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
      35. resolver.setBeanFactory(beanFactory);
      36. Object proxy = resolver.getLazyResolutionProxyIfNecessary(dd5, "bean1");
      37. // 返回的是代理对象
      38. System.out.println(proxy);
      39. System.out.println(proxy.getClass());

       通过org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency 可获取依赖注入的Bean,

       对@Autowired 、@Qualifier、@Value的解析

      5-9

      1. mport org.springframework.beans.factory.BeanFactoryUtils;
      2. import org.springframework.beans.factory.annotation.Autowired;
      3. import org.springframework.beans.factory.annotation.Qualifier;
      4. import org.springframework.beans.factory.config.BeanDefinition;
      5. import org.springframework.beans.factory.config.BeanDefinitionHolder;
      6. import org.springframework.beans.factory.config.DependencyDescriptor;
      7. import org.springframework.beans.factory.support.DefaultListableBeanFactory;
      8. import org.springframework.context.ConfigurableApplicationContext;
      9. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
      10. import org.springframework.context.annotation.Configuration;
      11. import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
      12. import org.springframework.stereotype.Component;
      13. import java.lang.reflect.Field;
      14. import java.util.ArrayList;
      15. import java.util.List;
      16. import java.util.Map;
      17. @SuppressWarnings("all")
      18. @Configuration
      19. public class A47_2 {
      20. public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
      21. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A47_2.class);
      22. DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
      23. System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 1. 数组类型");
      24. testArray(beanFactory);
      25. System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 2. List 类型");
      26. testList(beanFactory);
      27. System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 3. applicationContext");
      28. testApplicationContext(beanFactory);
      29. System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 4. 泛型");
      30. testGeneric(beanFactory);
      31. System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 5. @Qualifier");
      32. testQualifier(beanFactory);
      33. /*
      34. 学到了什么
      35. 1. 如何获取数组元素类型
      36. 2. Spring 如何获取泛型中的类型
      37. 3. 特殊对象的处理, 如 ApplicationContext, 并注意 Map 取值时的类型匹配问题 (另见 TestMap)
      38. 4. 谁来进行泛型匹配 (另见 TestGeneric)
      39. 5. 谁来处理 @Qualifier
      40. 6. 刚开始都只是按名字处理, 等候选者确定了, 才会创建实例
      41. */
      42. }
      43. private static void testQualifier(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
      44. DependencyDescriptor dd5 = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);
      45. Class type = dd5.getDependencyType();
      46. ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
      47. resolver.setBeanFactory(beanFactory);
      48. for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
      49. BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
      50. // @Qualifier("service2")
      51. if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd,name), dd5)) {
      52. System.out.println(name);
      53. System.out.println(dd5.resolveCandidate(name, type, beanFactory));
      54. }
      55. }
      56. }
      57. private static void testGeneric(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
      58. DependencyDescriptor dd4 = new DependencyDescriptor(Target.class.getDeclaredField("dao"), true);
      59. Class type = dd4.getDependencyType();
      60. ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
      61. resolver.setBeanFactory(beanFactory);
      62. for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
      63. BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
      64. // 对比 BeanDefinition 与 DependencyDescriptor 的泛型是否匹配
      65. if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd,name), dd4)) {
      66. System.out.println(name);
      67. System.out.println(dd4.resolveCandidate(name, type, beanFactory));
      68. }
      69. }
      70. }
      71. private static void testApplicationContext(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException, IllegalAccessException {
      72. DependencyDescriptor dd3 = new DependencyDescriptor(Target.class.getDeclaredField("applicationContext"), true);
      73. Field resolvableDependencies = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
      74. resolvableDependencies.setAccessible(true);
      75. Map, Object> dependencies = (Map, Object>) resolvableDependencies.get(beanFactory);
      76. // dependencies.forEach((k, v) -> {
      77. // System.out.println("key:" + k + " value: " + v);
      78. // });
      79. for (Map.Entry, Object> entry : dependencies.entrySet()) {
      80. // 左边类型 右边类型
      81. if (entry.getKey().isAssignableFrom(dd3.getDependencyType())) {
      82. System.out.println(entry.getValue());
      83. break;
      84. }
      85. }
      86. }
      87. private static void testList(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
      88. DependencyDescriptor dd2 = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), true);
      89. if (dd2.getDependencyType() == List.class) {
      90. Class resolve = dd2.getResolvableType().getGeneric().resolve();
      91. System.out.println(resolve);
      92. List list = new ArrayList<>();
      93. // 在指定工厂
      94. String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, resolve);
      95. for (String name : names) {
      96. // 通过Bean的名称获取Bean 等同于 beanFactory.getBean();
      97. Object bean = dd2.resolveCandidate(name, resolve, beanFactory);
      98. list.add(bean);
      99. }
      100. System.out.println(list);
      101. }
      102. }
      103. private static void testArray(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
      104. DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("serviceArray"), true);
      105. if (dd1.getDependencyType().isArray()) {
      106. Class componentType = dd1.getDependencyType().getComponentType();
      107. System.out.println(componentType);
      108. String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, componentType);
      109. List beans = new ArrayList<>();
      110. for (String name : names) {
      111. System.out.println(name);
      112. Object bean = dd1.resolveCandidate(name, componentType, beanFactory);
      113. beans.add(bean);
      114. }
      115. // array 与List 仅差一步类型转换 TypeConverter(高级转换接口)
      116. Object array = beanFactory.getTypeConverter().convertIfNecessary(beans, dd1.getDependencyType());
      117. System.out.println(array);
      118. }
      119. }
      120. static class Target {
      121. @Autowired private Service[] serviceArray;
      122. @Autowired private List serviceList;
      123. @Autowired private ConfigurableApplicationContext applicationContext;
      124. @Autowired private Dao dao;
      125. @Autowired @Qualifier("service2") private Service service;
      126. }
      127. interface Dao {
      128. }
      129. @Component("dao1") static class Dao1 implements Dao {
      130. }
      131. @Component("dao2") static class Dao2 implements Dao {
      132. }
      133. static class Student {
      134. }
      135. static class Teacher {
      136. }
      137. interface Service {
      138. }
      139. @Component("service1")
      140. static class Service1 implements Service {
      141. }
      142. @Component("service2")
      143. static class Service2 implements Service {
      144. }
      145. @Component("service3")
      146. static class Service3 implements Service {
      147. }
      148. }
      149. @Autowired解析 数组类型、List类型需要通过DependencyDescriptor 进一步确定集合中的类型再从BeanFactory中查找该类型的Bean,并放在对应的数据结构中。

         解析 ConfigurableApplicationContext 类型的依赖,该接口下属的实现常用类为 DefaultListableBeanFactory 其属性 

        resolvableDependencies 在org.springframework.context.support.AbstractApplicationContext#refresh 的org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory中添加了ApplicationContext。

        我们通过反射获取DefaultListableBeanFactory 的resolvableDependencies 遍历确定具有相同接口即可

        对于标注有 @Qualifier、或者是具有泛型的接口,可通过org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate 判断。

        参数一 BeanDefinitionHolder 需要一个BeanDefinition和beanName beanName可以通过 org.springframework.beans.factory.BeanFactoryUtils#beanNamesForTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class) 传入的 dd.getDependencyType() 确定后返回  beanName。

        @Autowired private Dao dao;
        @Autowired @Qualifier("service2") private Service service;

        @Qualifier 检查过后会再检查 @Primary,如果获取的仍不满足 就会以注入的name进行查找。

        beanFactory.getMergedBeanDefinition(name).isPrimary() name.equals(dd.getDependencyName())
        1. private static void testPrimary(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
        2. DependencyDescriptor dd = new DependencyDescriptor(Target1.class.getDeclaredField("service"), false);
        3. Class type = dd.getDependencyType();
        4. for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
        5. if (beanFactory.getMergedBeanDefinition(name).isPrimary()) {
        6. System.out.println(name);
        7. }
        8. if(name.equals(dd.getDependencyName())) {
        9. System.out.println(name);
        10. }
        11. }
        12. }

        48) 事件监听器 49) 事件发布器

        重要接口 org.springframework.context.ApplicationListener  org.springframework.context.ApplicationEventPublisher | ApplicationEventPublisher的主要实现 AbstractApplicationContext

         http://t.csdn.cn/8CzkOhttp://t.csdn.cn/8CzkO

        自定义一个能够将自定义的注解方法注册为ApplicationListener。

        org.springframework.beans.factory.SmartInitializingSingleton在BeanFactory引导期间,在单例初始化阶段结束时触发的回调接口。

        1. org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
        2. org.springframework.beans.factory.SmartInitializingSingleton#afterSingletonsInstantiated AbstractApplicationContext.finishBeanFactoryInitialization(ConfigurableListableBeanFactory) (org.springframework.context.support)
        3. AbstractApplicationContext.refresh() (org.springframework.context.support)
        1. import org.slf4j.Logger;
        2. import org.slf4j.LoggerFactory;
        3. import org.springframework.beans.factory.SmartInitializingSingleton;
        4. import org.springframework.beans.factory.annotation.Autowired;
        5. import org.springframework.context.ApplicationEvent;
        6. import org.springframework.context.ApplicationEventPublisher;
        7. import org.springframework.context.ConfigurableApplicationContext;
        8. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
        9. import org.springframework.context.annotation.Bean;
        10. import org.springframework.context.annotation.Configuration;
        11. import org.springframework.stereotype.Component;
        12. import java.lang.annotation.ElementType;
        13. import java.lang.annotation.Retention;
        14. import java.lang.annotation.RetentionPolicy;
        15. import java.lang.annotation.Target;
        16. import java.lang.reflect.Method;
        17. @Configuration
        18. public class A48_3 {
        19. public static void main(String[] args) {
        20. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A48_3.class);
        21. context.getBean(MyService.class).doBusiness();
        22. context.close();
        23. }
        24. /**
        25. *
        26. * @param context SmartInitializingSingleton 需要一个beanFactory 同时需要支持addApplicationListener 方法
        27. * @return
        28. */
        29. @Bean
        30. public SmartInitializingSingleton smartInitializingSingleton(ConfigurableApplicationContext context) {
        31. return () -> {
        32. for (String name : context.getBeanDefinitionNames()) {
        33. Object bean = context.getBean(name);
        34. for (Method method : bean.getClass().getMethods()) {
        35. // 判断方法上是否标注的有自定义的注解 @MyListener
        36. if (method.isAnnotationPresent(MyListener.class)) {
        37. context.addApplicationListener((event) -> {
        38. System.out.println(event);
        39. Class eventType = method.getParameterTypes()[0];// 监听器方法需要的事件类型
        40. if (eventType.isAssignableFrom(event.getClass())) {
        41. try {
        42. // 此处通过反射执行方式时候的形参并不是方法上的参数 method.getParameters()[0]
        43. method.invoke(bean, event);
        44. } catch (Exception e) {
        45. e.printStackTrace();
        46. }
        47. }
        48. });
        49. }
        50. }
        51. }
        52. };
        53. }
        54. @Component
        55. static class MyService {
        56. private static final Logger log = LoggerFactory.getLogger(MyService.class);
        57. @Autowired
        58. private ApplicationEventPublisher publisher; // applicationContext
        59. public void doBusiness() {
        60. log.debug("主线业务");
        61. // 主线业务完成后需要做一些支线业务,下面是问题代码
        62. publisher.publishEvent(new MyEvent("MyService.doBusiness()"));
        63. }
        64. }
        65. @Component
        66. static class SmsService {
        67. private static final Logger log = LoggerFactory.getLogger(SmsService.class);
        68. @MyListener
        69. public void listener(MyEvent myEvent) {
        70. log.debug("发送短信");
        71. }
        72. }
        73. @Component
        74. static class EmailService {
        75. private static final Logger log = LoggerFactory.getLogger(EmailService.class);
        76. @MyListener
        77. public void listener(MyEvent myEvent) {
        78. log.debug("发送邮件");
        79. }
        80. }
        81. @Retention(RetentionPolicy.RUNTIME)
        82. @Target(ElementType.METHOD)
        83. @interface MyListener {
        84. }
        85. static class MyEvent extends ApplicationEvent {
        86. public MyEvent(Object source) {
        87. super(source);
        88. }
        89. }
        90. }

        双亲委派模式 - 简书 (jianshu.com)icon-default.png?t=M85Bhttps://www.jianshu.com/p/5e0441cd2d4c 

      150. 相关阅读:
        随手记录第六话 -- 在Mac上搭建整个开发环境记录(Java、Python、Vue、Docker、idea)
        【手把手带你学Java EE】HTTP协议
        第五章:最新版零基础学习 PYTHON 教程—Python 字符串操作指南(第七节 - Python 中使用 % 进行字符串格式化)
        前端:nodejs多版本管理工具nvm
        阿里云ACE认证的含金量高吗?如何通过ACE认证考试?
        理论第六课——二分查找函数
        中秋发祝福?一套程序让你成为【相亲相爱一家人】群里最靓的仔
        手写RPC框架--4.服务注册
        SQL注入简介
        c++处理图像---绘制物体的凸包:cv::convexHull
      151. 原文地址:https://blog.csdn.net/qq_34922830/article/details/127680277