MentaContainer: IoC simples e rápido

Estou disponibilizando o MentaContainer, que é um framework de IoC simple, prático, rápido, sem XML e sem anotações. Suporta Injection, DI, Wiring, Singleton, blah, blah, blah, com a diferença de ser simples de usar e fácil de entender. É mais uma alternativa em relação ao Spring, Guice e PicoContainer.

Comparem com esses outros e coloquem seus comentários e sugestões.

http://www.seducaotecnologica.com.br/mentacontainer-ioc-simples-rapido/

-Sergio

PS: Quando você lê um livro, você gosta de ler um texto limpinho ou um texto repleto de anotações? Anotação = remendo de código.

Sergio, usa Generics no método get para não precisar fazer Casting

O get pode retornar qualquer coisa… Não dá para configurar isso não… Só se vc tivesse passando o type como parâmetro, o que não faz parte da API que retorna beans por NOME…

Não tem como fazer isso:

ex:

[code]public class Teste {
public T get(String name) {
return (T) new Date();
}

public static void main(String[] args) {
	Teste teste = new Teste();
	Date date = teste.get("blablah");
}

}[/code]

?

[quote=peerless]Não tem como fazer isso:

ex:

[code]public class Teste {
public <T> T get(String name) {
return (T) new Date();
}

public static void main(String[] args) {
	Teste teste = new Teste();
	Date date = teste.get("blablah");
}

}[/code]

?
[/quote]

Funcionou bonito !!! Espero agora nunca mais me esquecer desse truque esperto. MUITO OBRIGADO !!!

Bacana, Sergio. Esse container será usado nas próximas versões do Mentawai?

Show de bola Sérgio, estava pensando em uma coisa junto ao mentawai, mas vc fez logo um independente… muito bom.
Agora pode ser até usado em projetos Desktop, e tambem da pra fazer nossos TEST CASE bem mais fácil, e tambem chamar ações fora do contexto/request de forma simples.

A primeira idéia é essa. O Mentawai, como o Rufino falou, só suporta IoC dentro das actions, o que funciona bem para as actions, mas já para os test cases e chamadas assíncronas, não…

Claro que vc pode usar Spring, Guice ou outra coisa junto com ele para contornar esse problema, mas agora essa facilidade virá out-of-the-box com o MentaContainer.

saoj,

como você espera que seja o uso do MentaContainer?

qual é a vantagem dele sobre o PicoContainer?

já leu esse post do Uncle Bob?
http://blog.objectmentor.com/articles/2010/01/17/dependency-injection-inversion

O primeiro uso será dentro do Mentawai, como um case-proof. Hoje o Mentawai oferece IoC e AutoWiring dentro das actions, mas se você queria usar o container por fora de uma action, por exemplo para testes ou tasks assíncronas sem qualquer request por trás, você ficava na mão, isto é, tinha que instanciar e configurar os seus componentes na mão. Nada difícil mas feio. Claro que vc podia utilizar ele com Spring ou Guice, mas agora com esse container integrado vai ficar tudo transparente e abstraído. Basta pegar a referencia do container e partir para o abraço.

O MentaContainer é bem pequeno, não depende de nada e por ser totalmente baseado em configuração programática e sem annotations você pode integrá-lo de forma fácil a qualquer outro framework. Vc também pode utilizá-lo em seus projetos de forma não-obstrusiva.

Me parece que o pico container não registra os componentes por nome, apenas por type. Tente fazer aqueles exemplos básicos do MentaContainer com o Pico. Tenho minhas dúvidas se será tão simples…

Acabei de ler por alto. Mas ele critica IoC ??? Claro que qualquer coisa usada de forma errada, exagerada ou desnecessária será ruim, mas todo mundo hoje entende que IoC é essencial pra quase tudo.

Seu código:

Container c = new MentaContainer();
	 
c.ioc("userDAO", JdbcUserDAO.class);
	 
c.ioc("connection", Connection.class);
	 
c.autowire("conn", Connection.class, "connection");
	 
SomeService service = new SomeService();
	 
c.populate(service);

Pico container:

MutablePicoContainer c = new DefaultPicoContainer();
c.addComponent("userDAO", JdbcUserDAO.class);
	 
c.addComponent("connection", Connection.class);

c.addComponent("someService", SomeService.class);

SomeService service = (SomeService) c.getComponent("someService");

é a mesma coisa, só muda o nome dos métodos… e o pico até é mais simples, pq vc não precisa pedir pra fazer o autowire: ele faz sozinho, já é suposto…
e sim, dá pra registrar componentes por nome (o código acima tá usando isso)

Cuidado pra não reinventar a roda.

ele não critica IoC, ele critica a necessidade de usar um Container de IoC para fazer coisas comuns, em que fazer na mão é muito mais fácil…

