方面捕获额外的切入点

发布于 2025-01-10 03:54:46 字数 7026 浏览 2 评论 0原文

我在 Spring Boot 服务启动时收到这些消息。

Unable to proxy interface-implementing method [public final void org.springframework.web.filter.OncePerRequestFilter.doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse,javax.servlet.FilterChain) throws javax.servlet.ServletException,java.io.IOException] because it is marked as final: Consider using interface-based JDK proxies instead!

Unable to proxy interface-implementing method [public final void org.springframework.web.filter.GenericFilterBean.init(javax.servlet.FilterConfig) throws javax.servlet.ServletException] because it is marked as final: Consider using interface-based JDK proxies instead!

问题是我无法弄清楚我的切入点选择了哪些类导致了这种情况:

我的方面文件:

@Around("execution(public * com.torchai..*.*(..)) "
        + "&& loggingEnabledAndNotDisabled()"
        + "&& !allPublicControllerMethodsNotDisabled()"
        + "&& !allPublicControllerLayerMethodsNotDisabled()"
        + "&& !allPublicServiceMethodsNotDisabled()"
        + "&& !allPublicServiceLayerMethodsNotDisabled()")
public Object allPublicMethodsNotDisabledAndNotControllerOrService(
              final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    return log(proceedingJoinPoint, LogEvent.LAYER_OTHER);
}

@AfterThrowing(
        pointcut = "execution(public * com.torchai..*.*(..)) "
                + "&& loggingEnabledAndNotDisabled()"
                + "&& !allPublicControllerMethodsNotDisabled()"
                + "&& !allPublicControllerLayerMethodsNotDisabled()"
                + "&& !allPublicServiceMethodsNotDisabled()"
                + "&& !allPublicServiceLayerMethodsNotDisabled()",
        throwing = "t")
public void allPublicMethodsNotDisabledAndNotControllerOrService(
            final JoinPoint joinPoint, final Throwable t) {
    onException(joinPoint, t, LogEvent.LAYER_OTHER);
}

我没有粘贴所有支持的切入点,但您可以看到它们对于 @Around 是相同的@AfterThrowing。这些切点此时不应选择任何内容。

我发现真正有趣的是,如果我完全注释掉 @Around 代码,问题仍然存在。

但是,如果我保留 @Around 代码并注释掉 @AfterThrowing,问题就消失了。

因此,即使这两件事在它们选择的内容方面应该是等效的,但出于某种原因 @AfterThrowing 正在选择导致问题的内容。

更新

@kriegaex 感谢您的回复。这是我的其他切入点。只是想让事情变得简单,不包括它们。


    @Pointcut("@annotation(com.torchai.service.aspect.annotations.AspectLog) "
            + "|| @target(com.torchai.service.aspect.annotations.AspectLog)")
    public void methodOrClassLoggingEnabled() {
    }

    @Pointcut("!@annotation(com.torchai.service.aspect.annotations.AspectNoLog)")
    public void methodLoggingNotDisabled() {
    }

    @Pointcut("methodOrClassLoggingEnabled() && methodLoggingNotDisabled()")
    public void loggingEnabledAndNotDisabled() {
    }

    /**
     * Any public methods in classes which are specifically annotated as a Controller
     */
    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *) ||"
            + "within(@org.springframework.stereotype.Controller *)")
    public void allPublicControllerMethods() {
    }

    /**
     * Any public methods in classes which are specifically annotated as a Service
     */
    @Pointcut("within(@org.springframework.stereotype.Service *)")
    public void allPublicServiceMethods() {
    }

    // Controller classes are enabled by default, so don't need to be specifically enabled.
    @Pointcut("allPublicControllerMethods() "
            + "&& methodLoggingNotDisabled()")
    public void allPublicControllerMethodsNotDisabled() {
    }

    // Service classes are enabled by default, so don't need to be specifically enabled.
    @Pointcut("allPublicServiceMethods() "
            + "&& methodLoggingNotDisabled()")
    public void allPublicServiceMethodsNotDisabled() {
    }

    // Components which aren't Service classes, but in the service package need to be enabled with @AspectLog
    @Pointcut("execution(public * com.torchai.service..service..*(..)) "
            + "&& loggingEnabledAndNotDisabled()")
    public void allPublicServiceLayerMethodsNotDisabled() {
    }

    // Components which aren't Controller classes, but in the controller package need to be enabled with @AspectLog
    @Pointcut("execution(public * com.torchai.service..controller..*(..)) "
            + "&& loggingEnabledAndNotDisabled()")
    public void allPublicControllerLayerMethodsNotDisabled() {
    }

    @Around("allPublicControllerMethodsNotDisabled() "
            + "|| allPublicControllerLayerMethodsNotDisabled()")
    public Object logController(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        return log(proceedingJoinPoint, LogEvent.LAYER_CONTROLLER);
    }

    @Around("allPublicServiceMethodsNotDisabled() "
            + "|| allPublicServiceLayerMethodsNotDisabled()")
    public Object logService(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        return log(proceedingJoinPoint, LogEvent.LAYER_SERVICE);
    }

