【问题标题】:How can a Spring bean detect if it itself has been wrapped in an AOP proxy?Spring bean 如何检测它本身是否被包装在 AOP 代理中?
【发布时间】:2012-07-16 18:09:05
【问题描述】:

每当执行标记有@Transactional 注释的DAO 方法时,我们使用Spring 的TransactionInterceptor 来设置一些数据库分区信息,使用ThreadLocal。我们需要它才能将我们的查询路由到不同的数据库分区。

这适用于大多数 DAO 方法:

// this causes the invoke method to set a thread-local with the host name of
// the database server the partition is on
@Transactional
public int deleteAll() throws LocalDataException {

问题是当我们需要在 DAO 内部引用 DAO proxy 对象本身时。通常我们必须让调用者传入 proxy-dao:

public Pager<Foo, Long> getPager(FooDao proxyDao) {

这看起来像下面的代码,显然很恶心。

fooDao.getPager(fooDao);

问题在于,当我们在 FooDao 内部时,this 不是我们需要的代理 DAO。

是否有更好的机制让 bean 发现它周围有代理包装器?我查看了Spring AOPUtils,但我看不到找到对象代理的方法。例如,我不想要isAopProxy(...)。我也阅读了Spring AOP docs,但我看不到解决方案,除非我实现自己希望避免的 AOP 本机代码。

我怀疑我可以使用 ApplicationContextAware 实用程序 bean 和 setProxyDao(...) 方法将 DAO 注入到自身中,但这似乎也是一种 hack。任何其他想法如何检测代理以便我可以从 bean 本身中使用它?感谢您的帮助。

【问题讨论】:

  • 使用本机 Aspectj 加载/编译时编织根本不是一个选项 - 那么建议将编织到代理中,您不应该在代理中遇到代理问题和此引用?跨度>
  • this 不会做@Thorbjørn 因为正如帖子所述,我需要代理而不是 bean 本身。
  • 编写自己的原生 AOP 可能是我唯一的解决方案 @Biju。如果可以的话,我希望能避免它。谢谢你。
  • 绝对是@Thorbjørn。无法替换或包装this。 bean 本身看到thisnot 是其他所有人都被注入的代理。这就是 AOP 的工作原理。

标签: java spring proxy aop


【解决方案1】:

考虑到 AspectJ 编译时间或加载时间编织对您不起作用,按照您的建议提供一个 hacky 解决方案:

按照以下思路创建接口:

public interface ProxyAware<T> {
    void setProxy(T proxy);
}

让你的 Dao 实现 ProxyAware 实现,现在创建一个带有 Ordered 接口的 BeanPostProcessor 以最后运行,如下所示:

public class ProxyInjectingBeanPostProcessor implements BeanPostProcessor, Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (AopUtils.isAopProxy((bean))){
            try {
                Object target = ((Advised)bean).getTargetSource().getTarget();
                if (target instanceof ProxyAware){
                    ((ProxyAware) target).setProxy(bean);
                }
            } catch (Exception e) {
                // ignore
            }
        }
        return bean;
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

它很丑,但是很管用。

【讨论】:

  • 我最终删除了Ordered,因为它似乎对我的 AOP 有不利影响,因为某种原因。但除此之外它正在工作。也许您应该删除Ordered?再次感谢。
【解决方案2】:

Spring 提供了一个方便的静态实用程序AopContext.currentProxy() 方法,该方法将代理返回到调用它的对象。

虽然使用它被认为是一种不好的做法,但在 Java EE 中也存在语义上相同的方法:SessionContext.getBusinessObject()

我写了几篇关于这个实用方法和各种陷阱的文章:123

【讨论】:

  • 我在拨打电话时开始说我不是代理中,因此没有当前代理。但是我没有理由不能将getPager() 方法标记为@Transactional,在这种情况下我会这样做。所以这很有帮助@Tomasz。谢谢!
【解决方案3】:

使用 Spring 将 bean 引用注入 bean,甚至是同一个 bean,就像您对任何其他 bean 引用一样。无需特殊操作。

这种变量的存在在类设计中明确承认该类期望以某种方式被代理。这不一定是坏事,因为 aop 可以改变破坏类契约的行为。

bean 引用通常用于接口,而该接口甚至可以是自引用内部方法的不同接口。

保持简单。那就是疯狂。 :-)

更重要的是,确保语义有意义。这样做的需要可能是代码味道,该类混合了多个职责,最好分解为单独的 bean。

【讨论】:

  • 谢谢肯特。我希望不必将这种注入到我所有的 DAO 中。 BeanPostProcessor 似乎在工作,但我会记住这一点。
猜你喜欢
  • 2010-10-29
  • 1970-01-01
  • 2011-01-18
  • 1970-01-01
  • 2015-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-30
相关资源
最近更新 更多