我在启动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!
这意味着
- 在应用程序的某个位置(或依赖项中),您有一个扩展
OncePerRequestFilter
的Filter
类 - 不知怎么的,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..**)
,就像我之前建议的那样。所以我之前所说的一切仍然成立。您既没有报告是否尝试过这种方法以及效果如何,也没有说明是否发现了扩展OncePerRequestFilter
和GenericFilterBean
的bean。