Skip to content

Spring Boot 基于 BeanPostProcessor 结合动态代理的扩展应用(二)

上一篇我们简述了 BeanPostProcessor 自动为标注了自定义注解的 Bean 设置默认值的扩展应用, 这篇文章我们来介绍下 BeanPostProcessor 结合动态代理的扩展应用。

基本原理

BeanPostProcessor 在 Spring 容器初始化时被触发,它允许我们在 Spring 管理的 Bean 实例化后、初始化前或初始化后对 Bean 进行加工处理。通过 BeanPostProcessor 我们可以在 Spring 管理的 Bean 上创建动态代理,增强 Bean 的功能。我们将使用 Java 的 Proxy 类来创建动态代理,代理将拦截目标方法的调用,并添加日志功能。

实现步骤

  1. 创建接口和实现类

首先,我们定义一个简单的接口和一个实现类。

java
public interface ProcessorService {

    void executor();

}

@Service
@Slf4j
public class ProcessorServiceImpl implements ProcessorService {

    @Override
    public void executor() {
        log.info("executor service");
    }

}

编写 BeanPostProcessor 动态代理类

接下来,我们编写一个 BeanPostProcessor 实现类,这个类会在 Spring 初始化 Bean 之后为 ProcessorService 类型的 Bean 创建一个代理对象。

java
@Component
@Slf4j
public class LoggingBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ProcessorService) {
            // 创建动态代理
            return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(),new LoggingInvocationHandler(bean));
        }
        return bean;
    }

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


    private static class LoggingInvocationHandler implements InvocationHandler {

        private final Object target;

        private LoggingInvocationHandler(Object target) {
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 打印日志:方法调用前
            log.info("before method {}", method.getName());
            // 执行目标方法
            Object result = method.invoke(target, args);
            // 打印日志:方法调用后
            log.info("after method {}", method.getName());
            return result;
        }
    }
}

配置 Spring Boot 应用

java
@SpringBootApplication
public class ExtendApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(ExtendApplication.class, args);
        ProcessorService processorService = context.getBean(ProcessorService.class);
        processorService.executor();
    }
}

运行结果

当我们运行这个 Spring Boot 应用时,控制台将输出类似以下内容:

text
2024-12-09 10:29:57.835  INFO 28856 --- [           main] c.s.b.e.p.LoggingBeanPostProcessor       : before method executor
2024-12-09 10:29:57.836  INFO 28856 --- [           main] c.s.b.e.processor.ProcessorServiceImpl   : executor service
2024-12-09 10:29:57.836  INFO 28856 --- [           main] c.s.b.e.p.LoggingBeanPostProcessor       : after method executor

总结

通过实现 BeanPostProcessor,我们可以灵活地对 Spring 管理的 Bean 进行动态代理,从而在不修改 Bean 本身的代码的情况下,增强其功能。在这个示例中,我们为 ProcessorService 类型的 Bean 添加了日志记录功能,利用了 Java 的反射机制和 Proxy 类来创建代理对象。

Released under the MIT License.