1

Say I have the following class...

@Controller
public class WebController {
    @Autowired PersonService personService;


    @RequestMapping(value = "/get", method = RequestMethod.GET)
    @ResponseBody
    @Scope("session")
    public List<Player> getPerson(String personName) {
        return playerService.getByName(personName);
    }
}

Now this invokes the following service...

@Service("playerService")
public class PlayerServiceImpl implements PlayerService {
private List<Player> players;
@Override
    @Transactional
    public List<Player> getByName(final String name) {
        if (players == null) {
            players = getAll();
        }
        return getValidPlayers(name);
    }

If I initially start my application, players is null, correctly, then when in the same session, I invoke this method again with a new value, players is no longer null, as you would expect. However, no new thread appears to be being created, if I open a new browser window (therefore creating a new session) and invoke this method, it still has the values from the previous session.

Why is @Scope("session") not creating a new thread in the thread pool?

I've specified <context:component-scan base-package="com." /> in my servlet-context as expected, everything works fine apart from the service methods are all acting as singletons rather than creating a new thread per session like say a Java EE container.

If players was marked as static I'd understand.

I've also tried marking my controller as @Scope("session") (as shown below) but this appears to have no impact either. What's the best way to make my Spring app create a new thread for a new session?

@Controller
@Scope("session")
public class PlayerController {
Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
David
  • 19,577
  • 28
  • 108
  • 128
  • Aside from soulcheck's answer, you have a few points wrong. A session is not equivalent to a thread (in a synchronous servlet container, a thread serves a request, but requests for the same session might be served by different threads; threads are used whether the application is stateful or stateless). And a new browser window will only create a new session when used in incognito mode, otherwise the cookies are shared and you obviously get the same session. – Frank Pavageau May 12 '13 at 19:58
  • http://stackoverflow.com/a/1367680/106261 – NimChimpsky May 12 '13 at 20:55
  • @NimChimpsky hah, only now i looked into that link. Could have saved me some typing :) – soulcheck May 12 '13 at 21:06
  • @FrankPavageau sorry I should've said, when I open a new browser window in a different browser, like say one in IE and one in Chrome, where the session is not shared. – David May 13 '13 at 07:22

2 Answers2

3

You are using @Scope annotation the wrong way.

Quoting the docs:

When used as a type-level annotation in conjunction with the Component annotation, indicates the name of a scope to use for instances of the annotated type.

When used as a method-level annotation in conjunction with the Bean annotation, indicates the name of a scope to use for the instance returned from the method.

So you can annotate either a spring component bean or a method that creates a bean if you're using java config. Java config is the only reason it even compiles (it wouldn't in pre 3.0 spring)

In your case that annotation is on a normal bean method where it doesn't mean anything.

Solving the right problem

It looks like you're trying to implement db cache by storing query results in a List<Player> players.

Don't do that. Use one of the prebuilt cache abstractions (spring has a very nice one) instead.

So where should @Scope go?

Annotating @Controller with @Scope("session") won't help as it will create session scoped controllers but the service they have injected is still a singleton.

Annotating only Service bean won't work either, cause @Controller is a singleton and it's dependencies are autowired on application startup.

Annotating both @Service and @Controller might work, but seems a bit heavy handed.

It's better to avoid state at all.

Community
  • 1
  • 1
soulcheck
  • 36,297
  • 6
  • 91
  • 90
  • Ah ok - how would I modify my spring controller so that it creates a new thread for each session? I tried adding `@Scope("session")` on the controller and that had no impact either - updated question. – David May 12 '13 at 19:37
  • @david99world see the updates, i think you're trying to solve wrong problem – soulcheck May 12 '13 at 21:05
  • Just on the cache issue, I was using Hibernates second level cache, would it still be better to use a Spring based one? – David May 13 '13 at 07:25
  • @david99world it really depends, but this case hibernate should be enough – soulcheck May 13 '13 at 08:08
  • Yeah, the reason I changed it to use in memory was that this seemed faster than using Hibernates second level cache. Should I never use local variables so my app is always thread safe? – David May 13 '13 at 08:22
  • @david99world method-local are ok, but bean members should be avoided as there is a strong preference for singleton beans in spring and those are easier to manage when stateless. – soulcheck May 13 '13 at 08:28
  • Ah, excellent, one final question I promise! Because I can't use member variables in spring beans, say I have some really complex business logic that needs class level variables, what would I do in that situation? Can I just write a java class but not mark it with any spring annotation other than autowiring some class variables? – David May 13 '13 at 08:34
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/29825/discussion-between-soulcheck-and-david99world) – soulcheck May 13 '13 at 08:35
1

New threads are created for each request.

Your service has an instance variable (players) which is not threadsafe - it is shared by all threads. Any spring bean - including controllers and services are by default a singleton, you need to specify on the service annotation its scope.

@Service("playerService")
@Scope("session")
public class PlayerServiceImpl 

But its best(simpler, easier to scale) to keep beans singletons and not rely on instance variables (unless they are also managed by spring/threadsafe/singletons).

NimChimpsky
  • 46,453
  • 60
  • 198
  • 311
  • Ah ok - if it's best and safer to keep beans as singletons (I assume as I have done above, without the scope annotation), is there no risk that two users might login at the same time and the wrong user could receive incorrect data as the variables might've been editted from someone else's thread? – David May 13 '13 at 07:28
  • Also - say I have some complicated business logic, if I was sort of expecting to put this in my service layer but now knowing I can't use instance variables, should this be palmed off to the DAO level? Is the DAO level safe to have instance variables? If not, where's the best place to put my business logic while being thread safe (allowing class level variables)? – David May 13 '13 at 07:44