如何在构造方法之前执行逻辑

97 阅读3分钟
方法适用场景是否真正“在构造函数之前”
静态代码块类加载时初始化✅ 是(比构造函数更早)
工厂方法每次实例化前执行逻辑✅ 是(逻辑在构造函数外部)
AOP/代理对多个类统一处理✅ 是(通过字节码增强)
构造函数内部逻辑初始化对象前做一些准备❌ 否(逻辑在构造函数内)

在 Java 对象实例化过程中,构造方法是创建对象的必经之路,但确实有几种技术可以在构造方法执行之前执行逻辑。以下是几种实现方式:

1. 静态初始化块(类级别)

在类加载时执行,早于任何实例的创建(包括构造方法):

public class MyBean {
    // 静态初始化块 - 在类加载时执行
    static {
        System.out.println("静态初始化块执行(构造方法之前)");
    }
    
    public MyBean() {
        System.out.println("构造方法执行");
    }
}

特点

  • 只执行一次(类加载时)
  • 无法访问实例成员
  • 适用于全局初始化(如加载静态资源)

2. 实例初始化块(对象级别)

在每次创建对象时,在构造方法之前执行:

public class MyBean {
    // 实例初始化块 - 每次实例化时在构造方法前执行
    {
        System.out.println("实例初始化块执行(构造方法之前)");
    }
    
    public MyBean() {
        System.out.println("构造方法执行");
    }
}

执行顺序

  1. 静态初始化块(首次加载类时)
  2. 实例初始化块(每次实例化时)
  3. 构造方法

3. 使用工厂模式(最灵活)

通过工厂方法控制实例化过程:

public class MyBean {
    private MyBean() {
        System.out.println("构造方法执行");
    }
    
    public static MyBean createInstance() {
        System.out.println("前置逻辑执行(构造方法之前)");
        return new MyBean();
    }
}

// 使用方式
MyBean bean = MyBean.createInstance();

4. 使用依赖注入框架(如Spring)

通过@Bean工厂方法实现:

@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        // 构造方法前的逻辑
        System.out.println("执行前置逻辑");
        return new MyBean();  // 实际构造
    }
}

5. 字节码增强技术(高级用法)

使用字节码操作库(如 ASM、Byte Buddy)在编译期/加载期修改字节码:

new ByteBuddy()
    .subclass(MyBean.class)
    .method(named("<init>"))
    .intercept(SuperMethodCall.INSTANCE.andThen(
        MethodCall.invoke(MyBean.class.getMethod("postConstructor"))
    )
    .make();

关键区别总结

方式执行时机执行次数能否访问实例成员
静态初始化块类加载时一次
实例初始化块每次实例化时多次是(部分初始化)
工厂方法实例化前多次是(完全控制)
Spring @Bean 工厂容器初始化时一次

实际应用建议

  1. 常规需求:使用实例初始化块(简单直接)
  2. 复杂初始化:使用工厂模式(完全控制流程)
  3. 框架集成:使用 Spring 的 @Bean 工厂方法
  4. 全局初始化:静态初始化块 + 静态方法

重要提示:在构造方法之前执行的逻辑中,无法访问尚未初始化的实例成员(因为对象尚未完全构造)。此时访问的成员变量可能是默认值(0、null等),而非构造方法中设置的值。