user6344468
user6344468

Reputation:

How to inject a scoped-proxy bean into a singleton bean by using constructor injection

I'm developing a web application using Spring MVC, and want to have a request scoped Date bean which indicates when each request happens. To define such Date bean, I have written a following bean definition into application context xml.

<bean id="now"
      class="java.util.Date"
      scope="request">
    <aop:scoped-proxy/>
</bean>

Injecting this bean into a singleton bean by using field injection works fine.

public class ASingletonBean {
    @Autowired
    private Date now;
    ...
}

But I don't want to use field injection because it's not recommended. My IDE suggests to use constructor injection instead.

public class ASingletonBean{
    private final Date now;

    @Autowired
    public ASingletonBean(Date now) {
        this.now = now;
    }
}

Now the code above throws a following exception when the application launches.

org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'scopedTarget.java.util.Date#0':
Scope 'request' is not active for the current thread;
consider defining a scoped proxy for this bean if you intend to refer to it from a singleton;
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.

How can I avoid this error?

Upvotes: 1

Views: 4286

Answers (2)

user6344468
user6344468

Reputation:

Though Rafal G's answer makes sense and explains good practices, I've also found a dirty way to solve the problem.

The workaround is to inject not Date bean but a "provider" bean instead. First, you define that provider bean as aop:scoped-proxy bean.

<bean id="currentLocalDateTimeProvider"
      class="com.example.CurrentLocalDateTimeProvider"
      init-method="init"
      scope="request">
    <aop:scoped-proxy/>
</bean>

CurrentLocalDateTimeProvider's is defined as below:

import java.time.LocalDateTime;

public class CurrentLocalDateTimeProvider {
    private LocalDateTime now;

    public void init() {
        now = LocalDateTime.now();
    }

    public LocalDateTime now() {
        return now;
    }
}

And inject this into singleton beans.

public class ASingletonBean {
    @Autowired
    private final CurrentLocalDateTimeProvider provider;
}

Then call it's now method to get the request time stamp.

Hope this helps someone :)

Upvotes: 0

Rafal G.
Rafal G.

Reputation: 4432

Please, don't do that. Creating a new bean (you don't need) on every request is an overhead that can be easily avoided. Instead create a class + bean that implements java.util.function.Supplier for example:

@Component
class DateTimeProvider implements java.util.function.Supplier<Date> {
...
}

and then inject this java.util.function.Supplier<Date> into your `ASingletonBean'. This way, you will be able to get a current date/time when processing a request.

And few additional notes:

  • Use JodaTime or JDK8 JavaTime API instead of java.util.Date,
  • If you cannot use JDK8 (java.util.function.Supplier was added in JDK8) then you can either create your own interface or use one provided by Guava,
  • If you need a very precise timing for the received request then you should consider creating some sort of a "time stamping" filter, most likely by extending org.springframework.web.filter.OncePerRequestFilter class.

Edited - to answer a questions from comment:

  • "why it is preferable to using request-scoped bean" - A bean would always have to be injected to any business component you would create. Seems a bit much for just having a 'request timestamp'.

  • The request object you receive from the outside world should be translated into some sort of a 'domain request' (that contains the timestamp) and then handled internally only in its domain form. (read more on: Hexagonal Architecture a.k.a Ports and Adapters, Domain Driver Design). Why so? Because one can easily imagine that a request that now enters the system only by the means of HTTP request could enter the system in the form of JMS message or batch import operation - then you would only need to provide a new adapter to the system and entire logic inside core domain would not change.

  • If you are using Spring Boot then it is enough to create a bean that extends org.springframework.web.filter.OncePerRequestFilter#OncePerRequestFilter and implement your logic inside the only method that needs to be implemented. Spring will automatically use your filter.

Upvotes: 1

Related Questions