如何通过AOP切入点表达式仅对指定Spring Bean实例应用通知?
嘿,你的问题其实是切入点表达式的逻辑匹配出了问题——你用@Primary作为筛选条件,但那个被标注@Primary的Bean是JdbcTemplate类型的,而你想拦截的是NamedParameterJdbcTemplate的update方法,这两个条件根本凑不到一块儿,自然不会有匹配结果。
下面给你几种不需要自定义子类或注解的解决方案,都是Spring AOP原生支持的方式:
方案1:直接通过Bean名称匹配(最推荐)
Spring AOP自带bean()切入点,能直接根据Bean名称筛选目标对象,这是最简单直接的方式:
// 匹配所有NamedParameterJdbcTemplate的update方法 @Pointcut("execution(* org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(..))") public void namedParameterUpdate() {} // 精准匹配你要拦截的那个Bean名称,比如jdbcTemplateEk5_contragent @Pointcut("bean(jdbcTemplateEk5_contragent)") public void targetNamedBean() {} // 组合两个切入点,只对目标Bean生效 @Around("namedParameterUpdate() && targetNamedBean()") public Object interceptTargetBeanUpdate(ProceedingJoinPoint joinPoint) throws Throwable { // 这里写你的AOP通知逻辑 System.out.println("拦截到目标Bean的update方法执行"); return joinPoint.proceed(); }
这个方案完全基于你已经定义的Bean名称,不需要修改任何现有Bean的配置,简单高效。
方案2:通过关联的数据源筛选
如果你的Bean是绑定不同数据源的,也可以通过数据源的特征来间接定位目标Bean。比如你想拦截使用dataSourceEK5_contragent的Bean,可以在通知里做判断:
@Pointcut("execution(* org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(..))") public void namedParameterUpdate() {} @Around("namedParameterUpdate()") public Object interceptByDataSource(ProceedingJoinPoint joinPoint) throws Throwable { NamedParameterJdbcTemplate targetBean = (NamedParameterJdbcTemplate) joinPoint.getTarget(); // 获取Bean关联的数据源 DataSource dataSource = targetBean.getJdbcTemplate().getDataSource(); // 匹配数据源的标识(比如你给BasicDataSource设置的id) if ("dataSourceEK5_contragent".equals(((BasicDataSource) dataSource).getId())) { // 执行你的通知逻辑 System.out.println("拦截到指定数据源对应的Bean的update方法"); } return joinPoint.proceed(); }
这种方式适合Bean名称可能变动,但数据源标识固定的场景。
方案3:通过Bean元数据反向匹配
你也可以借助Spring的BeanFactory来获取目标Bean的名称,再做判断:
@Autowired private BeanFactory beanFactory; @Pointcut("execution(* org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(..))") public void namedParameterUpdate() {} @Around("namedParameterUpdate()") public Object interceptByBeanMetadata(ProceedingJoinPoint joinPoint) throws Throwable { // 从BeanFactory中找到目标对象对应的Bean名称 String targetBeanName = Arrays.stream(beanFactory.getBeanNamesForType(NamedParameterJdbcTemplate.class)) .filter(name -> beanFactory.getBean(name) == joinPoint.getTarget()) .findFirst() .orElse(null); // 判断是否是你要拦截的Bean if ("jdbcTemplateEk5_contragent".equals(targetBeanName)) { // 执行通知逻辑 System.out.println("通过元数据匹配到目标Bean并拦截"); } return joinPoint.proceed(); }
这个方案灵活性高,但相比前两种会有一点点性能开销,适合复杂的筛选场景。
为什么你的原切入点不生效?
最后再解释下你原来的问题:
@Around("namedParameterJdbcTemplateUpdate() && beanAnnotatedAsPrimary()")
你的beanAnnotatedAsPrimary()切入点是匹配被@Primary标注的Bean,但你定义的两个NamedParameterJdbcTemplate Bean都没有@Primary——@Primary只在JdbcTemplate类型的jdbcTemplateLocal上,所以这两个条件永远无法同时满足,自然不会匹配到任何方法调用。
内容的提问来源于stack exchange,提问作者62mkv

