You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring AOP对编程注册的NamedParameterJdbcTemplate Bean不生效如何解决

问题原因
  • 你手动调用registerSingleton()注册NamedParameterJdbcTemplate实例时,只是把裸对象直接存入Spring容器,没有经过Spring Bean生命周期的BeanPostProcessor处理链,而AOP切面的代理逻辑正是由负责AOP的后置处理器(比如AnnotationAwareAspectJAutoProxyCreator)在Bean初始化阶段完成代理生成的,所以这些手动注册的实例没有被切面包裹,自然无法被拦截。
  • 你原来单个Bean的时候是通过@Bean注解声明的,Spring会完整走完Bean的初始化、后置处理流程,所以会自动生成代理对象,切面生效。
修复方案

两种方案任选其一即可:

方案1:手动对实例应用后置处理器再注册

在注册单例之前,先调用initializeBean方法让Spring走完完整的Bean生命周期,再注册进容器,修改配置代码如下:

@Bean
public Map<String, NamedParameterJdbcTemplate> namedParameterJdbcTemplates() {
    
    ConfigurableListableBeanFactory beanFactory =
        ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
    
    Map<String, NamedParameterJdbcTemplate> templates = new HashedMap<>();
    
    dataSources().forEach((key, value) -> {
        // 1. 创建原始实例
        NamedParameterJdbcTemplate rawTemplate = new NamedParameterJdbcTemplate(value);
        // 2. 应用所有Bean后置处理器,完成AOP代理生成
        NamedParameterJdbcTemplate proxiedTemplate = (NamedParameterJdbcTemplate) beanFactory.initializeBean(rawTemplate, key);
        // 3. 注册代理后的实例为单例
        beanFactory.registerSingleton(key, proxiedTemplate);
        // 4. 存入Map的是代理对象
        templates.put(key, proxiedTemplate);
    });
    
    return templates;
}

方案2:注册BeanDefinition交由Spring管理生命周期

这种方式更符合Spring规范,不需要手动处理代理逻辑,只需要给每个数据源注册对应的Bean定义,由Spring自行完成实例化、代理增强流程:

@Bean
public Map<String, NamedParameterJdbcTemplate> namedParameterJdbcTemplates(ApplicationContext applicationContext) {
    DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
    Map<String, DataSource> dataSourceMap = dataSources();
    Map<String, NamedParameterJdbcTemplate> templates = new HashedMap<>();
    
    dataSourceMap.forEach((key, dataSource) -> {
        // 构造Bean定义
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(NamedParameterJdbcTemplate.class);
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(dataSource);
        // 注册Bean定义
        beanFactory.registerBeanDefinition(key, beanDefinition);
        // 从容器获取的就是已经完成代理增强的实例
        templates.put(key, applicationContext.getBean(key, NamedParameterJdbcTemplate.class));
    });
    return templates;
}
验证说明

修改完成后,你可以通过断点查看Map中存储的NamedParameterJdbcTemplate实例,如果类名带有CGLIB或者JDK动态代理标识,说明代理已经生成成功,切面即可正常拦截方法调用。

内容的提问来源于stack exchange,提问作者Frankie Drake

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 05:09:03