版本约定
本文内容若没做特殊说明,均基于以下版本:
- 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 静态内部类傻傻分不清,为了方便后续讲解,本处把关键要素做简要对比说明:
- 静态内部类可以声明静态or实例成员(属性和方法);而普通内部类则不可以声明静态成员(属性和方法)
- 静态内部类实例的创建不依赖于外部类;而普通外部类实例创建必须先有外部类实例才行(绑定关系拿捏得死死的,不信你问郑凯)
- 静态内部类不能访问外部类的实例成员;而普通内部类可以随意访问(不管静态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在后;