Pages

Wednesday, April 22, 2020

Singleton Scope Examples in Spring Boot

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.

Spring Boot Singleton Scope Examples

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);
         }
        
        }

Spring Singleton Scopes

No comments:

Post a Comment

Please do not add any spam links in the comments section.