what is the difference between #Bean annotation and #Component annotation at Spring? - spring

It might be a very simple question for you.But I read lots of documents and I am totally confused.We can use #Component instead of #Bean or #Bean instead of #Component(as well as #Repository #Service #Controller) ?
Cheers

Component
#Component also for #Service and #Repository are used to auto-detect and auto-configure beans using classpath scanning.
As long as these classes are in under our base package or Spring is aware of another package to scan, a new bean will be created for each of these classes
Bean and Component are mapped as one to one i.e one bean per Class.
These annotations (#Component, #Service, #Repository) are Class level annotations.
Example:
Lets Say we have a UserService Class which contains all methods for User Operation.
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
#Override
public User findByUsername( String username ) throws UsernameNotFoundException {
User u = userRepository.findByUsername( username );
return u;
}
public List<User> findAll() throws AccessDeniedException {
List<User> result = userRepository.findAll();
return result;
}
}
Spring will create a Bean for UserService and we can use this at multiple location/classes.
#Bean
#Bean is used to declare a single bean, rather than letting Spring do it automatically as in case of Component.
It decouples the declaration of the bean from the class definition, and lets you create and configure beans exactly how you choose.
#Bean are used at method level and can be configured as required
eg:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public SpringTemplateEngine springTemplateEngine()
{
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(htmlTemplateResolver());
return templateEngine;
}
#Bean
public SpringResourceTemplateResolver htmlTemplateResolver()
{
SpringResourceTemplateResolver emailTemplateResolver = new SpringResourceTemplateResolver();
emailTemplateResolver.setPrefix("classpath:/static/template/");
emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode("HTML");
emailTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
return emailTemplateResolver;
}
...
Read more about Stereotype Annotations here.

#Bean is used to define a method as a producer, which tells Spring to use that method to retrieve an object of the method return type and inject that object as a dependency whenever it's required.
#Component is used to define a class as a Spring component, which tells Spring to create an object (if it's Singleton) from and take care of it's lifecycle and dependencies and inject that object whenever it's required.
#Service and #Repository are basically just like #Component and AFAIK they are just for better grouping of your components.
#Service for Defining your service classes where you have your business logic, and #Repository for Defining your repository classes where you interact with an underlying system like database.

#Component
If we mark a class with #Component or one of the other Stereotype annotations these classes will be auto-detected using classpath scanning. As long as these classes are in under our base package or Spring is aware of another package to scan, a new bean will be created for each of these classes.
package com.beanvscomponent.controller;
import org.springframework.stereotype.Controller;
#Controller
public class HomeController {
public String home(){
return "Hello, World!";
}
}
There's an implicit one-to-one mapping between the annotated class and the bean (i.e. one bean per class). Control of wiring is quite limited with this approach since it's purely declarative. It is also important to note that the stereotype annotations are class level annotations.
#Bean
#Bean is used to explicitly declare a single bean, rather than letting Spring do it automatically like we did with #Controller. It decouples the declaration of the bean from the class definition and lets you create and configure beans exactly how you choose. With #Bean you aren't placing this annotation at the class level. If you tried to do that you would get an invalid type error. The #Bean documentation defines it as:
Indicates that a method produces a bean to be managed by the Spring container.
Typically, #Bean methods are declared within #Configuration classes.We have a user class that we needed to instantiate and then create a bean using that instance. This is where I said earlier that we have a little more control over how the bean is defined.
package com.beanvscomponent;
public class User {
private String first;
private String last;
public User(String first, String last) {
this.first = first;
this.last = last;
}
}
As i mentioned earlier #Bean methods should be declared within #Configuration classes.
package com.beanvscomponent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ApplicationConfig {
#Bean
public User superUser() {
return new User("Partho","Bappy");
}
}
The name of the method is actually going to be the name of our bean. If we pull up the /beans endpoint in the actuator we can see the bean defined.
{
"beans": "superUser",
"aliases": [],
"scope": "singleton",
"type": "com.beanvscomponent.User",
"resource": "class path resource
[com/beanvscomponent/ApplicationConfig.class]",
"dependencies": []
}
#Component vs #Bean
I hope that cleared up some things on when to use #Component and when to use #Bean. It can be a little confusing but as you start to write more applications it will become pretty natural.