我理解该消息的基本含义,尽管不可否认,我不确定如何做 JDK 代理。但我认为其实没有必要。我们确实有一个实现了 OncePerRequestFilter 的 Filter 类,但我不能确定这就是被选中的那个,因为它没有说。

但我真正的困惑不仅仅是一个我没有预料到的类被拾取,而且它似乎只被 @AfterThrowing 拾取,而不是被 @Around< /代码> .为什么这些不完全相同?

更新 #2(几分钟后)

我认为这可能与我使用的 execution 有关,而不是您对 within 的建议。我不完全理解其中的区别。但 @AfterThrowing@Around 中的定义仍然相同。即使我使用执行,它仍然需要匹配其他条件。

更新 #3

我创建了一个显示问题的存储库: https://github.com/peterkronenberg/aoptest< /a>

在启动时,Springboot 发出以下消息:

2022-03-24 15:58:57.261  INFO 35104 --- [           main] o.s.aop.framework.CglibAopProxy          : Unable to proxy interface-implementing method [public final void org.springframework.web.filter.OncePerRequestFilter.doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse,javax.servlet.FilterChain) throws javax.servlet.ServletException,java.io.IOException] because it is marked as final: Consider using interface-based JDK proxies instead!
2022-03-24 15:58:57.261  INFO 35104 --- [           main] o.s.aop.framework.CglibAopProxy          : Unable to proxy interface-implementing method [public final void org.springframework.web.filter.GenericFilterBean.init(javax.servlet.FilterConfig) throws javax.servlet.ServletException] because it is marked as final: Consider using interface-based JDK proxies instead!
2022-03-24 15:58:57.310 ERROR 35104 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Exception starting filter [authFilter]

服务未启动,这不是我最初看到的。但也许这提供了更多线索。但是,如果我禁用了 Aspect 代码,只需注释掉 AspectLayer 类中的 @Aspect 注释,那么它似乎就可以工作。 不知何故,我的方面代码和过滤器代码之间存在冲突,

希望我的方面代码选取我的 AuthFilter 类。我相信这就是导致问题的原因,尽管我不确定为什么

I've been getting these messages at startup of my Spring Boot service.

Unable to proxy interface-implementing method [public final void org.springframework.web.filter.OncePerRequestFilter.doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse,javax.servlet.FilterChain) throws javax.servlet.ServletException,java.io.IOException] because it is marked as final: Consider using interface-based JDK proxies instead!

Unable to proxy interface-implementing method [public final void org.springframework.web.filter.GenericFilterBean.init(javax.servlet.FilterConfig) throws javax.servlet.ServletException] because it is marked as final: Consider using interface-based JDK proxies instead!

