What is Spring Framework? An Unorthodox Guide (2024)

As already mentioned at the very beginning, Spring Framework, at its core, is a dependency injection container that manages the classes you wrote and their dependencies for you (see the previous section). Let’s find out how it does that.

What is an ApplicationContext?

That someone, who has control over all your classes and can manage them appropriately (read: create them with the necessary dependencies), is called ApplicationContext in the Spring universe.

What we want to achieve is the following code (I described the UserDao and DataSource in the previous section, go skim it if you came right here and skipped it):

import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import javax.sql.DataSource;public class MyApplication { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); // (1) UserDao userDao = ctx.getBean(UserDao.class); // (2) User user1 = userDao.findById(1); User user2 = userDao.findById(2); DataSource dataSource = ctx.getBean(DataSource.class); // (3) // etc ... }}
  1. Here we are constructing our Spring ApplicationContext. We’ll go into much more detail on how this works in the next paragraphs.

  2. The ApplicationContext can give us a fully configured UserDao, i.e. one with its DataSource dependency set.

  3. The ApplicationContext can also give us the DataSource directly, which is the same DataSource that it sets inside the UserDao.

This is pretty cool, isn’t it? You as the caller don’t have to worry about constructing classes anymore, you can simply ask the ApplicationContext to give you working ones!

But how does that work?

How to create an ApplicationContext?

In the code above, we put a variable called "someConfigClass" in the AnnotationConfigApplicationContext constructor. Here’s a quick reminder:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class MyApplication { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); // (1) // ... }}

