Articles

Guia rápida dos âmbitos do feijão de Primavera

Posted on

Visão geral

Neste tutorial rápido, vamos aprender sobre os diferentes tipos de âmbitos do feijão no quadro da Primavera.

O âmbito de um feijão define o ciclo de vida e a visibilidade desse feijão nos contextos em que o utilizamos.

A última versão do quadro da Primavera define 6 tipos de âmbitos:

  • singleton
  • prototype
  • request
  • session
  • application
  • websocket

Os últimos quatro âmbitos mencionados, pedido, sessão, aplicação e websocket, só estão disponíveis numa aplicação web-aware.

Outra leitura:

O que é um Feijão de Primavera?

Uma explicação rápida e prática do que é um Feijão de Primavera.
Leia mais →

Notações do Feijão de Primavera

Saiba como e quando utilizar as anotações padrão do feijão de Primavera – @Component, @Repository, @Service e @Controller.
Ler mais →

Singleton Scope

Quando definimos um feijão com o singleton scope, o contentor cria uma única instância desse feijão; todos os pedidos para esse nome de feijão devolverão o mesmo objecto, que está em cache. Quaisquer modificações ao objecto serão reflectidas em todas as referências ao feijão. Este escopo é o valor por defeito se nenhum outro escopo for especificado.

Vamos criar uma entidade Pessoa para exemplificar o conceito de escopo:

public class Person { private String name; // standard constructor, getters and setters}

Depois, definimos o feijão com o escopo de um botão utilizando a anotação @Scope:

@Bean@Scope("singleton")public Person personSingleton() { return new Person();}

Também podemos utilizar uma constante em vez do valor da String da seguinte forma:

@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)

Agora podemos proceder à escrita de um teste que mostra que dois objectos referentes ao mesmo feijão terão os mesmos valores, mesmo que apenas um deles mude o seu estado, uma vez que ambos se referem à mesma instância de feijão:

private static final String NAME = "John Smith";@Testpublic void givenSingletonScope_whenSetName_thenEqualNames() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml"); Person personSingletonA = (Person) applicationContext.getBean("personSingleton"); Person personSingletonB = (Person) applicationContext.getBean("personSingleton"); personSingletonA.setName(NAME); Assert.assertEquals(NAME, personSingletonB.getName()); ((AbstractApplicationContext) applicationContext).close();}

Os âmbitos.O ficheiro xml neste exemplo deve conter as definições xml dos feijões utilizados:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.baeldung.scopes.Person" scope="singleton"/> </beans>

Escopo do protótipo

Um feijão com o escopo do protótipo devolverá uma instância diferente cada vez que for solicitado ao recipiente. Define-se definindo o protótipo de valor para a anotação @Scope na definição do feijão:

@Bean@Scope("prototype")public Person personPrototype() { return new Person();}

Podemos também usar uma constante como fizemos para o escopo de um botão:

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Agora vamos escrever um teste semelhante ao anterior que mostra dois objectos solicitando o mesmo nome de feijão com o escopo do protótipo. Eles terão estados diferentes, uma vez que já não se referem à mesma instância de feijão:

private static final String NAME = "John Smith";private static final String NAME_OTHER = "Anna Jones";@Testpublic void givenPrototypeScope_whenSetNames_thenDifferentNames() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml"); Person personPrototypeA = (Person) applicationContext.getBean("personPrototype"); Person personPrototypeB = (Person) applicationContext.getBean("personPrototype"); personPrototypeA.setName(NAME); personPrototypeB.setName(NAME_OTHER); Assert.assertEquals(NAME, personPrototypeA.getName()); Assert.assertEquals(NAME_OTHER, personPrototypeB.getName()); ((AbstractApplicationContext) applicationContext).close();}

Os âmbitos de aplicação.O ficheiro xml é semelhante ao apresentado na secção anterior, acrescentando a definição xml para o feijão com o escopo do protótipo:

<bean class="org.baeldung.scopes.Person" scope="prototype"/>

Web Aware Scopes

Como mencionado anteriormente, existem quatro escopos adicionais que só estão disponíveis num contexto de aplicação web-aware. Utilizamo-los menos frequentemente na prática.

O âmbito do pedido cria uma instância de feijão para um único pedido HTTP, enquanto o âmbito da sessão cria uma instância de feijão para uma Sessão HTTP.

O âmbito de aplicação cria a instância de feijão para o ciclo de vida de um Contexto ServletContext, e o âmbito de websocket cria-a para uma sessão WebSocket específica.

Vamos criar uma classe a utilizar para instanciar os feijões:

public class HelloMessageGenerator { private String message; // standard getter and setter}

4.1. Request Scope

Podemos definir o feijão com o âmbito do pedido usando a anotação @Scope:

@Bean@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)public HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator();}

