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在后;



相关文章
|
9天前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
21 2
|
1月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
55 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
1月前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
63 2
|
5月前
|
运维 Java 关系型数据库
Spring运维之boot项目bean属性的绑定读取与校验
Spring运维之boot项目bean属性的绑定读取与校验
54 2
|
5月前
|
存储 运维 Java
Spring运维之boot项目开发关键之日志操作以及用文件记录日志
Spring运维之boot项目开发关键之日志操作以及用文件记录日志
63 2
|
5月前
|
Java Maven
springboot项目打jar包后,如何部署到服务器
springboot项目打jar包后,如何部署到服务器
435 1
|
5月前
|
XML 运维 Java
Spring运维之boot项目打包jar和插件运行并且设置启动时临时属性和自定义配置文件
Spring运维之boot项目打包jar和插件运行并且设置启动时临时属性和自定义配置文件
55 1
|
5月前
springboot2.4.5使用pagehelper分页插件
springboot2.4.5使用pagehelper分页插件
157 0
|
5月前
|
缓存 运维 Java
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
49 0
|
6月前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
69 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
下一篇
无影云桌面