5

I'm attempting to update an old Spring application. Specifically, I'm trying to pull all of the beans out of the old xml-defined form and pull them into a @SpringBootApplication format (while dramatically cutting down on the overall number of beans defined, because many of them did not need to be beans). My current issue is that I can't figure out how to make the ServletContext available to the beans that need it.

My current code looks something like this:

package thing;

import stuff

@SpringBootApplication
public class MyApp {

    private BeanThing beanThing = null;

    @Autowired
    private ServletContext servletContext; 

    public MyApp() {
        // Lots of stuff goes here.
        // no reference to servletContext, though
        // beanThing gets initialized, and mostly populated.
    }

    @Bean public BeanThing getBeanThing() { return beanThing; }

    @PostConstruct
    public void populateContext() {
        // all references to servletContext go here, including the
        // bit where we call the appropriate setters in beanThing
    }
}

The error I get back: Field servletContext in thing.MyApp required a bean of type 'javax.servlet.ServletContext' that could not be found.

So... what am I missing? Is there something I'm supposed to be adding to the path? Is there some interface I need to implement? I can't provide the bean myself because the whole point is that I'm trying to access servlet context info (getContextPath() and getRealPath() strings) that I don't myself have.

Ben Barden
  • 2,001
  • 2
  • 20
  • 28

2 Answers2

6

Please be aware of the best practice for accessing the ServletContext: You shouldn't do it in your main application class, but e. g. a controller.

Otherwise try the following:

Implement the ServletContextAware interface and Spring will inject it for you.

Remove @Autowired for the variable.

Add setServletContext method.

@SpringBootApplication
public class MyApp implements ServletContextAware {

    private BeanThing beanThing = null;

    private ServletContext servletContext; 

    public MyApp() {
        // Lots of stuff goes here.
        // no reference to servletContext, though
        // beanThing gets initialized, and mostly populated.
    }

    @Bean public BeanThing getBeanThing() { return beanThing; }

    @PostConstruct
    public void populateContext() {
        // all references to servletContext go here, including the
        // bit where we call the appropriate setters in beanThing
    }

    public void setServletContext(ServletContext servletContext) {
        this.context = servletContext;
    }


}
Bennett Dams
  • 6,463
  • 5
  • 25
  • 45
  • So... I'm currently working in Intellij IDEA, and it complains if I try to implement ServletContextAware without implementing abstract method setServletContext(ServletContext). I could probably leverage that, and I'll try, but could you address that one way or another in your answer? – Ben Barden Apr 26 '18 at 16:21
  • Yes, if you implement that interface, you need to add the `setServletContext` method. Updated my answer. – Bennett Dams Apr 26 '18 at 16:30
  • Cool. Seems to be working. As it turns out, I managed to fit the logic under setServletContext itself, and was able to get rid of populateContext() and the @PostConstruct entirely. Accepted for now, and will stay accepted as long as it doesn't generate some other bug later in the process. – Ben Barden Apr 26 '18 at 16:34
  • As far as "best practice"... there are a *lot* of design decisions in this mess of code that I myself would not have made if I were the one making them for the first time. Hopefully I'll have an opportunity to fix them later. – Ben Barden Apr 26 '18 at 16:37
  • 2
    So... I managed to get to the point where it was actually compiling and running... and for reasons I don't understand, setServletContext isn't ever being called. Do you have any knowledge of this? – Ben Barden May 03 '18 at 18:40
  • 2
    setServletContext is not called for me as well, not working in Spring Boot Starter 2.2.5 – Pali Apr 05 '20 at 13:28
  • I can't get access to ServletContextAware. What namespace is it imported from? Or package? – TheJeff Jun 25 '20 at 19:53
0

Make the bean lazy and use a parameter:

@Lazy
@Bean
public BeanThing getBeanThing(ServletContext servletContext) {
    return new BeanThing(servletContext);
}

It needs to be lazy because the ServletContext won't exist when the instance of the MyApp is created. When the ServletContext becomes available, Spring will remember it somewhere and it will be able to fill in the parameter.

To make this work, you just have to make sure the bean isn't requested before that.

Now you might have to create beans which need BeanThing earlier than that. The solution then is to inject a Provider<BeanThing> and make sure the provider is only used after the ServletContext exists (i.e. not in @PostConstruct or similar).

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820