The problem's I can't figure out what classes are getting selected by my Pointcut that is causing this:

My Aspect file:

@Around("execution(public * com.torchai..*.*(..)) "
        + "&& loggingEnabledAndNotDisabled()"
        + "&& !allPublicControllerMethodsNotDisabled()"
        + "&& !allPublicControllerLayerMethodsNotDisabled()"
        + "&& !allPublicServiceMethodsNotDisabled()"
        + "&& !allPublicServiceLayerMethodsNotDisabled()")
public Object allPublicMethodsNotDisabledAndNotControllerOrService(
              final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    return log(proceedingJoinPoint, LogEvent.LAYER_OTHER);
}

@AfterThrowing(
        pointcut = "execution(public * com.torchai..*.*(..)) "
                + "&& loggingEnabledAndNotDisabled()"
                + "&& !allPublicControllerMethodsNotDisabled()"
                + "&& !allPublicControllerLayerMethodsNotDisabled()"
                + "&& !allPublicServiceMethodsNotDisabled()"
                + "&& !allPublicServiceLayerMethodsNotDisabled()",
        throwing = "t")
public void allPublicMethodsNotDisabledAndNotControllerOrService(
            final JoinPoint joinPoint, final Throwable t) {
    onException(joinPoint, t, LogEvent.LAYER_OTHER);
}

I'm not pasting in all the supporting Pointcuts, but you can see they are the same for @Around and @AfterThrowing. These points cuts should not be selecting anything at this point.

What I find really interesting is if I totally comment out the @Around code, the problem is still there.

But if I leave the @Around code and comment out @AfterThrowing, the problem is gone.

So even though these 2 things should be equivalent in terms of what they select, for some reason @AfterThrowing is selecting something that is causing the problem.

Update

@kriegaex Thanks for your through response. Here are my other pointcuts. Was just trying to keep things simple by not including them.


    @Pointcut("@annotation(com.torchai.service.aspect.annotations.AspectLog) "
            + "|| @target(com.torchai.service.aspect.annotations.AspectLog)")
    public void methodOrClassLoggingEnabled() {
    }

    @Pointcut("!@annotation(com.torchai.service.aspect.annotations.AspectNoLog)")
    public void methodLoggingNotDisabled() {
    }

    @Pointcut("methodOrClassLoggingEnabled() && methodLoggingNotDisabled()")
    public void loggingEnabledAndNotDisabled() {
    }

    /**
     * Any public methods in classes which are specifically annotated as a Controller
     */
    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *) ||"
            + "within(@org.springframework.stereotype.Controller *)")
    public void allPublicControllerMethods() {
    }

    /**
     * Any public methods in classes which are specifically annotated as a Service
     */
    @Pointcut("within(@org.springframework.stereotype.Service *)")
    public void allPublicServiceMethods() {
    }

    // Controller classes are enabled by default, so don't need to be specifically enabled.
    @Pointcut("allPublicControllerMethods() "
            + "&& methodLoggingNotDisabled()")
    public void allPublicControllerMethodsNotDisabled() {
    }

    // Service classes are enabled by default, so don't need to be specifically enabled.
    @Pointcut("allPublicServiceMethods() "
            + "&& methodLoggingNotDisabled()")
    public void allPublicServiceMethodsNotDisabled() {
    }

    // Components which aren't Service classes, but in the service package need to be enabled with @AspectLog
    @Pointcut("execution(public * com.torchai.service..service..*(..)) "
            + "&& loggingEnabledAndNotDisabled()")
    public void allPublicServiceLayerMethodsNotDisabled() {
    }

    // Components which aren't Controller classes, but in the controller package need to be enabled with @AspectLog
    @Pointcut("execution(public * com.torchai.service..controller..*(..)) "
            + "&& loggingEnabledAndNotDisabled()")
    public void allPublicControllerLayerMethodsNotDisabled() {
    }

    @Around("allPublicControllerMethodsNotDisabled() "
            + "|| allPublicControllerLayerMethodsNotDisabled()")
    public Object logController(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        return log(proceedingJoinPoint, LogEvent.LAYER_CONTROLLER);
    }

    @Around("allPublicServiceMethodsNotDisabled() "
            + "|| allPublicServiceLayerMethodsNotDisabled()")
    public Object logService(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        return log(proceedingJoinPoint, LogEvent.LAYER_SERVICE);
    }

I understand the basic meaning of the message, although admittedly, I'm not sure how to do the JDK proxy instead. But I don't think it really necessary. We do have a Filter class which implements OncePerRequestFilter, but I can't be sure that that is the one getting picked up, because it doesn't say.

But my real confusion is not just that a class is getting picked up that I didn't expect, but that it seems to only get picked up by the @AfterThrowing and not by @Around . Why wouldn't these be exactly the same?

Update #2 (a few minutes later)

I'm thinking this might have something to do with my used of execution as opposed to your suggestion of within. I don't fully understand the difference. But still, the definitions in @AfterThrowing and @Around are the same. And even where I'm using execution, it still needs to match the other conditions.

Update #3

I've created a repo that shows the problem: https://github.com/peterkronenberg/aoptest

At startup, Springboot issues these messages:

2022-03-24 15:58:57.261  INFO 35104 --- [           main] o.s.aop.framework.CglibAopProxy          : Unable to proxy interface-implementing method [public final void org.springframework.web.filter.OncePerRequestFilter.doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse,javax.servlet.FilterChain) throws javax.servlet.ServletException,java.io.IOException] because it is marked as final: Consider using interface-based JDK proxies instead!
2022-03-24 15:58:57.261  INFO 35104 --- [           main] o.s.aop.framework.CglibAopProxy          : Unable to proxy interface-implementing method [public final void org.springframework.web.filter.GenericFilterBean.init(javax.servlet.FilterConfig) throws javax.servlet.ServletException] because it is marked as final: Consider using interface-based JDK proxies instead!
2022-03-24 15:58:57.310 ERROR 35104 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Exception starting filter [authFilter]

The service doesn't start up, which is not what I was seeing originally. But maybe that provides more clues. But if I disabled the Aspect code, simply by commenting out the @Aspect annotation in the AspectLayer class, then it seems to work.
Somehow, there is a conflict between my Aspect code and my Filter code

I do not want my AuthFilter class to be picked up by my Aspect code. I believe that's what is causing the problem, although I'm not sure why

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

半城柳色半声笛 2025-01-17 03:54:46

让我们检查带有一些额外换行符的错误消息:

Unable to proxy interface-implementing method
  public final void org.springframework.web.filter.OncePerRequestFilter.doFilter(
    javax.servlet.ServletRequest,
    javax.servlet.ServletResponse,
    javax.servlet.FilterChain
  )
  throws javax.servlet.ServletException,java.io.IOException

because it is marked as final:
  Consider using interface-based JDK proxies instead!

这意味着

  • 在您的应用程序(或依赖项)中的某个位置,您有一个以
  • 某种方式 扩展 OncePerRequestFilterFilter 类Spring AOP 正在尝试代理它。
  • 它尝试代理的方式是使用 CGLIB 代理。
  • CGLIB 代理通过创建子类和重写方法来工作,以便能够拦截它们并委托给原始类或跳过它们,无论开发人员喜欢什么。
  • Spring 框架类 OncePerRequestFilter 实现了 Filter 的接口方法 doFilter,但由于某种原因将其声明为 final。即,代理过滤器时,无法代理来自父类的final方法。如果稍后您在代理实例上调用它,它实际上最终会出现在原始对象中。您无法使用方面、顾问程序或方法拦截器来连接它。
  • 提示“考虑使用基于接口的 JDK 代理”意味着如果您需要挂钩这样的方法,您可以配置 Spring 创建 JDK 代理而不是 CGLIB 代理。这样,代理将直接实现目标类实现的所有接口的方法,并且您可以挂钩这两个最终方法。但在这种情况下,其他未实现任何接口方法的目标类方法就无法再被拦截。这是一种权衡,选择更适合您的变体,因为您无法同时拥有两者。