Containers IoC são bastante úteis qdo integrado a frameworks como o VRaptor, o Spring MVC, o proprio EJB com CDI…
usar um container na mão só pra instanciar objetos numa classe da sua aplicação é meio desperdício, usar o construtor da classe e fazer na mão é melhor!
é isso que o Uncle Bob está falando

Legal, não sabia que suportava por nome tb, mas isso realmente me pareceu básico dele suportar. O exemplo da introdução no site deles não menciona isso e achei que por ser básico deveria aparecer ali. Então pensei que não suportava.

AutoWire totalmente automático é loucura. Funciona em hello world, mas em qualquer projeto maior vc vai começar a ter clash de dependencia. Por isso no MentaContainer isso é explícito por nome e type. Deixar apenas por type tem dois problemas:

  1. Clash como eu falei

  2. Demasiadamente custoso, pois agora vc tem que checar todas as propriedades de todo mundo e fazer um cruzamento entre todas as classes e suas propriedades. Quando vc define uma lista finita de dependencias isso fica bem mais simples e rápido.

Não sei se o Pico te permite especificar as dependencias. Provavelmente sim, mas olhei o javadoc deles e não encontrei nada como addDependency.

Mas tudo bem, podem usar o Pico ao invés do MentaContainer se assim desejarem. Vcs são livres pra isso. O PicoContainer é muito bom. Não sei porque o Google não usou o PicoContainer quando precisou de um container de IoC com configuração programática. Eles adoram re-inventar a roda por lá, a começar pelo próprio buscador…

Já pra mim valeu muito a pena porque o Mentawai suporta IoC e DI e AutoWiring desde 2005 quando foi lançado e eu apenas extraí essa funcionalidade num framework separado. Valeu como aprendizado e principalmente para fazer com que o Mentawai se torne um container de IoC genérico e não apenas para Actions. Claro que usando ele com Spring/Guice/Pico/XXX vc tb consegue isso, mas lembre-se que desde 2005, antes de Struts2, VRaptor2, VRaptor3, Seam, etc. o framework possui a filosofia de ser FULLSTACK, sem XML e sem Annotations. É isso que diferencia ele dos outros 10 milhões de frameworks. A roda fica diferente das outras porque tem sua própria integridade.

Ola

Vc poderia ter algo como

Date d = ioc.get("Hoje",Data.class);

Cujo codigo pode ser

[code] public T get(String key, Class aClass) throws Exception {
try {
Object c = data.get(key).newInstance();

        return (T) c;
    } catch (InstantiationException ex) {
        throw new Exception("ops");
    } catch (IllegalAccessException ex) {
        throw new Exception("ops");
    }
}[/code]

[quote=saoj]Legal, não sabia que suportava por nome tb, mas isso realmente me pareceu básico dele suportar. O exemplo da introdução no site deles não menciona isso.

AutoWire totalmente automático é loucura. Funciona em hello world, mas em qualquer projeto maior vc vai começar a ter clash de dependencia.
[/quote]
Não concordo…
Se você faz Inversão de Controle do jeito certo, vc não tem ciclos de dependência, e portanto não tem clash…

clash é só qdo vc precisa instanciar uma classe mais de uma vez com configurações diferentes…
mas nesse caso será que o problema não está no design da sua aplicação?

se a sua classe tem muitas dependências, de novo, o problema provavelmente está no design da sua aplicação!
se sua classe só tem 2 ou 3 dependências, não é tão mais custoso

Bom, boa sorte aí com seu framework novo, vc vai aprender bastante coisa com ele :wink:

[]'s

Clash não é ciclo, mas o caso que vc tem duas variáveis do mesmo tipo, ou que implementam a mesma interface, daí vc fica sem ter como saber qual que recebe o quê. Especificando explicitamente vc nunca terá esse problema porque vc especifica o tipo e o NOME da propriedade.

O problema de não especificar nada é que um objeto pode ter 100 propriedades mas apenas uma é uma dependencia que vai precisar de wiring. Melhor especificá-la pois vc ganha três vezes:

:arrow: A coisa fica clara e vc sabe exatamente o que está dependendo de que.

:arrow: Fica mais fácil (e rápido) para o container achar essa dependencia e injetá-la, isto é, não precisa checar cada uma das 100 propriedades.

:arrow: Menos risco de vc injetar algo sem querer, porque o container achou uma propriedade na super.super.super.super class e calhou de ter algo parecido definido no container.

Acho que vc misturou. A minha aplicacao provavelmente vai ter poucas dependencias e EXATAMENTE POR CAUSA DISSO não custa nada definí-las na mão ao invés de deixar o container adivinhá-las.

Menos linhas de código não é necessariamente melhor. As vezes é bom saber o que está acontecendo e manter o controle. :wink:

MentaContanier => http://forum.seducaotecnologica.com.br/posts/list/5.page

