Skip to content

Spring Boot 基于 BeanPostProcessor 扩展点应用示例(一)

在 Spring 中,BeanPostProcessor 是一个强大的扩展点,允许开发者在 Spring 容器实例化和初始化 Bean 时插入自定义逻辑。通过实现 BeanPostProcessor 接口,可以在 Bean 的生命周期中进行额外的处理,例如动态代理、属性设置或校验等操作。 本文介绍讲解如何使用 BeanPostProcessor 实现一个简单的功能:自动为标注了自定义注解的 Bean 设置默认值。

BeanPostProcessor 接口定义

BeanPostProcessor 接口包含两个方法:

  • postProcessBeforeInitialization(Object bean, String beanName):

在 Spring 容器调用任何初始化方法(如 @PostConstruct 注解的方法、init-method 属性指定的方法或实现了 InitializingBean 接口的 afterPropertiesSet 方法)之前执行。返回值可以是原始的 bean 或者是一个新的对象实例(例如代理)。如果返回 null,则该 bean 将不会被添加到容器中。

  • postProcessAfterInitialization(Object bean, String beanName):

在 Spring 容器完成所有初始化工作后执行,包括调用了所有的初始化方法。同样,返回值可以是原始的 bean 或者是一个新的对象实例。返回 null 表示不使用该 bean。

java
public interface BeanPostProcessor {

	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

实现自定义逻辑示例

场景描述

假设我们有一个注解 @DefaultValue,用来标记需要被自动赋值的字段。我们希望通过实现 BeanPostProcessor,在 Bean 初始化时自动为这些字段设置默认值。

实现步骤

(1) 创建自定义注解

java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DefaultValue {

    String value() default "";
}

(2) 创建示例 Bean

java
@Data
public class ExampleBean {

    @DefaultValue("Default Name")
    private String name;

    @DefaultValue("100")
    private String age;

    @Override
    public String toString() {
        return "ExampleBean{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}

(3) 编写 BeanPostProcessor 实现类

java
@Component
public class DefaultValueBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(DefaultValue.class)) {
                DefaultValue defaultValue = field.getAnnotation(DefaultValue.class);
                field.setAccessible(true); // 允许访问私有字段
                try {
                    if (field.get(bean) == null) { // 如果字段值为 null,则设置默认值
                        field.set(bean, defaultValue.value());
                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Failed to set default value for field: " + field.getName(), e);
                }
            }
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

(4) 配置和运行

将 ExampleBean 定义为 Spring 管理的 Bean:

java
@Configuration
public class AppConfig {

    @Bean
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }
}

@SpringBootApplication
public class ExtendApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(ExtendApplication.class, args);
        ExampleBean bean = context.getBean(ExampleBean.class);
        System.out.println(bean);
    }
}

运行程序后,ExampleBean 的 name 和 age 字段将被自动赋值:

java

ExampleBean{name='Default Name', age='100'}

备注: 上述只是阐述 BeanPostProcessor 的扩展, 并没有说设置默认值就需要这样, 直接赋值实际上反而更简单。

总结

BeanPostProcessor 为我们提供了灵活的扩展能力,可以在 Bean 初始化之前自定义逻辑。在本文的示例中,我们通过实现 BeanPostProcessor 实现了自动设置默认值的功能。这种方式可以扩展到更多的场景,通过合理使用 BeanPostProcessor,可以使 Spring 应用更具可扩展性和灵活性,但也需要注意滥用可能导致系统复杂性增加。

备注:下一篇会介绍跟动态代理组合在一起的应用

Released under the MIT License.