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。
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) 创建自定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DefaultValue {
String value() default "";
}
(2) 创建示例 Bean
@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 实现类
@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:
@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 字段将被自动赋值:
ExampleBean{name='Default Name', age='100'}
备注: 上述只是阐述 BeanPostProcessor 的扩展, 并没有说设置默认值就需要这样, 直接赋值实际上反而更简单。
总结
BeanPostProcessor 为我们提供了灵活的扩展能力,可以在 Bean 初始化之前自定义逻辑。在本文的示例中,我们通过实现 BeanPostProcessor 实现了自动设置默认值的功能。这种方式可以扩展到更多的场景,通过合理使用 BeanPostProcessor,可以使 Spring 应用更具可扩展性和灵活性,但也需要注意滥用可能导致系统复杂性增加。
备注:下一篇会介绍跟动态代理组合在一起的应用