1 ano depois :wink:

ignorando o problema do objeto ter 100 propriedades, nenhum dos IOC containers faz autowiring automático de setters. Alguns fazem de construtor, que eu acho bem positivo.

A propria classe tem que saber quais são as suas dependências, não um cara externo (Separation of Concerns)

mais rápido ainda é usar o setter, já que vc vai ter que falar propriedade por propriedade :wink:

pra isso existem testes de integração :wink:

nesse caso é melhor usar setters então :wink:

concordo que não necessariamente é melhor. Mas com o autowiring vc também tem controle sobre o que está sendo injetado
(anotando o setter ou a propriedade, ou recebendo no construtor).

sobre o MentaContainer: ainda acho o PicoContainer bem mais simples, e se vc quer mais controle o Guice faz de um jeito bem mais legal.

[quote=Lucas Cavalcanti]1 ano depois :wink:
[/quote]

Tive que gastar um tempo pensando para te responder direito.

Isso faz sentido mesmo, mas daí teria que se partir para auto-wiring implicito via construtor ao invés de wiring explicito via setter. Talvez fosse o caso de se pensar nessa funcionalidade para o MentaContainer.

Como vc usa o PICO ou o GUICE com um web framework? Possuem esses o escopo THREAD com clearing de instancias, o que equivale ao escopo REQUEST?

// The container supports the two methods below:  
  
	/**
	 * Clear all cached instances for that scope. If you have a thread-pool for example you will
	 * want to clear the THREAD scope when your thread is returned to the pool. Because you have a thread
	 * pool you will have the SAME thread handling different requests and each request will need its own instances
	 * from the container. Therefore, each time you are done with a thread and it is returned to your thread-pool
	 * you can call clear to release the instances allocated and cached by the container. A web container and/or framework
	 * can use this feature to implement a REQUEST scope which is nothing more than the THREAD scope with clear. If the web
	 * container was not using a thread-pool, the THREAD scope would be equal to the REQUEST scope as each request would
	 * always be handled by a different thread.
	 *  
	 * It does not make sense to clear a NONE scope (the method returns doing nothing). You can clear a SINGLETON scope if necessary.
	 * 
	 * @param scope The scope to be cleared.
	 */
    public void clear(Scope scope);  
      
    /** 
     * Clear a single key from cache and return the instance that was cached. 
     *  
     * @param key The key representing the bean inside the container. 
     * @return The value that was cached and it is not anymore (was cleared) or null if nothing was cleared 
     */  
    public &lt;T&gt; T clear(String key);  
package org.mentacontainer;  
  
/** 
* Some components can also implement this interface to perform some cleanup 
* when the instance is cleared. For example, a connection pool will want 
* to know when the connection instance is cleared so it can return it to 
* the pool. 
*  
* It makes more sense to use this interface for components that will be placed 
* in the THREAD scope, but you can also use it with components in the SINGLETON 
* scope. 
*  
* This is particular useful for the THREAD scope for dealing with thread pools, so 
* when the thread is returned to the thread pool you will want to clear the THREAD 
* scope. That's pretty much how web containers work: one thread per request coming from 
* a thread pool. 
*  
* @author sergio.oliveira.jr@gmail.com 
* 
* @param &lt;E&gt; 
*/  
public interface Clearable&lt;E&gt; {  
      
    public void onCleared(E clearedObject);  
}  

o guice tem um subprojeto chamado guice-web que já tem os escopos de request e session…

o pico é lightweight, então vc que controla os escopos

a anotação @PreDestroy funciona para limpar os objetos em ambos os casos (e faz parte do java SE).

De novo, não precisa reinventar a roda, já está pronto.

Excelente

Não vi o escopo thread lá com destroy/clear, mas talvez tenha escondido em algum lugar…

Sou contra anotacoes. Prefiro implementar uma interface a anotar a minha classe, mas isso é gosto pessoal.

O que eu quero dizer é que o container tem que suportar o escopo REQUEST ( = THREAD + CLEAR) primeiro. Uma vez suportando isso a sua classe pode ser anotada ou pode implementar uma interface para que libere resources num destroy.

Se for assim ninguém faz mais nada porque tudo já está feito. Por que o Google fez o Guice se já tinha PICO e Spring? Por que o google fez um search engine se já tinha Yahoo e Altavista? Por que fizeram Seam se já tinha Struts, JSF, blah? A beleza está nas diferencas sutis, nos detalhes de implementacao, na simplicidade, na documentacao… Re-inventar a roda é um termo depreciativo, pois sugere que o cara vai fazer uma coisa totalmente igual a outra… Difícil haver dos sistemas / frameworks iguais… Programacao e design de APIs pode ser uma arte.

Constructor X Setter Injection => http://blog.springsource.com/2007/07/11/setter-injection-versus-constructor-injection-and-the-use-of-required/