首先,在讲解 BeanPostProcessor 的应用之前,我们肯定得先要知道 BeanPostProcessor 是什么,以及它的特性是什么。那么,我们先来看一看它的源码:

// org.springframework.beans.factory.config.BeanPostProcessor
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;
	}
}

可以看到,BeanPostProcessor 接口中定义了两个方法,它们的作用分别是(注意到它们发挥作用的时间点与这两个方法的名字有关):

  • postProcessBeforeInitialization: bean 初始化方法调用前被调用
  • postProcessAfterInitialization: bean 初始化方法调用后被调用

注意到,这两个方法的执行时机都是在 bean 完成实例化之后,且会作为参数传递到方法中(bean 参数即需要操作的容器 bean 实例;beanName 即 bean 实例的名称)。

理解了 BeanPostProcessor 的接口定义之后,我们来看一个它在业务系统中的经典用法。

我们可以思考一个优惠券结算(或核销)的场景:业务系统中通常会定义多种优惠券,比如满减券、满返券、折扣券等等;虽然它们的使用规则都不相同,但是它们的处理接口定义却是一样的。此时,我们就可以通过 BeanPostProcessor 接口来聚合这些优惠券的 “Process Bean”,以避免每一次都要注入所有的优惠券处理器 Bean。下面,我们就来通过代码实现这个功能(特性)。

优惠券枚举类

定义一个枚举类,包含我们的业务系统所支持的所有优惠券类型(可以根据需要自行扩展),如以下代码所示:

@Getter
@AllArgsConstructor
public enum CouponTypeEnum {

    MAN_JIAN("man_jian", "满减券"),
    MAN_FAN("man_fan", "满返券"),
    ZHE_KOU("zhe_kou", "折扣券"),
    ;

    private final String type;
    private final String description;
}
优惠券处理器注解

这个注解用于标注在优惠券处理器类上,用于指定优惠券处理器的类型,即这个处理器是用于处理哪一类优惠券的。如以下代码所示:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CouponHandlerProcessor {

    /** 优惠券类型 */
    CouponTypeEnum value();
}
优惠券处理器接口定义及各个实现类

我们的核心实现是优惠券处理器,它们都有着相同的接口定义,并通过各个实现类达到不同的处理效果;且我们通过注解和枚举类标识出这个 “Service Bean” 是用于处理哪一类优惠券的(可以根据需要自行扩展,例如一个处理器可以处理多类优惠券)。如以下代码所示:

// 优惠券处理器服务接口定义
public interface ICouponHandlerService {

    void process(String coupon);
}

// 满返券优惠券处理器
@Slf4j
@Service
@CouponHandlerProcessor(CouponTypeEnum.MAN_FAN)
public class ManFanCouponHandlerServiceImpl implements ICouponHandlerService {

    @Override
    public void process(String coupon) {
        log.info("....");
    }
}

// 满减券优惠券处理器
@Slf4j
@Service
@CouponHandlerProcessor(CouponTypeEnum.MAN_JIAN)
public class ManJianCouponHandlerServiceImpl implements ICouponHandlerService {

    @Override
    public void process(String coupon) {
        log.info("....");
    }
}

// 折扣券优惠券处理器
@Slf4j
@Service
@CouponHandlerProcessor(CouponTypeEnum.ZHE_KOU)
public class ZheKouCouponHandlerServiceImpl implements ICouponHandlerService {

    @Override
    public void process(String coupon) {
        log.info("....");
    }
}
优惠券处理器工厂

工厂的功能是当你需要哪一种优惠券处理器的时候,就可以通过工厂的方法给到你。我们先来贴出代码,再去解释这里的实现思想。

@Slf4j
@Component
public class CouponHandlerFactory implements BeanPostProcessor {

    private static final Map<CouponTypeEnum, ICouponHandlerService> serviceMap =
            new ConcurrentHashMap<>();

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {

        if (bean instanceof ICouponHandlerService) {
            // 获取对象运行时对象类
            Class<?> clazz = bean.getClass();
            // 获取自定义的注解
            CouponHandlerProcessor annotation = clazz.getAnnotation(CouponHandlerProcessor.class);
            // TODO 是不是可以对 bean 进行校验
            // 绑定对应关系
            serviceMap.put(annotation.value(), (ICouponHandlerService) bean);
            log.info("....");
        }

        return bean;
    }

    public ICouponHandlerService getHandler(CouponTypeEnum type) {
        return serviceMap.get(type);
    }
}

可以看到,我们所实现的优惠券处理器工厂类实现了 BeanPostProcessor,且在 postProcessBeforeInitialization 方法中过滤 IOC 容器中的每一个 bean,获取到 bean 的 CouponHandlerProcessor 注解,识别处理器类型,放到 map 中。那么,当需要某一种优惠券处理器时,就可以通过指定类型返回对应的处理器。