Os escopos de Beans do Spring nos permitem ter um controle mais granular da criação das instâncias dos beans. Às vezes, queremos criar uma única instância do bean como singleton, mas em outros casos, podemos querer que ele seja criado a cada solicitação ou apenas uma vez em uma sessão.
Escopos de Beans do Spring
Existem cinco tipos de escopos de beans do Spring:
- singleton – apenas uma instância do bean do Spring será criada para o container do Spring. Este é o escopo padrão do bean do Spring. Ao usar esse escopo, certifique-se de que o bean não tenha variáveis de instância compartilhadas, caso contrário, poderá levar a problemas de inconsistência de dados.
- prototype – uma nova instância será criada sempre que o bean for solicitado ao container do Spring.
- request – é o mesmo que o escopo de protótipo, no entanto, é destinado a ser usado em aplicativos da web. Uma nova instância do bean será criada para cada solicitação HTTP.
- session – um novo bean será criado para cada sessão HTTP pelo container.
- global-session – isso é usado para criar beans de sessão global para aplicativos de portlet.
Escopo Singleton e Protótipo do Spring Bean
Os escopos singleton e protótipo do Spring podem ser usados em aplicativos Spring autônomos. Vamos ver como podemos configurar facilmente esses escopos usando a anotação @Scope
. Digamos que temos uma classe de bean Java.
package com.journaldev.spring;
public class MyBean {
public MyBean() {
System.out.println("MyBean instance created");
}
}
Vamos definir a classe de configuração do Spring onde iremos definir o método para obter uma instância de MyBean do contêiner Spring.
package com.journaldev.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MyConfiguration {
@Bean
@Scope(value="singleton")
public MyBean myBean() {
return new MyBean();
}
}
Observe que singleton
é o escopo padrão, então podemos remover @Scope(value="singleton")
da definição de bean acima. Agora, vamos criar um método principal e testar o escopo singleton.
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();
}
}
Quando o programa acima é executado, obteremos uma saída como abaixo.
MyBean instance created
867988177
867988177
Observe que ambas as instâncias de MyBean têm o mesmo hashcode e o construtor é chamado apenas uma vez, o que significa que o contêiner Spring está sempre retornando a mesma instância de MyBean. Agora, vamos alterar o escopo para protótipo
.
@Bean
@Scope(value="prototype")
public MyBean myBean() {
return new MyBean();
}
Desta vez, obteremos a seguinte saída quando o método principal é executado.
MyBean instance created
867988177
MyBean instance created
443934570
Está claro que a instância de MyBean é criada toda vez que é solicitada ao contêiner Spring. Agora, vamos alterar o escopo para requisição
.
@Bean
@Scope(value="request")
public MyBean myBean() {
return new MyBean();
}
Neste caso, obteremos a seguinte exceção.
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)
É porque os escopos request
, session
e global-session
não estão disponíveis para aplicações independentes.
Escopo de Solicitação e Sessão do Bean Spring
Para o exemplo de escopo de bean de solicitação e sessão do Spring, criaremos uma aplicação web Spring Boot. Crie um projeto inicializador do Spring Boot e escolha “web” para que possamos executá-lo como uma aplicação web. Nosso projeto final ficará como na imagem abaixo.
ServletInitializer
e SpringBootMvcApplication
são classes Spring Boot geradas automaticamente. Não precisamos fazer nenhuma alteração lá. Aqui está o meu arquivo pom.xml, dê uma olhada nas dependências para nossa aplicação. Seu arquivo pom.xml pode ser ligeiramente diferente dependendo da versão do Eclipse que você está usando.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.spring</groupId>
<artifactId>Spring-Boot-MVC</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Spring-Boot-MVC</name>
<description>Spring Beans Scope MVC</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>10</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Vamos criar alguns componentes Spring e configurá-los como beans Spring no contêiner Spring com escopo request
e session
.
Escopo de Bean de Solicitação do Spring
package 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;
}
}
Escopo de Sessão Spring Bean
package 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;
}
}
Componente Spring
Agora vamos criar um componente Spring e usar o Spring para configurar automaticamente os beans acima.
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;
}
}
Controlador Rest do Spring
Finalmente, vamos criar uma classe RestController e configurar alguns pontos finais da API para nossos propósitos de teste.
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();
}
}
Configuração de Tempo Limite da Sessão Spring Boot
Finalmente, temos que configurar as variáveis de tempo limite da sessão do Spring Boot, adicionar as propriedades abaixo em src/main/resources/application.properties
.
server.session.cookie.max-age= 1
server.session.timeout= 1
Agora nossos beans Spring com escopo de sessão serão invalidados em um minuto. Apenas execute a classe SpringBootMvcApplication
como uma aplicação Spring Boot. Você deverá ver a saída abaixo para nossos endpoints sendo configurados.
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()
Teste de Escopo de Solicitação de Spring Bean
Abra qualquer navegador e vá para o URL https://localhost:8080/nameRS
e verifique a saída do console. Você deverá ver Construtor de DataRequestScope Chamado
sendo impresso em cada solicitação.
Teste de Escopo de Sessão de Spring Bean
Vá para https://localhost:8080/nameSS
e você obterá a seguinte saída. Agora vá para
https://localhost:8080/nameSSUpdated
para que o valor do nome de DataSessionScope
seja atualizado para Session Scope Updated
. Agora, vá novamente para
https://localhost:8080/nameSS
e você deverá ver o valor atualizado. Neste momento, você deverá ver
DataSessionScope Constructor Called at XXX
apenas uma vez na saída do console. Agora, aguarde 1 minuto para que nosso bean de escopo de sessão seja invalidado. Em seguida, vá novamente para https://localhost:8080/nameSS
e você deverá ver a saída original. Além disso, verifique a mensagem do console para a criação do DataSessionScope
novamente pelo contêiner. Isso é tudo para o tutorial de escopo de beans do Spring.
Você pode baixar o projeto Spring Beans Scope Spring Boot do nosso Repositório no GitHub.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-scopes