Spring Bean Scopes allows us to have more granular control of the bean instances creation. Sometimes we want to create bean instance as singleton but in some other cases we might want it to be created on every request or once in a session. Show
Spring Bean ScopesThere are five types of spring bean scopes:
Spring Bean Singleton and Prototype ScopeSpring bean singleton and prototype scopes can be used in standalone spring apps. Let’s see how we can easily configure these scopes using @Scope annotation. Let’s say we have a java bean class. package com.journaldev.spring; public class MyBean { public MyBean() { System.out.println("MyBean instance created"); } }Let’s define the spring configuration class where we will define the method to get MyBean instance from spring container. Note that singleton is default scope, so we can remove @Scope(value="singleton") from above bean definition. Now let’s create a main method and test the singleton scope. package com.journaldev.spring; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class MySpringApp { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(MyConfiguration.class); ctx.refresh(); MyBean mb1 = ctx.getBean(MyBean.class); System.out.println(mb1.hashCode()); MyBean mb2 = ctx.getBean(MyBean.class); System.out.println(mb2.hashCode()); ctx.close(); } }When above program is executed, we will get output like below. MyBean instance created 867988177 867988177Notice that both MyBean instances have same hashcode and the constructor is called once once, it means that spring container is returning the same instance of MyBean always. Now let’s change the scope to prototype. @Bean @Scope(value="prototype") public MyBean myBean() { return new MyBean(); }This time we will get following output when main method is executed. It’s clear that MyBean instance is created every time it’s requested from spring container. Now let’s change the scope to request. @Bean @Scope(value="request") public MyBean myBean() { return new MyBean(); }In this case, we will get following exception. Exception in thread "main" java.lang.IllegalStateException: No Scope registered for scope name 'request' at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:347) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:224) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1015) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:339) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1107) at com.journaldev.spring.MySpringApp.main(MySpringApp.java:12)It’s because request, session and global-session scopes are not available for standalone applications. Spring Bean Request and Session ScopeFor spring bean request and session scope example, we will create Spring Boot web application. Create a spring boot starter project and choose “web” so that we can run it as a web application. Our final project will look like below image. ServletInitializer and SpringBootMvcApplication are auto generated spring boot classes. We don’t need to make any changes there. Here is my pom.xml file, have a look at the dependencies for our application. Your pom.xml file might be slightly different based on the Eclipse version you are using.Let’s create some spring components and configure them as spring beans in the spring container with scope as request and session. Spring Bean Request Scopepackage com.journaldev.spring; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; @Component @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) public class DataRequestScope { private String name = "Request Scope"; public DataRequestScope() { System.out.println("DataRequestScope Constructor Called"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }Spring Bean Session Scopepackage com.journaldev.spring; import org.springframework.context.annotation.Scope; import java.time.LocalDateTime; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; @Component @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) public class DataSessionScope { private String name = "Session Scope"; public DataSessionScope() { System.out.println("DataSessionScope Constructor Called at "+LocalDateTime.now()); } public String getName() { return name; } public void setName(String name) { this.name = name; } }Spring ComponentNow let’s create a spring component and use spring to auto configure above beans. package com.journaldev.spring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Customer { @Autowired private DataRequestScope dataRequestScope; @Autowired private DataSessionScope dataSessionScope; public DataRequestScope getDataRequestScope() { return dataRequestScope; } public void setDataRequestScope(DataRequestScope dataRequestScope) { this.dataRequestScope = dataRequestScope; } public DataSessionScope getDataSessionScope() { return dataSessionScope; } public void setDataSessionScope(DataSessionScope dataSessionScope) { this.dataSessionScope = dataSessionScope; } }Spring Rest ControllerFinally, let’s create a RestController class and configure some API end points for our testing purposes. package com.journaldev.spring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloData { @Autowired private Customer customer; @RequestMapping("/nameRS") public String helloRS() { return customer.getDataRequestScope().getName(); } @RequestMapping("/nameSSUpdated") public String helloSSUpdated() { customer.getDataSessionScope().setName("Session Scope Updated"); return customer.getDataSessionScope().getName(); } @RequestMapping("/nameSS") public String helloSS() { return customer.getDataSessionScope().getName(); } }Spring Boot Session Timeout ConfigurationFinally we have to configure spring boot session timeout variables, add below properties in src/main/resources/application.properties. Now our spring beans with session scope will be invalidated in one minute. Just run the SpringBootMvcApplication class as spring boot application. You should see below output for our endpoints being configured. 2018-05-23 17:02:25.830 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameRS]}" onto public java.lang.String com.journaldev.spring.HelloData.helloRS() 2018-05-23 17:02:25.831 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameSSUpdated]}" onto public java.lang.String com.journaldev.spring.HelloData.helloSSUpdated() 2018-05-23 17:02:25.832 INFO 6921 --- [main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/nameSS]}" onto public java.lang.String com.journaldev.spring.HelloData.helloSS()Spring Bean Request Scope TestOpen any browser and go to URL https://localhost:8080/nameRS and check the console output. You should see DataRequestScope Constructor Called getting printed on each request. Spring Bean Session Scope TestGo to https://localhost:8080/nameSS and you would get following output. Now go to https://localhost:8080/nameSSUpdated so that DataSessionScope name value is updated to Session Scope Updated. Now again go to https://localhost:8080/nameSS and you should see the updated value. By this time, you should see DataSessionScope Constructor Called at XXX only once in the console output. Now wait for 1 minute so that our session scoped bean is invalidated. Then again go to https://localhost:8080/nameSS and you should see the original output. Also you should check console message for creation of DataSessionScope again by the container. That’s all for spring beans scope tutorial.You can download the Spring Beans Scope Spring Boot project from our GitHub Repository. What is the default scope of a bean?Spring - Bean Scopes. Is Spring bean singleton by default?By default, Spring beans are singletons. The problem arises when we try to wire beans of different scopes. For example, a prototype bean into a singleton. This is known as the scoped bean injection problem.
Why Spring bean scope is singleton by default?When a bean is a singleton, only one shared instance of the bean will be managed and all requests for beans with an id or ids matching that bean definition will result in that one specific bean instance being returned. Only when you have to keep some session details you should use for example session scope.
|