Sunday, 9 February 2014

Spring series, part 5: @Component vs @Bean


In this post, I will look into the distinction between a component and a bean. Even though both of them refer to Spring managed beans, each serves a different purpose. @Component and its specializations (@Controller, @Service and @Repository) allow for auto-detection using classpath scanning. @Bean on the other hand can only be used to explicitly declare a single bean in a configuration class.


You might find interesting that components have been around for quite a long time, since Spring 2.5. Here is a brief overview of the component types and their purpose. As you will see in a short while, all component types are treated in the same way. The subtypes are mere markers, think code readability rather than features.

Component types and their purpose
Annotation Purpose
@ComponentA candidate for auto-detection via classpath scanning.
@ControllerA web controller, popularized by Spring MVC.
@RepositoryData manager / storage, ties to enterprise apps (DAO, DDD)
@ServiceMeant to provide business logic - a (stateless) facade.

I said there is no difference among the individual component types. Let's prove it using a simple web app which exposes a single url returning a message in a JSON format. The front-end is, naturally, handled by a controller:
import org.springframework.stereotype.Controller;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RequestMethod;  
import org.springframework.web.bind.annotation.ResponseBody;  
import org.zezutom.springseries0114.part04.model.Message;  
 
@Controller  
@RequestMapping("/web/message")  
public class WebController {  
   @RequestMapping(method = RequestMethod.GET)  
   public @ResponseBody  
   Message message() {  
     return new Message("Controller", "An old good controller.");  
   }  
}  
However, any @Component would do, as shown on the following "controllers". As long as it can be found on the classpath, it is good to use:
import org.springframework.stereotype.Component;  
..  
@Component // Not a controller?! Makes no difference  
@RequestMapping("/component/message")  
public class ComponentController {  
..  
}  
 import org.springframework.stereotype.Service;   
 ..   
 @Service // Not a controller?! Makes no difference   
 @RequestMapping("/service/message")   
 public class ServiceController {   
  ..   
 }   
Now, a bean right? That's a slightly different story. A @Bean-annotated class would not be found and could not be therefore used as a controller in the example above. Though, auto-wiring definitely applies here and provided the bean is defined in either an xml or a programmatic configuration it can be easily dependency-injected. Let's promote code reusability and enhance our controllers with a dedicated message builder:
 // Just a POJO  
 public class MessageBuilder {  
   public Message getInstance(String title, String text) {  
     return new Message(title, text);  
   }  
 }  
 // Let's turn the POJO into a bean  
 import org.springframework.context.annotation.Configuration;  
 import org.springframework.context.annotation.Bean;  
 ..  
 @Configuration  
 public class AppConfig {  
   @Bean  
   public MessageBuilder messageBuilder() {  
     return new MessageBuilder();  
   }  
 }  
 // Finally, hook it up  
 ..  
 @Autowired  
 private MessageBuilder messageBuilder;  
 ..  
The example is obviously fully covered by tests, feel free to download the source code and try it out.

Source Code

Previous: Part 4 - @Lazy on injection points 
Next: Part 6 - Spring 4 and generics-based injection matching

1 comment:

  1. A very interesting article Tomas. Something that is worth mentioning is that the Repository annotation confers special behavior to all beans it marks. The PersistenceExceptionTranslationPostProcessor automatically applies persistence exception translation to any bean marked with @Repository. I have post more about this here: https://readlearncode.com/2016/02/13/insights-from-stackoverflow-most-voted-for-spring-4-questions/#1

    ReplyDelete