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?
Notações do Feijão de Primavera
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>