Escopos de Beans do Spring
Os Escopos de Beans do Spring nos permitem ter um controle mais granular na criação das instâncias de beans. Às vezes, queremos criar uma instância de bean como singleton, mas em outros casos, podemos querer que ela seja criada a cada solicitação ou uma vez por sessão.
Existem cinco tipos de beans do Spring escopos:
- singleton – apenas uma instância do bean do Spring será criada para o contêiner do Spring. Este é o escopo padrão do bean do Spring. Ao usar este escopo, certifique-se de que o bean não tenha variáveis de instância compartilhadas, caso contrário, pode levar a problemas de inconsistência de dados.
- prototype – Uma nova instância será criada sempre que o bean for solicitado ao contêiner do Spring.
- request – Isso é o mesmo que o escopo prototype, 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 contêiner.
- global-session – Isso é usado para criar beans de sessão global para aplicativos Portlet.
Singleton e Escopo 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 em 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 definiremos 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 mudar 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
É claro que a instância de MyBean é criada toda vez que é solicitada pelo contêiner Spring. Agora, vamos mudar o escopo para request
.
@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 aplicativos independentes.
Escopo de Solicitação e Sessão do Bean Spring
Para um exemplo de escopo de solicitação e sessão do bean Spring, criaremos um aplicativo da web Spring Boot. Crie um projeto inicializador do Spring Boot e escolha “web” para que possamos executá-lo como um aplicativo da web. Nosso projeto final se parecerá com a imagem abaixo.
ServletInitializer
e SpringBootMvcApplication
são classes geradas automaticamente pelo Spring Boot. Não precisamos fazer nenhuma alteração lá. Aqui está o meu arquivo pom.xml, dê uma olhada nas dependências para o nosso aplicativo. Seu arquivo pom.xml pode ser ligeiramente diferente com base na 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 Solicitação do Bean 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 do 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 de Rest do Spring
Finalmente, vamos criar uma classe RestController e configurar alguns pontos de extremidade 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 do Tempo Limite da Sessão do 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. Basta executar a classe SpringBootMvcApplication
como aplicação Spring Boot. Você deve ver a saída abaixo para nossos pontos de extremidade 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 Requisição do Spring Bean
Abra qualquer navegador e vá para o URL https://localhost:8080/nameRS
e verifique a saída no console. Você deverá ver Construtor DataRequestScope Chamado
sendo impresso a cada requisição.
Teste de Escopo de Sessão do Spring Bean
Vá para https://localhost:8080/nameSS
e você receberá 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 no console para a criação de 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 GitHub.
Source:
https://www.digitalocean.com/community/tutorials/spring-bean-scopes