现在,关于您对 Spring AOP 完全针对某些类的惊讶,您需要了解许多切入点正在动态评估,特别是当它们包含诸如 this()之类的内容时target()@(target) 等需要运行时信息。用户常常会惊讶于他们的通用切入点似乎“代理了世界”,甚至经常包括 Spring 自己的框架 bean。通常,您可以通过使用诸如 && 之类的内容来确定切入点的范围来限制这一点。在(org.acme.myapp..**)内。你可以尝试一下。由于无法看到您的许多切入点,我只能推测它们可能包含什么内容,但就像软件开发中经常出现的那样,魔鬼在于细节。


问题编辑后更新:就像我之前推测的那样,您正在使用动态切入点,例如@target@annotation以及一堆inside(@org.springframework..* *) 内容也针对 Spring 自己的 bean。但我缺少的是一些特定于应用程序的包范围 &&在(org.acme.myapp..**)内,就像我之前建议的那样。所以我之前所说的一切仍然有效。您既没有报告是否尝试过该操作以及效果如何,也没有说明是否发现扩展了 OncePerRequestFilterGenericFilterBean 的 bean。

Let us inspect the error message with some extra line breaks:

Unable to proxy interface-implementing method
  public final void org.springframework.web.filter.OncePerRequestFilter.doFilter(
    javax.servlet.ServletRequest,
    javax.servlet.ServletResponse,
    javax.servlet.FilterChain
  )
  throws javax.servlet.ServletException,java.io.IOException

because it is marked as final:
  Consider using interface-based JDK proxies instead!

What that means is that

  • somewhere in your application (or in a dependency) you have a Filter class extending OncePerRequestFilter and
  • somehow Spring AOP is trying to proxy it.
  • The way it is trying to proxy it is by using CGLIB proxies.
  • CGLIB proxies work by creating subclasses and overriding methods so as to be able to intercept them and delegate to the original ones or skip them, whatever the developer prefers.
  • Spring framework class OncePerRequestFilter implements an interface method doFilter from Filter, but for some reason declares it final. I.e., when proxying the filter, the final method from the parent class cannot be proxied. If later you call it on the proxy instance, it actually ends up in the original object. You cannot hook into it with aspects, advisors or method interceptors.
  • The hint "consider using interface-based JDK proxies instead" means that if you need to hook into such a method, you could configure Spring to create a JDK proxy instead of a CGLIB proxy. That way, the proxy would directly implement methods of all interfaces the target class implements, and you could hook into those two final methods. But in that case, other target class methods which do not implement any interface methods cannot be intercepted anymore. It is a trade-off, choose whichever variant is better for you, because you cannot have both.

Now with regard to your astonishment that some classes are being targeted at all by Spring AOP, you need to understand that many pointcuts are being evaluated dynamically, especially if they contain things like this(), target(), @(target) etc. which need runtime information. Users are often surprised that their generic pointcuts seem to "proxy the world", often even including Spring's own framework beans. Usually, you can limit that by scoping the pointcuts with something like && within(org.acme.myapp..**). You can try that. Not being able to see your many pointcuts, I can only speculate what they might contain, but like so often in software development the devil is in the details.


Update after question edit: Like I speculated before, you are using dynamic pointcuts such as @target and @annotation and a bunch of within(@org.springframework..* *) stuff which also targets Spring's own beans. But what I am missing is some application-specific package scoping && within(org.acme.myapp..**), like I suggested before. So all of what I said before still holds. Neither have you reported back on whether you tried that and what the effect was, nor have you said if you found the bean(s) extending OncePerRequestFilter and GenericFilterBean.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文