Related

Does spring inject any beans provided in the #Configuration class

If I have a #Configuration class where I have a bean like below, will the dataMap be resolved in the constructor of the DataService class. What type of dependency injection is this? Is it by type because the name for sure doesn't match?
#Bean
public Map<String, List<Data>> data() {
final Map<String, List<Data>> dataMap = new HashMap<>();
readings.put("1", new Data());
return dataMap;
}
and a class
#Service
public class DataService {
private final Map<String, List<Data>> information;
public DataService(Map<String, List<Data>> information) {
this.information = information;
}
}
#Configuration annotation serves as a placeholder to mention that whichever classes annotated with #Configuration are holding the bean definitions!
When Spring application comes up, spring framework will read these definitions and create beans (or simply objects) in IOC (Inversion of control) container These would be Spring managed objects/beans !
To answer your question, it should create a bean and this is a setter based injection!
However, your #Bean must be some user defined or business entity class in ideal scenarios!
Few links for you to refer to:
https://www.codingame.com/playgrounds/2096/playing-around-with-spring-bean-configuration
https://www.linkedin.com/pulse/different-types-dependency-injection-spring-kashif-masood/

Spring conditional component scan configuration

I have a configuration class which registers beans based on a very simple condition (checking a property value in application.properties). The configuration class and the condition are the following:
#Configuration
#Conditional(DatabaseConfigurationCondition.class)
#ComponentScan(basePackageClasses = DBConfigComponents.class)
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
}
and
public class DatabaseConfigurationCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("configuration.type").contains("db");
}
}
In addition of the beans registered in this configuration class I have component scan which scans for other components. When the condition is not met, I expect the beans which are defined in the configuration class not to be registered (which happens to be a case), but also I expect other classes which are annotated with #Component (or #Repository, #Service, etc.. ) and are in same folder as DBConfigComponents.class marker interface not to be registered, which does not happen. Beans which are scanned are always registered, no matter if the condition is fulfilled or not.
When I put the #Conditional(DatabaseConfigurationCondition.class) on each #Component annotated class, than it's working correctly, but I don't want to put it on each class separately.
Any suggestion?
Fortunately, I managed to fix this. The problem in my case was that I had another #ComponentScan annotation placed in other configuration class in other Maven module - not conditional on any property. The components which are in same package as DBConfigComponents marker interface were actually scanned by the other configuration class.
The way #ComponentScan works is on package level. Although, in different Maven modules, both configuration classes were in same package. #ComponentScan works perfectly fine with #Conditional. No need #Conditional to be placed on each component separately.
The best way to achieve this is not to annotate these beans using #Component / #Service and #Repository annotations. Instead you should return these as part of the configuration you have setup which would be DatabaseConfigurationLoader. See sample below.
#Configuration
#Conditional(DatabaseConfigurationCondition.class)
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
#Bean
public SomeService someService() {
return new SomeService();
}
#Bean
public SomeComponent someComponent() {
return new SomeComponent();
}
}
Note: Typically #Configuration with #Conditional are used in libraries that you want to include in your spring boot application. Such libraries should not share the same package as your spring boot application. Thus they should not be picked up by #ComponentScan annotation. Beans from libraries should not be annotated with #Component / #Service / #Repository annotations. Spring suggests using AutoConfiguration for that. See https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html & https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html
No need to implement Condition interface, you need to use '#ConditionalOnProperty' annotation:
#Configuration
#ComponentScan(basePackageClasses = DBConfigComponents.class)
#ConditionalOnProperty(name = "configuration.type", havingValue = "db")
public class DatabaseConfigurationLoader {
#Bean
public DatabaseConfigurationRepository databaseConfigurationRepository() {
return new DatabaseConfigurationRepository();
}
}
you can use 'prefix' instead of 'havingValue' depending on your needs.

Make a third party class a service

