1. Introduction
In this article, We'll be learning how to create a singleton bean in Spring Boot and Spring applications. Once a bean is declared as singleton then spring container will create exactly only one instance for that bean. But, We can make bean scope as singleton as a result container will create only once instance as similar to the Singleton Desing Pattern.
By Default, A bean in spring is treated as singleton scoped. But, This can be changed to prototype as well with <bean> tag attribute and @Scope annotation.
2. Singleton With @Scope Annotation
First of all, we will see now how to create and next, how to make bean as a singleton with @Scope annotation. @Scope Annotation has attribute value that takes the value as ConfigurableBeanFactory.SCOPE_SINGLETON or ConfigurableBeanFactory.SCOPE_PROTOTYPE
Another this is we already discussed in the previous article, Bean creation with @Bean annotation.
2.1 Creating Classes
Now, it is time to create a class for defining bean examples.
According to the Spring guidelines creating first a model class as below. Before going to the next step, first, understand that this service class has two instance variables amount and payamentStatus.
[package com.javaprogramto.bean.scopes.beanscopes;
public class PaymentService {
private long amount;
private String paymentStatus;
public long getAmount() {
return amount;
}
public void setAmount(long amount) {
this.amount = amount;
}
public String getPaymentStatus() {
return paymentStatus;
}
public void setPaymentStatus(String paymentStatus) {
this.paymentStatus = paymentStatus;
}
}]
Another class to create a bean for the model service with @Bean and @Scope annotations. Basically, @Component makes this class is visible to the Spring Boot for scanning and register PaymentService as a singleton bean with the context.
[package com.javaprogramto.bean.scopes.beanscopes;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
public class PaymentConfiguration {
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public PaymentService getPaymentService() {
return new PaymentService();
}
}]
2.2 Creating CommandLineRunner for Testing
All the above code is just enough to test the code in spring application but in the spring boot, we should use the CommandLineRunner interface. CommandLineRunner is the most useful way to test our logic.
As soon as you create the class, ready for testing.
[import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class PaymentRunner implements CommandLineRunner {
Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private PaymentService service1;
@Autowired
private PaymentService service2;
@Override
public void run(String... args) throws Exception {
logger.info("service1 hashcode " + service1.hashCode());
service1.setAmount(100l);
service1.setPaymentStatus("success");
logger.info("service2 hashcode " + service2.hashCode());
logger.info("service2 amount " + service2.getAmount());
logger.info("service2 status " + service2.getPaymentStatus());
}
}]
Additionally, you can add any further logic as you need for testing. But, Here we are autowiring PaymentService twice with name service1 and service2 by using @Autowired. Whenever you are working with CommandLineRunner, a method run(String... args) should be overridden and it takes var-args arguments.
2.3 Running Main Spring Boot Application
All the changes are ready, you can run the main application of spring boot.
[package com.javaprogramto.bean.scopes.beanscopes;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BeanScopesApplication {
public static void main(String[] args) {
SpringApplication.run(BeanScopesApplication.class, args);
}
}]
2.4 Output
Finally, Completed the writing the code. Without any delay, Start the main application and see the output.
Important to realize that we did not add any RestController in our examples. As a result, the Spring Boot starter should not start the embed tomcat server.
[ . ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.6.RELEASE)
2020-04-22 21:41:47.239 INFO 5780 --- [ main] c.j.b.s.b.BeanScopesApplication : Starting BeanScopesApplication on -Pro-2.local with PID 5780 (/Users/venkateshn/Documents/VenkY/blog/workspace/bean-scopes/target/classes started by venkateshn in /Users/venkateshn/Documents/VenkY/blog/workspace/bean-scopes)
2020-04-22 21:41:47.242 INFO 5780 --- [ main] c.j.b.s.b.BeanScopesApplication : No active profile set, falling back to default profiles: default
2020-04-22 21:41:47.770 INFO 5780 --- [ main] c.j.b.s.b.BeanScopesApplication : Started BeanScopesApplication in 0.97 seconds (JVM running for 1.548)
2020-04-22 21:41:47.771 INFO 5780 --- [ main] c.j.b.scopes.beanscopes.PaymentRunner : service1 hashcode 1360518503
2020-04-22 21:41:47.771 INFO 5780 --- [ main] c.j.b.scopes.beanscopes.PaymentRunner : service2 hashcode 1360518503
2020-04-22 21:41:47.771 INFO 5780 --- [ main] c.j.b.scopes.beanscopes.PaymentRunner : service2 amount 100
2020-04-22 21:41:47.771 INFO 5780 --- [ main] c.j.b.scopes.beanscopes.PaymentRunner : service2 status success]
As I have said, spring boot did not start tomcat server and it executed run() method of PaymentRunner. Obviously, it has printed hashcodes of two objects and those are the same. And also, added values to the service1 instance and those are reflected in service2 object because PaymentService bean created as a singleton. Hence, all changes made to the service1 are available to the service2 and vice versa.
3. Conclusion
To summarize, We've seen how to create bean as a singleton scope. Furthermore injected those beans in CommandLineRunner implementation and seen how the values are changed to all instances of singleton bean.
As usual example code is over GitHub and the project can be downloaded.
[lock]
[View on GitHub ##eye##].
[Download ##file-download##]
[/lock]
- [accordion]
- PaymentService.java
package com.javaprogramto.bean.scopes.beanscopes; public class PaymentService { private long amount; private String paymentStatus; public long getAmount() { return amount; } public void setAmount(long amount) { this.amount = amount; } public String getPaymentStatus() { return paymentStatus; } public void setPaymentStatus(String paymentStatus) { this.paymentStatus = paymentStatus; } }
- PaymentConfiguration.java
package com.javaprogramto.bean.scopes.beanscopes; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component public class PaymentConfiguration { @Bean @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) public PaymentService getPaymentService() { return new PaymentService(); } }
- PaymentRunner.java
package com.javaprogramto.bean.scopes.beanscopes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class PaymentRunner implements CommandLineRunner { Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private PaymentService service1; @Autowired private PaymentService service2; @Override public void run(String... args) throws Exception { logger.info("service1 hashcode " + service1.hashCode()); service1.setAmount(100l); service1.setPaymentStatus("success"); logger.info("service2 hashcode " + service2.hashCode()); logger.info("service2 amount " + service2.getAmount()); logger.info("service2 status " + service2.getPaymentStatus()); } }
- BeanScopesApplication.java
package com.javaprogramto.bean.scopes.beanscopes; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class BeanScopesApplication { public static void main(String[] args) { SpringApplication.run(BeanScopesApplication.class, args); } }
No comments:
Post a Comment
Please do not add any spam links in the comments section.