方面捕获其他切入点



我在启动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!

问题是我不知道我的Pointcut选择了什么类,这导致了这个问题:

我的特性文件:

@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);
}

我没有粘贴所有支持的Pointcut,但您可以看到它们对于@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中的定义仍然相同。即使在我使用execution的地方,它仍然需要匹配其他条件。

更新#3

我创建了一个回购来显示问题:https://github.com/peterkronenberg/aoptest

在启动时,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类被我的方面代码获取。我相信这就是问题的原因,尽管我不确定为什么

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

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
  • 不知怎么的,SpringAOP正试图代理它
  • 它尝试代理它的方式是使用CGLIB代理
  • CGLIB代理通过创建子类和重写方法来工作,以便能够拦截它们并委托给原始方法或跳过它们,无论开发人员喜欢什么
  • Spring框架类OncePerRequestFilter实现了来自Filter的接口方法doFilter,但由于某种原因,它声明为final。也就是说,当代理过滤器时,不能代理父类的最终方法。如果以后在代理实例上调用它,它实际上最终会出现在原始对象中。你不能用方面、顾问或方法拦截器来钩住它
  • 提示";考虑使用基于接口的JDK代理代替";意味着,如果您需要挂接这样一个方法,您可以将Spring配置为创建JDK代理,而不是CGLIB代理。这样,代理将直接实现目标类实现的所有接口的方法,并且您可以挂接这两个最终方法。但在这种情况下,其他没有实现任何接口方法的目标类方法就不能再被拦截了。这是一种权衡,选择对你更好的变体,因为你不可能两者兼得

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


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

相关内容

  • 没有找到相关文章

最新更新