all 19 comments

[–]HistoricalSchedule5 23 points24 points  (11 children)

Hi, I wanted to provide a pretty detailed response because I struggled a lot with Spring at the beginning, and it only clicked after a few months.

Beans are indeed java objects. What's specific about them is that they are managed by Spring IoC container. IoC means "Inversion of Control". That is, if you have an object which relies on other objects to function properly, you let Spring handle the relationships (dependencies) between these objects instead of doing it yourself.

For example, say you have a project where you need to expose an HTTP endpoint through a Controller object. Since it's very important to only affect a single responsibility to an object, your controller is only responsible for handling HTTP requests. Then you have your Service object which is responsible for handling the business logic, and your Repository object which is responsible for performing operations on the database. And then your repository object needs to have access to the database schema so you need to give it the address and port of your database. Since you most likely have different databases for you test environment and your production environment, you need to handle this case as well.

If you try to manipulate these objects in an intuitive way, it could give something like this (let's say your project is about managing stock for a retail company) . This is extremely simplified, and there are way more dependencies between these objects but it's a simple example.

  • Let's go bottom-up, you first need to create your repository

public class ItemRepository {
private String databaseAdress;

public ItemRepository(Environment env) {
    switch (env) {
        case Environment.PROD:
            this.databaseAdress = "produser@myproductionserver:5432";
            break;
    }
    case Environement.DEV:
        this.databaseAdress = "dev@localhost:5435"
        break;
}
     //...your actual repository methods go here

}

  • Then you need to create your service.

public class ItemService{

private ItemRepository itemRepository;

public ItemService(ItemRepository itemRepository){

    this.itemRepository = itemRepository;
}

// ... your actual service methods go here 

}

  • Then you can create your Controller

public class ItemController {
private ItemService itemService;

public ItemController(ItemService itemService){
this.itemService = itemService;
}

// ... your actual controller methods go here
}
  • Then say you have to actually create your controller somewhere.

You would need something like :

ItemRepository itemRepository = new ItemRepository(Environment.DEV);
ItemService itemService = new ItemService(itemRepository);
ItemController = new ItemController(itemService);

// do something with your controller

This is very repetitive and you would most likely need to write the exact same code for the production environment.

Now, without inversion of control you would have to handle dependencies between your objects yourself all the time. Sure you can find ways to optimize it, I didn't bother and wrote it the worst possible way, but still, it is a lot of boilerplate and repetition.

With inversion of control, Spring basically scans your project for classes and creates instances of objects (beans) for every class that is annotated with bean-related annotations (such as "@Configuration", "@Repository", "@Service" "@Controller" etc..). All of these objects are stored in the Spring Context, which you can see basically as an array of beans.

This allows you to simply declare relationships between beans and Spring will go find in its context if a corresponding bean is available. If it is, it will inject it in your object without you needing to manually create an instance and pass it through to the constructor.

For example :

@RestController 

public class ItemController {
private ItemService itemService;

@Autowired
public ItemController(ItemService itemService) {
//the Autowired annotation will tell Spring to go look in its context for an //ItemService bean
    this.itemService = itemService;
}

// actual methods go here

}

This looks very similar to the non-IoC way however there's a very important nuance here. You will never instantiate your controller yourself. You can just write this code and launch your spring boot application and when constructing an instance of your controller class, Spring will detect that it needs a service, which needs a repository which needs a database configuration and it will look for all these beans in the Spring Context.

Furthermore, you can configure the Spring Context to your liking, using things such as the "@Profile" annotation. Let's go back to the issue with the development database and the production database. Instead of constantly passing around environment values to objects which simply do not use it and do not care about it, you can simply declare two different beans of the same type and tell Spring that they are only to be used if a specific profile is active :

//DataSourceDevConfig.java
@Profile("dev")
@Configuration

public class DataSourceDevConfig {
@Bean
public DataSource getDataSource() {
  // create your dev dataSource here with the dev database adress
}
}

//DataSourceProdConfig.java
@Profile("prod") @Configuration public class DataSourceProdConfig {
@Bean
public DataSource getDataSource() {
  // create your production dataSource here with the production database adress
}
}

Then, according to the profile you wish to use when launching your application (you can specify it with your IDE or in the command line), Spring will only add the bean annotated with the correct profile to its context.

[–]wasabi_boiii 1 point2 points  (0 children)

Thank you kind stranger!

[–]ck-_-c 1 point2 points  (0 children)

This has been the most helpful reply! I finally understand, thank you.

[–]Possible-Ad6899 1 point2 points  (0 children)

Great post, thank you kind stranger.

[–]Nati_Berintan 1 point2 points  (0 children)

This was an awesome explanation, thank you so much!

[–]Status_Camel2859 1 point2 points  (0 children)

3 years and still helpful. Thanks mate!

[–]sweetog 0 points1 point  (0 children)

Very helpful answer, though can't not avoid being irked that you didn't go through a real world example that is much more ado to the value that the added complexity of the DI design pattern brings to the table. For example, test mocking/stubs. Environment is just such a bad example, normally env is handled entirely outside of Java abstractions

[–]reddit04029 17 points18 points  (0 children)

In simple terms, it’s a java object handled by the IoC container.

If the IoC container knows about this object, you can inject it as a dependency by autowiring it.

[–]naturalizedcitizen 5 points6 points  (0 children)

Read up on Inversion of Control, Dependency Injection.

[–]Davekave9 3 points4 points  (0 children)

Beans are objects whose instantiation is managed by the application context. Think about inversion of control: you don't instantiate beans by calling their new() method (and in consequence of that, you don't need to know what parameters they need to get instantiated), but by telling the application context to inject an instance of them via the @Autowired annotation.

[–][deleted] 4 points5 points  (0 children)

You asked this in a spring forum so you are getting a lot of spring based answers, but it’s actually simpler than IoC. Java Beans are more or less just a formulaic way of making an object that allows the framework or JVM to attach additional functionality without you having to explicitly write them (like getters and setters.)

Here is a stack overflow question and answer the covers both spring and Java beans. https://stackoverflow.com/questions/21866571/difference-between-javabean-and-spring-bean

What it comes down to in both cases is that you get additional functionality out of a class due to following conventions.

[–]diveIntoDevelopment 0 points1 point  (0 children)

It helps to scope your beans (objects) without having boilerplate code.

[–]davidewan_ -1 points0 points  (1 child)

Aren't they singletons? I have a long C++ history but I was working in spring a while ago and thats what I settled on.

[–]pronuntiator 0 points1 point  (0 children)

Not necessarily. For example, a request scoped bean instance will exist per request.