The well known Java framework Spring and its modern personal assistant Spring Boot are often touted as being 'magic'. In this blog I am going to try to pull away the curtain and show you what's behind it. We will start by explaining the basic Spring framework and later on Spring Boot will join the party, after which the terms Spring and Spring Boot will be used interchangeably meaning the Spring framework with Spring Boot sprinkled on top.
The Heart of Spring
At its core, Spring is a framework which implements the "Inversion of Control" design pattern. This means that the creation of most objects which are needed in your application are not created directly from within your code (by calling something like Service service = new ServiceImpl()
) but rather this is done by Spring. Because you don't have to explicitly create these objects yourself anymore, you don't even have to know their exact types. You can use interfaces instead of concrete classes and let Spring figure out which implementation to use. This is great because you can easily swap out real implementations with mock versions for testing or change external dependencies like database drivers.
Dependency Injection
Spring implements the Inversion of Control pattern with "Dependency Injection". We already saw that we don't have to explicitly create objects anymore, so how does Spring know what objects to manage for us? When your application starts, Spring creates an omniscient omnipresent omnipotent object called the Application Context
, the mother of all objects. This special object takes care of creating, managing and destroying all of the other needed objects. In Spring world, these objects are called Beans
. The real power of beans is that they can be used to create other beans, creating a complete hierarchy of beans.
Giving Spring the Boot
How does Spring know which classes to turn into beans? While you could still use the ancient ways of XML configuration, it is much easier to use Spring Boot, a special layer on top of Spring. Spring Boot makes heavy use of annotation processing and this is where most developers start to see and feel (and fear) the magic. If you are developing a Spring Boot application, just adding @SpringBootApplication
to your application class turns it into a Spring managed bean, how so? Take a look at this simple application class:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
When this application is launched through its main method, it calls into Spring to execute this application with the given arguments. In turn, Spring scans this application for annotations and finds the @SpringBootApplication
annotation. This annotation is actually what Spring calls a meta-annotation
and all this means is that it is made up of other annotations, in this case @EnableAutoConfiguration
, @ComponentScan
and @Configuration
, we will take a look at what these mean.
Auto all the configurations
The first embedded annotation, @EnableAutoConfiguration
, tells Spring to scan and load explicitly declared @Configuration
classes from your application's dependencies. Some modules contain a special file called spring.factories
to tell applications and other libraries to automatically load the listed @Configuration
classes if @EnableAutoConfiguration
is present. The classes annotated with @Configuration
contain methods annotated with @Bean
which are used by Spring to obtain a specific instance of a class whenever someone needs it. If you have a class with a constructor such as public MyService(DataSource dataSource)
, there is a good chance that data source object is created from a @Bean
annotated method inside a @Configuration
class inside one of your dependencies.
Scan all the components
The next embedded annotation,@ComponentScan
, tells Spring to scan the package and sub packages of your application for beans. External dependencies are not the only source of beans, your application may contain many beans itself as well! With this special annotation @Configuration
classes inside your application are also scanned for any @Bean
methods they might contain as well as classes annotated with stereotype annotations.
Instant bean recognition
Spring Boot offers a set of special annotations, called stereotypes, that you can put on top of a class to turn it into a bean if @ComponentScan
is used, these are
@Component
used for utility classes which have no other specific role@Service
used for service classes, mostly containing business logic@Repository
used for database access@Controller
used for MVC controllers, there is another variant called@RestController
which is used for REST endpoints
If any of the classes in your application is annotated with one of these, it will be turned into a bean by Spring and the beauty of this is that you can use all of these beans in other beans! For instance, you could have a constructor for a service class (annotated with @Service
) public MyService(MyRepository myRepository)
and you will get a fully instantiated repository object ready to use inside of that class.
Just configure it
The last embedded annotation, @Configuration
has already made an appearance, but it simply means: 'here may be beans'. That's right, your application class itself can have some @Bean
methods inside of it which are then also used by Spring to create those objects.
It's lonely to be a Singleton
So what exactly does it mean to be a bean? It means that Spring's application context is in control of your creation and destruction. Most beans come into existence as singletons, meaning that during the lifetime of your application there is one and only one instance of a certain class and it is shared by everybody. As they are shared it is a very bad idea to let them have any state, singletons should be stateless to prevent concurrency issues and are generally used as processors, taking in some data, processing it and handing it over to the next in line, such as a repository which saves it to the database. Beans don't have to be singletons, sometimes you need a fresh copy each time and luckily Spring also provides ways to let you specify this. Generally speaking though, Spring beans are almost always singletons.
Proxy for you
Annotations are not only used to tell Spring where to find beans and how to create them, they are also used internally by Spring to enrich your classes and methods with some added functionality such as caching or transactions. Adding these cross cutting concerns to certain entry points in your application is done with a design pattern called "Aspect Oriented Programming" or AOP for short. If you annotate a method with @Transactional
Spring generates a proxy class where that method is overridden and surrounded with code managing the transaction. So while your class and method may look like
public class MyService {
@Transactional
public void process() {
// code running inside of transaction
}
}
What actually happens behind the scenes when someone calls the process()
method is that they are calling the proxy instead. Note that because of how beans are wired and managed by Spring, it is possible for Spring to replace any instances where a type of MyService
is required with MyServiceProxy
instead, because MyServiceProxy
extends MyService
, so the proxy may look a bit like this:
public class MyServiceProxy extends MyService {
@Override
public void process() {
try {
// simplified for this example
transaction.start();
super.process();
transaction.commit();
} catch (Exception e) {
transactionManager.rollback();
}
}
}
This also explains why calling these annotated methods from within your own MyService
class does not work, because if you are already inside of MyService
and not the proxy class, you will call the regular method, not the overridden proxy method. The name of the proxy class is not actually nice and clean as in this example, but rather something cryptic like MyService$$EnhancerBySpringCGLIB$$11b3e0aa
because it is generated at runtime. Virtual classes like this can also add to that feeling of magic because there is no source file generated, it just exists inside the JVM and when the application ends, it's gone! If you've worked with Spring for some time, you are probably familiar with long stacktraces with names of classes and methods which you didn't write yourself and exist either inside the Spring framework or inside one of these generated classes.
Down the rabbit hole
As you can see, there is nothing 'magic' about Spring and Spring Boot even though at times it may definitely seem like it. It's all just beans and annotations all the way down. The Spring framework provides the application context which manages the beans and Spring Boot offers annotations to turn regular classes into beans and proxies. I hope you have a slightly better understanding of Spring and if you are looking for more information, you can always consult the excellent documentation.