类初始化时机

对于类加载过程中初始化阶段,虚拟机严格规定了有且只有四种情况必须立即对类进行“初始化”:

  1. 遇到new、getstatic、putstatic和invokestatic这4条指令码时,如果类没有初始化,则需要先进行初始化。这4条指令对应的java场景是:
    1. 使用new关键字实例化对象
    2. 读取或设置一个静态字段,不包括被final修饰的、已在编译器把结果放入常量池的静态字段
    3. 调用一个静态方法
  2. 使用java.lang.reflect包方法对类进行发射调用
  3. 当初始化一个类的时候,发现其父类还没有进行过初始化,则需要先初始化其父类。但是对于一个接口来说,只有在用到其父接口的时候才会去初始化。
  4. 当虚拟机启动时,用户需要指定一个要执行的类,虚拟机会先初始化这个类

以上这四种情况被称为是对一个类的主动引用。除此之外所有引用类的方式,都不会触发初始化,被称为被动应用。

下面是几个被动引用的例子,假设

class SuperClass{
    public static int value = 13;

    static {
        System.out.println("SuperClass Init");
    }
}

class SubClass extends SuperClass{
    static {
        System.out.println("SubClass Init");
    }
}


  1. 通过子类引用父类的静态字段,不会导致子类初始化

public class ClassLoadTest {
    public static void main(String[] args) {
        System.out.println(SubClass.value);
    }
}


#=>SuperClass Init

#=>13

对于静态字段,只有直接定义这个字段的类才会被初始化。

  1. 通过数组来定义引用类,不会触发此类的实例化

public class ClassLoadTest {
    public static void main(String[] args) {
        SuperClass[] superClasses = new SuperClass[10];
    }
}


#=>

  1. 常量在编译阶段会存入调用类的常量池,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。

class SuperClass{
    public static final int value = 13;

    static {
        System.out.println("SuperClass Init");
    }
}

public class ClassLoadTest {
    public static void main(String[] args) {
        System.out.print(SuperClass.value);
    }
}

#=>

在编译阶段SuperClass.value的值就被存在了ClassLoadTest的常量池中了,对SuperClass.value的引用实际上相当于ClassLoadTest内部的引用,所以在ClassLoadTest的类文件中并没有SuperClass类的符号引用入口,这两个类在编译后没有任何关系了。


在 Spring Boot 应用中,`MeterRegistry` 和自定义计数器(如 `emailDuplicateCounter`)的初始化时机由 **Spring 容器管理**,具体流程如下: --- ### **1. 初始化顺序** #### **(1) Spring 容器启动阶段** - **`MeterRegistry` 初始化** - Spring Boot 自动配置(`MeterRegistryAutoConfiguration`)会优先创建默认的 `MeterRegistry` 实现(如 `SimpleMeterRegistry` 或 `PrometheusMeterRegistry`,取决于依赖)。 - 默认情况下,`MeterRegistry` 会被注册为 **单例 Bean**,生命周期与 Spring 容器一致。 - **`UserServiceImpl` 初始化** - 当 `UserServiceImpl` 被 Spring 容器管理时(通过 `@Service` 注解),其构造器会在 **依赖注入阶段** 被调用。 - 此时,`MeterRegistry` 已经完成初始化,可以直接注入到 `UserServiceImpl` 中。 #### **(2) 计数器注册时机** - **`Counter.builder().register(meterRegistry)`** - 在 `UserServiceImpl` 的构造器中调用时,`meterRegistry` 参数是已初始化的 Bean。 - 计数器会立即注册到 `MeterRegistry` 中,但 **不会立即开始收集数据**,直到第一次调用 `increment()`。 --- ### **2. 关键代码执行时序** 以下是 Spring Boot 启动时关键步骤的简化时序: 1. **Spring 容器初始化** - 加载 `ApplicationContext`,扫描 `@Component`、`@Service` 等注解。 - 执行 `MeterRegistryAutoConfiguration`,创建 `MeterRegistry` Bean。 2. **`UserServiceImpl` 构造器调用** - Spring 通过构造器注入依赖(`UserRepository`、`RegisterNumberConfig`、`MeterRegistry`)。 - 执行 `Counter.builder().register(meterRegistry)`,将计数器注册到 `MeterRegistry`。 3. **应用就绪** - 所有 Bean 初始化完成后,应用开始接收请求。 - 当发生邮箱重复时,调用 `emailDuplicateCounter.increment()`,计数器值增加。 --- ### **3. 验证初始化时机的代码示例** 可以通过 `@PostConstruct` 注解验证计数器是否在构造器后已就绪: ```java @Service public class UserServiceImpl implements UserService { private final Counter emailDuplicateCounter; public UserServiceImpl(UserRepository userRepository, RegisterNumberConfig registerNumberConfig, MeterRegistry meterRegistry) { System.out.println("构造器调用:注册计数器到 MeterRegistry"); this.emailDuplicateCounter = Counter.builder("user.registration.email.duplicates") .description("Count of duplicate email attempts") .register(meterRegistry); } @PostConstruct public void init() { System.out.println("@PostConstruct:计数器是否已注册? " + (emailDuplicateCounter != null ? "是" : "否")); } } ``` **输出示例**: ``` 构造器调用:注册计数器到 MeterRegistry @PostConstruct:计数器是否已注册? 是 ``` --- ### **4. 常见问题与排查** #### **(1) 计数器未注册?** - **原因**:`MeterRegistry` 未正确注入(如缺少依赖或配置)。 - **排查**: - 检查 `pom.xml` 是否包含 `spring-boot-starter-actuator` 和 `micrometer-core`。 - 查看启动日志,搜索 `MeterRegistry` 相关日志(如 `Initializing MeterRegistry`)。 #### **(2) 计数器数据未更新?** - **原因**:未触发 `increment()` 方法,或 `MeterRegistry` 未配置持久化。 - **排查**: - 确保有重复邮箱的注册请求调用 `addUser()` 方法。 - 访问 `/actuator/metrics/user.registration.email.duplicates` 查看实时值。 #### **(3) 多实例下的计数器隔离** - **场景**:应用部署多个实例时,每个实例的计数器是独立的。 - **解决方案**: - 如需全局统计,需集成外部监控系统(如 Prometheus + Grafana)。 - 或使用 `DistributedMeterRegistry`(如 `PrometheusMeterRegistry` 配合 Pushgateway)。 --- ### **5. 总结** | 阶段 | 行为 | |------|------| | **Spring 容器启动** | 初始化 `MeterRegistry` Bean | | **`UserServiceImpl` 构造** | 注册计数器到 `MeterRegistry` | | **应用运行时** | 调用 `increment()` 更新计数器值 | | **监控访问** | 通过 `/actuator/metrics/<name>` 获取数据 | **关键点**: - 计数器的注册发生在 Spring 容器初始化完成后、应用接收请求前。 - 确保 `MeterRegistry` 和 Actuator 配置正确,计数器名称与访问路径一致。 --- ### **
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值