OTUser
OTUser

Reputation: 3848

Accessing Spring beans in Runnable thread

I need to access Spring beans(featureService and uxService) in my Runnable Thread below but am getting null value for applicationContext hence I couldnt get Spring beans inside the Runnable. I am wondering if its possible to access spring beans inside a runnable or not? if not please suggest me an alternative approach.

I am using Spring 4.0.6 and Java 8

@Component
public class UserMenuUpdateTask implements Runnable, Serializable, ApplicationContextAware {


    private static final long    serialVersionUID = 3336518785505658027L;

    List<User>                   userNamesList;

    FeatureService               featureService;

    UXService uxService;

    private ApplicationContext   applicationContext;

    public UserMegaMenuUpdateTask() {}

    public UserMegaMenuUpdateTask(List<User> userNamesList) {
        this.userNamesList = userNamesList;
    }

    @Override
    public void run() {
        try {
            for (User user : userNamesList) {

                    featureService = (FeatureService) applicationContext.getBean("featureService");
                    uxService = (UxService) applicationContext.getBean("uxService");                    
                //.........
            }
        } catch (BaseApplicationException ex) {
            throw new BaseApplicationException(ex);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;

    }
}

I am calling the runnable as below

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new UserMenuUpdateTask(activeUsers));

Upvotes: 10

Views: 12738

Answers (3)

Vladislav Gutov
Vladislav Gutov

Reputation: 116

ApplicationContextAware interface works only when object is instantiated inside spring. When you do new UserMenuUpdateTask(activeUsers) spring doesn't know anything about this object hence it doesn't set applicationContext.

Write constructor without parameters, set scope to prototype and get this object from spring, at next line set activeUsers and it should work. Or set application by hands after object creation.

Also I would recommend you to change this lines

    for (User user : userNamesList) {

        featureService = (FeatureService) applicationContext.getBean("featureService");
        uxService = (UxService) applicationContext.getBean("uxService");                    
            //.........
        }

to

    featureService = (FeatureService) applicationContext.getBean("featureService");
    uxService = (UxService) applicationContext.getBean("uxService");      

    for (User user : userNamesList) {
        //.........
    }

Upvotes: 1

Jiri Tousek
Jiri Tousek

Reputation: 12440

ApplicationContextAware is an interface used by Spring to set its context into beans that Spring itself manages (knows about). Since you create the UserMenuUpdateTask yourself, Spring does not even know about the instance and cannot set the field.

If you don't need a new instance of your runnable for every call / every thread (i.e. if the UserMenuUpdateTask is stateless or thread-safe), you can let Spring manage it (through annotations or XML configuration) and use the Spring-instantiated instance in ExecutorService.execute().

If a new instance of UserMenuUpdateTask is needed every time, you'll need to instead make the calling class ApplicationContextAware (assuming instance of it is managed by Spring), and set the pointer to application context in the UserMenuUpdateTask before giving it to the ExecutorService.

Upvotes: 2

Grim
Grim

Reputation: 1974

Spring uses ThreadLocal to store the applicationContext but ExecutorService creates a different Thread where no beans are managed and/or the beanContext does not find any beans.

Follow this instructions instead.

Upvotes: 1

Related Questions