Ryuzaki L
Ryuzaki L

Reputation: 40048

How Singleton bean can be Autowired in different places spring boot

I'm confused at this point, and i know all spring boot applications beans are singleton, according to my understanding if we have class annotated with @Service annotation that bean can be @Autowired in only one class (correct me if i'm wrong) here is the code that works fine, but i'm trying to understand how it works? how one bean can be @Autowired in two different classes?

How SampleService bean can be @Autowired in SampleController2 and SampleController3 at a time ?

And is this recommended approach? and in this case two threads can parallely change the data inside bean?

SampleController2

@RestController
@RequestMapping(value="samplemock")
public class SampleController2 {

@Autowired
private SampleService2 sampleservice2;

@RequestMapping(value="/mock1",method=RequestMethod.GET)
public void mockCall1() {
    sampleservice2.m1();
   }

}

SampleController3

@RestController
@RequestMapping(value="samplemock2")
public class SampleController3 {

@Autowired
private SampleService2 sampleservice2;

@RequestMapping(value="/mock1",method=RequestMethod.GET)
public void mockCall1() {
    sampleservice2.m1();

   }
 }

SampleService2

@Service
 public class SampleService2 {

public void m1() {
    System.out.println("bean is autowired");
    }
 }

Upvotes: 2

Views: 14218

Answers (3)

Makoto
Makoto

Reputation: 106430

The intention behind dependency injection and inversion of control is simple:

  • You define your injectables (like services) once, and they are instantiated once (unless you specify otherwise).
  • Those injectables are then used everywhere applicable, and you don't control their lifecycle, scope or state.

While I feel like the last point answers your primary question fairly tacitly, I'll elaborate - in a DI context, the only thing that really matters are enforceable contracts. That is to say, if your service subscribes to a specific type of contract, and you have a component which wishes to inject a service which fulfills that contract, then your DI layer should faithfully register a service which can fulfill that contract.

You get into fun and exciting stuff with bean priority, qualifiers and application profiles at that point, but this is the general idea.

For a concrete example: javax.sql.DataSource is an interface which is implemented by many JDBC-backed solutions, such as MySQL, Postgres, Oracle, and others. If you wish to have two different beans which talk to two different databases, but you want to be able to use those interchangeably, then you define a bean of type DataSource to use and configure which data source gets created. Again, this does involve things like @Qualifier to ensure you wire in the most specific bean at the most appropriate time.

Also, that last point is fairly important to answer this part of your question:

... and in this case two threads can parallely change the data inside bean?

It is very unwise to create an injectable bean with its own inherent state. That is, if you have SampleService attach itself to some sort of cached state with a collection inside of it, you're basically violating expectations since you don't know when or how often that collection is going to have elements added to it or removed from it.

The better convention is to have beans which can reference stateful services, but don't store that state in the bean itself (such as a database connection, but not entire database tables).

Upvotes: 0

Andreas
Andreas

Reputation: 159096

Here is a simplified view of what Spring does on startup:

// Create bean: sampleService2
SampleService2 sampleService2 = new SampleService2();

// Create bean: sampleController2
SampleController2 sampleController2 = new SampleController2();
sampleController2.sampleservice2 = sampleService2; // because @Autowired

// Create bean: sampleController3
SampleController3 sampleController3 = new SampleController3();
sampleController3.sampleservice2 = sampleService2; // because @Autowired

As you can see, the singleton bean sampleService2 is autowired into both sampleController2 and sampleController3.

The beans are added to a repository, so you can look them up by name or type at any later point in time.

Upvotes: 4

Gustavo Passini
Gustavo Passini

Reputation: 2668

By default, as you mentioned, all Spring beans are singletons, but your second assumption is wrong: the same bean can be autowired in many other beans.

In fact that's the whole point of them being singletons.

That also means two different threads could change the state of the same bean indeed. You would most of the time want to keep your beans stateless for that reason.

If you really ever need to have one different instance of a bean for each place where it is autowired, you can change the scope of that bean to prototype. See Spring bean scopes docs.

Upvotes: 4

Related Questions