Um pouco de gambi não faz mal a ninguém: Spring em ambientes não gerenciados

Outro dia, na Caelum, passamos por uma situação interessante. Dois colegas de trabalho, Lucas e Marcio, estavam programando no projeto de final de curso deles e tinha a seguinte situação: o projeto é todo baseado no Spring, mas mesmo assim eles decidiram realizar o deploy em um servidor de aplicação, no caso o Wildfly. É uma decisão interessante, ao invés de optar por um caminho ou outro, eles juntaram as forças das duas plataformas. Vai ser o tema do próximo post deste blog :).

E a decisão valeu a pena! Eles precisavam lidar com WebSockets e a especificação no JAVA EE 7 deixa muito simples implementar qualquer funcionalidade baseada  nesse protocolo. O problema é que agora eles tem uma classe que é gerenciada pelo servidor de aplicação, mas precisam acessar objetos que estão gerenciados pelo Spring. O que fazer? Os container não se falam e, nessa situação, só nos restou partir para um workaround(gambi).  Dê uma olhada no esboço da classe deles:

	@ServerEndpoint("/channel/sales")
	public class AgileBoardEndpoint {
		
		@Inject
		private ConnectedUsers connectedUsers;
		//dao gerenciado pelo Spring
		@AutoWired
		private TaskDao tasks;
		
		@OnOpen
		public void onNewUser(Session session){
			connectedUsers.add(session);
		}
		
		@OnMessage
		public void onMessage(String message) {
			tasks.save(new Task(message));
			connectedUser.alert(task);
		}
	}

Não adianta eles anotarem nenhum atributo com AutoWired, já que, como dissemos, o objeto desta classe não vai ser instanciado pelo Spring. Para resolver tal situação é necessário que você acesse o objeto que representa uma parte do container do Spring de maneira programática.

	@ServerEndpoint("/channel/sales")
	public class AgileBoardEndpoint {
		
		@Inject
		private ConnectedUsers connectedUsers;
		
		@OnOpen
		public void onNewUser(Session session){
			connectedUsers.add(session);
		}
		
		@OnMessage
		public void onMessage(String message) {
                        ApplicationContext ctx = ApplicationContextHolder.getInstance();
			TaskDao tasks = ctx.getBean(TaskDao.class);
			tasks.save(new Task(message));
			connectedUser.alert(task);
		}
	}

A classe ApplicationContext possui o método getBean, que te permite fazer o lookup de um objeto pela sua classe. A instância retornada foi criada exatamente da mesma forma que seria se você tivesse pedido a injeção pelo atributo usando o AutoWired. A pergunta que fica é: como vamos obter uma instância do objeto do tipo ApplicationContext?  É justamente que entra nossa solução de contorno(workaround).

	@Component
	public class ApplicationContextHolder implements ApplicationContextAware{
		
		private static ApplicationContext instance;

		@Override
		public void setApplicationContext(ApplicationContext applicationContext)
				throws BeansException {
			ApplicationContextHolder.instance = applicationContext;
		}

		public static ApplicationContext getInstance(){
	        return instance;
		}


	}

Implementamos a interface ApplicationContextAware quando precisamos ser notificados da criação do ApplicationContext. É parecido com um desses listeners que implementamos quando queremos ser notificados, por exemplo, sobre o inicio do nosso servlet container. Como, em geral, todas as classes ficam gerenciadas sob o mesmo contexto, podemos guardar a instância passada em um atributo estático.

O benefício dessa solução é que conseguimos acessar o contexto do Spring de qualquer lugar, foi justamente o que fizemos a partir da classe que lidava com os WebSockets do JAVA EE. É um bom truque para integrar duas pontas que não se falam muito. O malefício, é exatamente o mesmo :). Você pode acessar de qualquer lugar, tem que tomar cuidado para não sair usando este poder onde não é necessário e acabar prejudicando a manutenção do código. Outro ponto ruim é que você acabou de deixar seu código mais difícil de testar.

Este tipo de decisão acontece todos os dias enquanto estamos desenvolvendo. O importante é saber o que ganhamos e perdemos, porque assim vamos por um caminho baseado em um julgamento crítico, sabendo exatamente as consequências.  No mundo dos sonhos, o Spring vai implementar a especificação do CDI e vai ficar tudo bem para a gente. Lembre, saber tirar proveito da tecnologia que você usa, faz muita diferença no produto final que será entregue.

One thought on “Um pouco de gambi não faz mal a ninguém: Spring em ambientes não gerenciados

Leave a comment