What you really want to pass into the ApplicationContext constructor, is a reference to a configuration class, which should look like this:

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class MyApplicationContextConfiguration { // (1) @Bean public DataSource dataSource() { // (2) MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); return dataSource; } @Bean public UserDao userDao() { // (3) return new UserDao(dataSource()); }}
  1. You have a dedicated ApplicationContext configuration class, annotated with the @Configuration annotation, that looks a bit like the Application.java class from How to 'manage' dependencies in a global Application class.

  2. You have a method that returns a DataSource and is annotated with the Spring-specific @Bean annotation.

  3. You have another method, which returns a UserDao and constructs said UserDao by calling the dataSource bean method.

This configuration class is already enough to run your very first Spring application.

import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class MyApplication { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(MyApplicationContextConfiguration.class); UserDao userDao = ctx.getBean(UserDao.class); // User user1 = userDao.findById(1); // User user2 = userDao.findById(1); DataSource dataSource = ctx.getBean(DataSource.class); }}

Now, let’s find out what exactly Spring and the AnnotationConfigApplicationContext do with that Configuration class you wrote.

Are there alternatives to AnnotationConfigApplicationContext?

There are many ways to construct a Spring ApplicationContext, for example through XML files, annotated Java configuration classes or even programmatically. To the outside world, this is represented through the single ApplicationContext interface.

Look at the MyApplicationContextConfiguration class from above. It is a Java class that contains Spring-specific annotations. That is why you would need to create an Annotation ConfigApplicationContext.

If, instead, you wanted to create your ApplicationContext from XML files, you would create a ClassPathXmlApplicationContext.

There are also many others, but in a modern Spring application, you will usually start out with an annotation-based application context.

What does the @Bean annotation do? What is a Spring Bean?

You’ll have to think of the methods inside your ApplicationContext configuration class as factory methods. For now, there is one method that knows how to construct UserDao instances and one method that constructs DataSource instances.

These instances that those factory methods create are called beans. It is a fancy word for saying: I (the Spring container) created them and they are under my control.

But this leads to the question: How many instances of a specific bean should Spring create?

What are Spring bean scopes?

How many instances of our DAOs should Spring create? To answer that question, you need to learn about bean scopes.

  • Should Spring create a singleton: All your DAOs share the same DataSource?

  • Should Spring create a prototype: All your DAOs get their own DataSource?

  • Or should your beans have even more complex scopes, like saying: A new DataSource per HttpRequest? Or per HttpSession? Or per WebSocket?

You can read up on a full list of available bean scopes here, but for now it is suffice to know that you can influence the scope with yet another annotation.

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Scope;import org.springframework.context.annotation.Configuration;@Configurationpublic class MyApplicationContextConfiguration { @Bean @Scope("singleton") // @Scope("prototype") etc. public DataSource dataSource() { MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); return dataSource; }}

The scope annotation controls how many instances Spring will create. And as mentioned above, that’s rather simple:

  • Scope("singleton") → Your bean will be a singleton, there will only be one instance.

  • Scope("prototype") → Every time someone needs a reference to your bean, Spring will create a new one. (There’s a couple of caveats here, like injecting prototypes in singletons, though).

  • Scope("session") → There will be one bean created for every user HTTP session.

  • etc.

The gist: Most Spring applications almost entirely consist of singleton beans, with the occasional other bean scope (prototype, request, session, websocket etc.) sprinkled in.

Now that you know about ApplicationContexts, Beans & Scopes, let’s have another look at dependencies, or the many ways our UserDAO could obtain a DataSource.

What is Spring’s Java Config?

So far, you explicitly configured your beans in your ApplicationContext configuration, with the help of @Bean annotated Java methods.

This is what you would call Spring’s Java Config, as opposed to specifying everything in XML, which was historically the way to go with Spring. Just a quick recap of what this looks like:

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class MyApplicationContextConfiguration { @Bean public DataSource dataSource() { MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); return dataSource; } @Bean public UserDao userDao() { // (1) return new UserDao(dataSource()); }}
  1. One question: Why do you have to explicitly call new UserDao() with a manual call to dataSource()? Cannot Spring figure all of this out itself?

This is where another annotation called @ComponentScan comes in.

What does @ComponentScan do?

The first change you’ll need to apply to your context configuration is to annotate it with the additional @ComponentScan annotation.

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration@ComponentScan // (1)public class MyApplicationContextConfiguration { @Bean public DataSource dataSource() { MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); return dataSource; } // (2) // no more UserDao @Bean method!}
  1. We added the @ComponentScan annotation.

  2. Note, that the UserDAO definition is now missing from the context configuration!

What this @ComponentScan annotation does, is tell Spring: Have a look at all Java classes in the same package as the context configuration if they look like a Spring Bean!

This means if your MyApplicationContextConfiguration lives in package com.marcobehler, Spring will scan every package, including subpackages, that starts with com.marcobehler for potential Spring beans.

How does Spring know if something is a Spring bean? Easy: Your classes need to be annotated with a marker annotation, called @Component.

What do @Component & @Autowired do?

Let’s add the @Component annotation to your UserDAO.

import javax.sql.DataSource;import org.springframework.stereotype.Component;@Componentpublic class UserDao { private DataSource dataSource; public UserDao(DataSource dataSource) { // (1) this.dataSource = dataSource; }}
  1. This tells Spring, similarly to that @Bean method you wrote before: Hey, if you find me annotated with @Component through your @ComponentScan, then I want to be a Spring bean, managed by you, the dependency injection container!

(When you look at the source code of annotations like @Controller, @Service or @Repository later on, you’ll find that they all consist of multiple, further annotations, always including @Component!).

There’s only one little piece of information missing. How does Spring know that it should take the DataSource that you specified as a @Bean method and then create new UserDAOs with that specific DataSource?

Easy, with another marker annotation: @Autowired. Hence, your final code will look like this.

import javax.sql.DataSource;import org.springframework.stereotype.Component;import org.springframework.beans.factory.annotation.Autowired;@Componentpublic class UserDao { private DataSource dataSource; public UserDao(@Autowired DataSource dataSource) { this.dataSource = dataSource; }}

Now, Spring has all the information it needs to create UserDAO beans:

  • UserDAO is annotated with @Component → Spring will create it

  • UserDAO has an @Autowired constructor argument → Spring will automatically inject the DataSource that is configured via your @Bean method

  • Should there be no DataSources configured in any of your Spring configurations, you will receive a NoSuchBeanDefinition exception at runtime.

Constructor Injection & Autowired Revisited

I have been lying to you a tiny bit in the previous section. In earlier Spring versions (pre 4.2, see history), you needed to specify @Autowired in order for constructor injection to work.

With newer Spring versions, Spring is actually smart enough to inject these dependencies without an explicit @Autowired annotation in the constructor. So this would also work.

@Componentpublic class UserDao { private DataSource dataSource; public UserDao(DataSource dataSource) { this.dataSource = dataSource; }}

Why did I mention @Autowired then? Because it does not hurt, i.e. makes things more explicit and because you can use @Autowired in many other different places, apart from constructors.

Let’s have a look at different ways of dependency injection - constructor injection just being one many.

What are Field and Setter Injection?

Simply put, Spring does not have to go through a constructor to inject dependencies.

It can also directly inject fields.

import javax.sql.DataSource;import org.springframework.stereotype.Component;import org.springframework.beans.factory.annotation.Autowired;@Componentpublic class UserDao { @Autowired private DataSource dataSource;}

Alternatively, Spring can also inject setters.

import javax.sql.DataSource;import org.springframework.stereotype.Component;import org.springframework.beans.factory.annotation.Autowired;@Componentpublic class UserDao { private DataSource dataSource; @Autowired public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; }}

These two injection styles (fields, setters) have the same outcome as constructor injection: You’ll get a working Spring Bean. (In fact, there’s also another one, called method injection which we won’t cover here.)

But obviously, they differ from one another, which means there has been a great many debates about which injection style is best and which one you should use in your project.

Constructor Injection vs. Field Injection

There have been a great many debates online, whether constructor injection or field injection is better, with a number of strong voices even claiming that field injection is harmful.

To not add further noise to these arguments, the gist of this article is:

  1. I have worked with both styles, constructor injection and field injection in various projects over the recent years. Based solely on personal experience, I do not truly favor one style over the other.

  2. Consistency is king: Do not use constructor injection for 80% of your beans, field injection for 10% and method injection for the remaining 10%.

  3. Spring’s approach from the official documentation seems sensible: Use constructor injection for mandatory dependencies and setter/field injection for optional dependencies. Be warned: Be really consistent with that.

Most importantly, keep in mind: The overall success of your software project will not depend on the choice of your favorite dependency injection method (pun intended).

The Confident Spring Professional

If you want to easily get a deep and practical understanding of the entire Spring Ecosystem or simply refresh your Spring knowledge: I have written a course for you.

Interested in trying out the full first module?

Learn More

Summary: Spring’s IoC container

By now, you should know pretty much everything you need to know about Spring’s dependency container.

There is of course more to it, but if you have a good grasp of ApplicationContexts, Beans, dependencies and different methods of dependency injection, then you are already on a good path.

Let’s see what else Spring has to offer, apart from pure dependency injection.

What is Spring Framework? An Unorthodox Guide (2024)
Top Articles
Latest Posts
Article information

Author: Twana Towne Ret

Last Updated:

Views: 5713

Rating: 4.3 / 5 (44 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Twana Towne Ret

Birthday: 1994-03-19

Address: Apt. 990 97439 Corwin Motorway, Port Eliseoburgh, NM 99144-2618

Phone: +5958753152963

Job: National Specialist

Hobby: Kayaking, Photography, Skydiving, Embroidery, Leather crafting, Orienteering, Cooking

Introduction: My name is Twana Towne Ret, I am a famous, talented, joyous, perfect, powerful, inquisitive, lovely person who loves writing and wants to share my knowledge and understanding with you.