Reputation: 1296
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.
@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?
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.
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
Upvotes: 2
Views: 663
Reputation: 67317
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
Filter
class extending OncePerRequestFilter
andOncePerRequestFilter
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.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
.
Upvotes: 1