O atributo proxyMode é necessário porque no momento da instanciação do contexto da aplicação web, não há nenhum pedido activo. Spring cria um proxy a ser injectado como uma dependência, e instancia o feijão alvo quando é necessário num pedido.

p>Podemos também usar uma anotação @RequestScope composta que actua como atalho para a definição acima:

@Bean@RequestScopepublic HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator();}

P>A seguir podemos definir um controlador que tem uma referência injectada ao pedidoScopedBean. Precisamos de aceder ao mesmo pedido duas vezes para testar os âmbitos específicos da web.

Se mostrarmos a mensagem cada vez que o pedido é executado, podemos ver que o valor é reposto a zero, mesmo que mais tarde seja alterado no método. Isto deve-se ao facto de uma instância de feijão diferente ser devolvida para cada pedido.

@Controllerpublic class ScopesController { @Resource(name = "requestScopedBean") HelloMessageGenerator requestScopedBean; @RequestMapping("/scopes/request") public String getRequestScopeMessage(final Model model) { model.addAttribute("previousMessage", requestScopedBean.getMessage()); requestScopedBean.setMessage("Good morning!"); model.addAttribute("currentMessage", requestScopedBean.getMessage()); return "scopesExample"; }}

4.2. Âmbito da sessão

Podemos definir o feijão com o âmbito da sessão de forma semelhante:

@Bean@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)public HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator();}

Existe também uma anotação composta dedicada que podemos utilizar para simplificar a definição do feijão:

@Bean@SessionScopepublic HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator();}

A seguir definimos um controlador com uma referência à sessãoScopedBean. Mais uma vez, precisamos de executar dois pedidos para mostrar que o valor do campo da mensagem é o mesmo para a sessão.

Neste caso, quando o pedido é feito pela primeira vez, a mensagem de valor é nula. Contudo, uma vez alterado, esse valor é retido para pedidos subsequentes, uma vez que a mesma instância do feijão é devolvida para toda a sessão.

@Controllerpublic class ScopesController { @Resource(name = "sessionScopedBean") HelloMessageGenerator sessionScopedBean; @RequestMapping("/scopes/session") public String getSessionScopeMessage(final Model model) { model.addAttribute("previousMessage", sessionScopedBean.getMessage()); sessionScopedBean.setMessage("Good afternoon!"); model.addAttribute("currentMessage", sessionScopedBean.getMessage()); return "scopesExample"; }}

4.3. Application Scope

O âmbito da aplicação cria a instância do feijão para o ciclo de vida de um ServletContext.

Isto é semelhante ao âmbito de um botão, mas há uma diferença muito importante no que diz respeito ao âmbito do feijão.

Quando os feijões são escopados por aplicação, a mesma instância do feijão é partilhada entre várias aplicações baseadas em servletContext, enquanto os feijões escopados por botão são escopados apenas para um contexto de aplicação única.

Vamos criar o feijão com o âmbito da aplicação:

@Bean@Scope( value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)public HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator();}

Análogo ao âmbito do pedido e da sessão, podemos utilizar uma versão mais curta:

@Bean@ApplicationScopepublic HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator();}

Agora vamos criar um controlador que faça referência a este feijão:

@Controllerpublic class ScopesController { @Resource(name = "applicationScopedBean") HelloMessageGenerator applicationScopedBean; @RequestMapping("/scopes/application") public String getApplicationScopeMessage(final Model model) { model.addAttribute("previousMessage", applicationScopedBean.getMessage()); applicationScopedBean.setMessage("Good afternoon!"); model.addAttribute("currentMessage", applicationScopedBean.getMessage()); return "scopesExample"; }}

Neste caso, uma vez definido na aplicaçãoScopedBean, a mensagem de valor será retida para todos os pedidos subsequentes, sessões e mesmo para diferentes aplicações de servlet que acedam a este feijão, desde que este esteja a correr no mesmo ServletContext.

4.4. WebSocket Scope

Finalmente, vamos criar o feijão com o websocket scope:

@Bean@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)public HelloMessageGenerator websocketScopedBean() { return new HelloMessageGenerator();}

Quando se acede pela primeira vez, os feijões WebSocket scoped são armazenados nos atributos da sessão WebSocket. A mesma instância do feijão é então devolvida sempre que esse feijão é acedido durante toda a sessão WebSocket.

Podemos também dizer que apresenta um comportamento de um só botão, mas limitado a uma sessão WebSocket.

Conclusion

Neste artigo, discutimos os diferentes âmbitos do feijão fornecidos pela Primavera e quais são os seus usos pretendidos.

A implementação deste artigo pode ser encontrada no projecto GitHub.

Comece com Spring 5 e Spring Boot 2, através do curso Learn Spring:

>> O CURSO

/div>

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *