46

Following is my aspect:

    @Configurable
    @Aspect
    public class TimingAspect {

        @Autowired
        private HttpServletRequest httpServletRequest;

        // Generic performance logger for any mothod
        private Object logPerfomanceInfo(ProceedingJoinPoint joinPoint, String remoteAddress) {
            StringBuilder tag = new StringBuilder();
            if (joinPoint.getTarget() != null) {
                tag.append(joinPoint.getTarget().getClass().getName());
                tag.append(".");
            }
            tag.append(joinPoint.getSignature().getName());
            StopWatch stopWatch = new StopWatch(tag.toString());
            Object result = joinPoint.proceed(); // continue on the intercepted method
            stopWatch.stop();

            PerformanceUtils.logInPerf4jFormat(stopWatch.getStartTime(), stopWatch.getElapsedTime(), stopWatch.getTag(), stopWatch.getMessage(), remoteAddress);
            return result;
        }

        @Around("execution(* $$$.$$$.$$$.api.controller.*.*(..))")
        public Object logAroundApis(ProceedingJoinPoint joinPoint) throws Throwable {
            String remoteAddress = null;
            if (httpServletRequest != null) {
               remoteAddress = httpServletRequest.getRemoteAddr();
            }
            return logPerfomanceInfo(joinPoint, remoteAddress);
        }

        @Around("execution(* $$$.$$$.$$$.$$$.$$$.$$$.*(..))")
        public Object logAroundService(ProceedingJoinPoint joinPoint) throws Throwable {
            String remoteAddress = null;
            if (httpServletRequest != null) {
                remoteAddress = httpServletRequest.getRemoteAddr();
            }
            return logPerfomanceInfo(joinPoint, remoteAddress);
        }

I do not get any compile time errors but I do following exception when I start my jetty server:

nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

One thing to note here is, if I remove "logAroundService" method, I do not get any exceptions.

hrishikeshp19
  • 8,838
  • 26
  • 78
  • 141

8 Answers8

30

You shouldn't autowire a HttpServletRequest in your aspect as this will tie your aspect to be only runnable for classes that are called from within an executing HttpServletRequest.

Instead use the RequestContextHolder to get the request when you need one.

private String getRemoteAddress() {
    RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
    if (attribs instanceof NativeWebRequest) {
        HttpServletRequest request = (HttpServletRequest) ((NativeWebRequest) attribs).getNativeRequest();
        return request.getRemoteAddr();
    }
    return null;
}
Yusuf K.
  • 4,195
  • 1
  • 33
  • 69
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • Please add your final solution to your question for future reference. – M. Deinum Jun 05 '14 at 06:03
  • 1
    Remember to add the following listener in the web.xml (otherwise attribs will be null): ` org.springframework.web.context.request.RequestContextListener ` – fl4l Dec 21 '15 at 16:08
16

@M. Deinum answer doesn't work for me. I use these codes instead

RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
if (attributes != null) {
    HttpServletRequest request = ((ServletRequestAttributes) attributes).getRequest();
    return request.getRemoteAddr();
}
Saeed Zhiany
  • 2,051
  • 9
  • 30
  • 41
min
  • 953
  • 1
  • 11
  • 23
12

Create bean for RequestContextListener. I got the same error for autowiring HttpServletRequest And the following two lines of code works for me

@Bean
public RequestContextListener requestContextListener() {
    return new RequestContextListener();
}
Avijit Biswas
  • 425
  • 5
  • 11
7

As the error message said: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

To fix it, register a RequestContextListener listener in web.xml file.

<web-app ...>
   <listener>
    <listener-class>
        org.springframework.web.context.request.RequestContextListener
    </listener-class>
   </listener>
</web-app>
Nicholas Lu
  • 1,655
  • 15
  • 14
1

With your pointcut expression, you're basically proxying every bean and applying that advice. Some beans exist and operate outside the context of an HttpServletRequest. This means it cannot be retrieved.

You can only inject the HttpServletRequest in places where a Servlet container request handling thread will pass through.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • all the methods intercepted by all pointcuts are called in the same servlet container request handling thread. – hrishikeshp19 Jun 03 '14 at 22:24
  • If it cannot be retrieved, it should be null, why illegalstate? – hrishikeshp19 Jun 03 '14 at 22:26
  • @riship89 Post your full stack trace to prove it. The other possibility is that you have your aspect loaded by the `ContextLoaderListener` `ApplicationContext` rather than the `DispatcherServlet`'s. – Sotirios Delimanolis Jun 03 '14 at 22:26
  • @riship89 With `@Autowired` you are setting an injection target. You are saying I want this to be managed by Spring. Well, in this case, Spring can't manage it so it crashes. – Sotirios Delimanolis Jun 03 '14 at 22:27
  • Okay, I get it now. many of my beans are in application context and not in servlet context. Now, how can I expose httpservlet request in my whole application? – hrishikeshp19 Jun 03 '14 at 23:53
  • @riship89 Depends. Say you had a background job bean that was working in parallel with the rest of your application but not in the context of a request. Your Aspect would currently be advising it but would fail right away. What request should it inject? There's none running. – Sotirios Delimanolis Jun 03 '14 at 23:55
  • Would it help if servlet context is in application context? Currently, my servlet context is outside my application context. – hrishikeshp19 Jun 04 '14 at 00:13
  • @riship89 Sorry, but that doesn't make much sense. The application context is one context. It contains beans that are should be shared by the whole application. This is loaded by the `ContextLoaderListener`. The servlet context contains beans that should only be relevant to the servlet, ie. the `DispatcherServlet`. Since the servlet is part of the application, it has access to the application context, but not vice versa. Can you clarify? – Sotirios Delimanolis Jun 04 '14 at 00:15
  • makes sense, ignore my comment. Let us say that I do not have any background processes running, and only thing that is running is server, how can I expose httpservletrequest to application context beans? – hrishikeshp19 Jun 04 '14 at 00:18
  • @riship89 Almost everything in an `ApplicationContext` is a bean, even if you didn't declare. In other words, even the Spring infrastructure components are beans. Your Aspect applies to them as well. But the `ApplicationContext` is initialized outside of a request, so your aspect would try to run for any method on any of those beans outside of a request context. Your only solutions are to restrict your aspect or don't inject the `HttpServletRequest`. – Sotirios Delimanolis Jun 04 '14 at 00:21
  • is there any easy way to move all the beans in request scope. Or is there a way to assign scope="request" to all the beans in application context? – hrishikeshp19 Jun 04 '14 at 01:21
0

In case you came here because you searched the error message from the Internet around or after the year 2021... I did that too and eventually realized I had two @Configuration classes that implemented WebMvcConfigurer. Removing the duplicate solved the problem.

Torben
  • 3,805
  • 26
  • 31
0

I solved by adding the following:

@Around(value = "@annotation(JwtSecure)")
@Pointcut("within(@org.springframework.stereotype.Repository *)"
        + " || within(@org.springframework.stereotype.Service *)"
        + "|| within(@org.springframework.stereotype.Component *)"
        + " || within(@org.springframework.web.bind.annotation.RestController *)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Abd Abughazaleh
  • 4,615
  • 3
  • 44
  • 53
0

If you are getting this issue in Spring boot when you have received a request from postman/web and you are trying to spawn a new thread (may be by using CompletableFuture) to get a response. Then you might be trying to get something within the spawned thread which is related to the original request like the sessionId. Instead, you should get them (sessionId and other parameters) in main thread and pass the sessionId and other parameters which is associated to the original request method argument which you'll call in the CompletableFuture. I have shared a screenshot of the same where I am extracting the sessionId from original request and passing it through to the someMethodCall() as a parameter.

enter image description here

Ashish Singh
  • 399
  • 4
  • 11