I'm a beginner at Spring and beginning to understand how beans work. I want to declare a 3rd party class as a Service or a Bean. How do I do this? Should I just extend the class and annotate that?
example:
#Service
public class MyService { public MyService(ThirdPartyClass thirdPartyClass){..}....}
Here I cannot annotate ThirdPartyClass as a Service or otherwise
If your aren't the owner of the class that you would like to use as a bean, you can create the bean declaration in one of application's configuration classes:
#Configuration
public class YourConfig {
#Bean
public ThirdPartyClass thirdPartyClass() {
return new ThirdPartyClass();
}
}
Spring will instantiate an appropriate object based on that description and expose it via container to other beans.
You can add the class which is out of your control as #Bean
#Configuration
public class ApplicationConfig {
#Bean
public ClassName methodName() {
return new ClassName();
}
}
At the time of initializing the application, spring will call this method and register ClassName object to spring context and will be made available where you #Autowired this bean.

Spring Boot Autowired failed - null

I have 3 classes which are found in different packages in a spring boot application as follows:
Why does #Autowired work in certain classes only?Anything I am doing wrong?
#Configuration
public class Configurations{
#Autowired
Prop prop; //works fine
#Bean
//other bean definitions
}
#Component
public class Prop{
public void method(){};
}
public class User{
#Autowired
Prop prop; //does not work, null
public void doWork(){
prop.method();
}
}
I have also tried the #PostConstruct, but same result
public class User{
#Autowired
Prop prop; //does not work, null
#PostConstruct
public void doWork(){
prop.method();
}
}
The #Autowired annotation works only if Spring detects that the class itself should be a Spring bean.
In your first example you annotated Configurations with the #Configuration annotation. Your User class on the other hand does not have an annotation indicating that it should be a Spring bean.
There are various annotations (with different meanings) to make your class being picked up by the Spring container, some examples are #Service, #Component, #Controller, #Configuration, ... . However, this only works if your class is in a package that is being scanned by the Spring container. With Spring boot, the easiest way to guarantee that is by putting your User class in a (sub)package of your main class (the class annotated with #SpringBootApplication).
You can also manually create your bean by writing the following method in your Configurations:
#Bean
public User user() {
return new User();
}
In this case you don't have to annotate your User class, nor do you have to make sure that it is in a package that is being scanned.

Spring #Component & #Bean annotation

I believe #Configuration annotation when used in conjunction with #Bean annotation in spring is used to replace xml configuration. However I saw a piece of code where #Bean was used in conjunction with #Component (defined at class level). Is this a valid declaration? Are there any any pros / cons in using #Component with #Bean annotation vs using #Configuration and #Bean.
EDIT:
Thanks #Sundar & #Biju. I did programmatic call between 2 bean methods under Component class. I saw different object values. However when I used Configuration , I saw the same bean values. Based on what you had explained , I assume a regular method call was made when I used #Component , whereas when I used #Configuration , I assume method annotated with #Bean was treated as a Spring Bean
Code
#Component
public class AppConfig {
#Bean(name="customerService")
public CustomerService getCustomerService(){
System.out.println(getService());
System.out.println(getService());
return getService();
}
#Bean
public CustomerService getService(){
return new CustomerServiceImpl();
}
}
Console Output
com.company.service.CustomerServiceImpl#68bbe345
com.company.service.CustomerServiceImpl#30b8a058
Code
#Configuration
public class AppConfig {
#Bean(name="customerService")
public CustomerService getCustomerService(){
System.out.println(getService());
System.out.println(getService());
return getService();
}
#Bean
public CustomerService getService(){
return new CustomerServiceImpl();
}
}
Console Output
com.company.service.CustomerServiceImpl#71623278
com.company.service.CustomerServiceImpl#71623278
It is a valid declaration, however there are catches - the one within a #Component is referred to as a lite-mode and dependencies cannot easily be injected for beans declared in this form. The recommendation is always to use #Bean in a #Configuration annotated class - here is a good reference on this - http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-java-basic-concepts
You can use #Component as an alternative for #Configuration. It’s official suggestion from spring team.
Simply declare your #Bean methods on classes not annotated with #Configuration (but typically with another Spring stereotype instead, e.g. #Component). As long as you don’t do programmatic calls between your #Bean methods, this is going to work just as fine, but conditions apply*.
Please refer more info in this link.
http://dimafeng.com/2015/08/29/spring-configuration_vs_component/

Resources