static关键字有何魔法?竟让Spring Boot搞出那么多静态内部类(上)

简介: static关键字有何魔法?竟让Spring Boot搞出那么多静态内部类(上)

版本约定


本文内容若没做特殊说明,均基于以下版本:


  • JDK:1.8
  • Spring Framework:5.2.2.RELEASE


正文


说到Java里的static关键字,这当属最基础的入门知识,是Java中常用的关键字之一。你平时用它来修饰变量和方法了,但是对它的了解,即使放在JavaSE情景下知道这些还是不够的,问题虽小但这往往反映了你对Java基础的了解程度。


当然喽,本文并不讨论它在JavaSE下使用,毕竟咱们还是有一定逼格的专栏,需要进阶一把,玩玩它在Spring环境下到底能够迸出怎么样的火花呢?比如静态内部类~


Spring下的静态内部类


static修饰类只有一种情况:那就是这个类属于内部类,这就是我们津津乐道的静态内部类,形如这样:


public class Outer {
    private String name;
    private static Integer age;
    // 静态内部类
    private static class Inner {
        private String innerName;
        private static Integer innerAge;
        public void fun1() {
            // 无法访问外部类的成员变量
            //System.out.println(name);
            System.out.println(age);
            System.out.println(innerName);
            System.out.println(innerAge);
        }
    }
    public static void main(String[] args) {
        // 静态内部类的实例化并不需要依赖于外部类的实例
        Inner inner = new Inner();
    }
}


在实际开发中,静态内部类的使用场景是非常之多的。


认识静态/普通内部类


由于一些小伙伴对普通内部类 vs 静态内部类傻傻分不清,为了方便后续讲解,本处把关键要素做简要对比说明:


  1. 静态内部类可以声明静态or实例成员(属性和方法);而普通内部类则不可以声明静态成员(属性和方法)
  2. 静态内部类实例的创建不依赖于外部类;而普通外部类实例创建必须先有外部类实例才行(绑定关系拿捏得死死的,不信你问郑凯)
  3. 静态内部类不能访问外部类的实例成员;而普通内部类可以随意访问(不管静态or非静态) --> 我理解这是普通内部类能 “存活” 下来的最大理由了吧😄


总之,普通内部类和外部类的关系属于强绑定,而静态内部类几乎不会受到外部类的限制,可以游离单独使用。既然如此,那为何还需要static静态内部类呢,直接单独写个Class类岂不就好了吗?存在即合理,这么使用的原因我个人觉得有如下两方面思考,供以你参考:


  • 静态内部类是弱关系并不是没关系,比如它还是可以访问外部类的static的变量的不是(即便它是private的)
  • 高内聚的体现


在传统Spirng Framework的配置类场景下,你可能鲜有接触到static关键字使用在类上的场景,但这在Spring Boot下使用非常频繁,比如属性配置类的典型应用:


@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
  // server.port = xxx 
  // server.address = xxx
  private Integer port;
  private InetAddress address;
  ...
  // tomcat配置
  public static class Tomcat {
    // server.tomcat.protocol-header = xxx
    private String protocolHeader;
    ...
    // tomcat内的log配置
    public static class Accesslog {
      // server.tomcat.accesslog.enabled = xxx
      private boolean enabled = false;
      ...
    }
  } 
}


这种嵌套case使得代码(配置)的key 内聚性非常强,使用起来更加方便。试想一下,如果你不使用静态内部类去集中管理这些配置,每个配置都单独书写的话,像这样:


@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
}
@ConfigurationProperties(prefix = "server.tomcat", ignoreUnknownFields = true)
public class TomcatProperties {
}
@ConfigurationProperties(prefix = "server.tomcat.accesslog", ignoreUnknownFields = true)
public class AccesslogProperties {
}


这代码,就问你,如果是你同事写的,你骂不骂吧!用臃肿来形容还是个中意词,层次结构体现得也非常的不直观嘛。因此,对于这种属性类里使用静态内部类是非常适合,内聚性一下子高很多~


除了在内聚性上的作用,在Spring Boot中的@Configuration配置类下(特别常见于自动配置类)也能经常看到它的身影:


@Configuration(proxyBeanMethods = false)
public class WebMvcAutoConfiguration {
  // web MVC个性化定制配置
  @Configuration(proxyBeanMethods = false)
  @Import(EnableWebMvcConfiguration.class)
  @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
  @Order(0)
  public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    ...
  }
  @Configuration(proxyBeanMethods = false)
  public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    ...
  }
}


利用静态内部类把相似配置类归并在一个 .java文件 内,这样多个static类还可公用外部类的属性、方法,也是一种高内聚的体现。同时static关键字提升了初始化的优先级,比如本例中的EnableWebMvcConfiguration它会优先于外部类加载~


关于static静态内部类优先级相关是重点,静态内部类的优先级会更高吗?使用普通内部能达到同样效果吗?拍脑袋直接回答是没用的,带着这两个问题,接下来A哥举例领你一探究竟…  


static静态配置类提升配置优先级


自己先构造一个Demo,场景如下:

@Configuration
class OuterConfig {
    OuterConfig() {
        System.out.println("OuterConfig init...");
    }
    @Bean
    static Parent parent() {
        return new Parent();
    }
    @Configuration
    private static class InnerConfig {
        InnerConfig() {
            System.out.println("InnerConfig init...");
        }
        @Bean
        Daughter daughter() {
            return new Daughter();
        }
    }
}


测试程序:


@ComponentScan
public class TestSpring {
    public static void main(String[] args) {
        new AnnotationConfigApplicationContext(TestSpring.class);
    }
}


启动程序,结果输出:

InnerConfig init...
OuterConfig init...
Daughter init...
Parent init...


结果细节:似乎都是按照字母表的顺序来执行的。I在前O在后;D在前P在后;



相关文章
|
22天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
38 0
|
1天前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
4 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
|
3天前
|
XML Java C++
【Spring系列】Sping VS Sping Boot区别与联系
【4月更文挑战第2天】Spring系列第一课:Spring Boot 能力介绍及简单实践
28 0
【Spring系列】Sping VS Sping Boot区别与联系
|
2月前
|
XML 监控 druid
【Java专题_02】springboot+mybatis+pagehelper分页插件+druid数据源详细教程
【Java专题_02】springboot+mybatis+pagehelper分页插件+druid数据源详细教程
|
3月前
|
Java
springboot项目打包瘦身
springboot项目打包瘦身
|
5月前
|
Java 测试技术
Springboot集成JUnit5优雅进行单元测试
Springboot集成JUnit5优雅进行单元测试
|
9月前
|
Java Maven
【Springboot】创建boot工程spring-boot-maven-plugin报红、出错_解决方案
【Springboot】创建boot工程spring-boot-maven-plugin报红、出错_解决方案
313 0
|
9月前
|
SQL druid 前端开发
让SpringBoot不需要Controller、Service、DAO、Mapper,卧槽!这款工具绝了!
让SpringBoot不需要Controller、Service、DAO、Mapper,卧槽!这款工具绝了!
|
10月前
|
缓存 Oracle NoSQL
【Spring学习笔记 六】静态/动态代理实现机制
【Spring学习笔记 六】静态/动态代理实现机制
84 0
|
11月前
|
Java C++ Spring
Spring Boot - ConfigDataEnvironmentPostProcessor(Boot 2.4)搞定配置文件加载优先级
Spring Boot - ConfigDataEnvironmentPostProcessor(Boot 2.4)搞定配置文件加载优先级
236 0