Dependency Injection mit Googles Guice bei mit new erzeugten Beans mt AspectJ

Thomas Darimont

Erfahrenes Mitglied
Hallo,

hier mal ein kleines Beispiel dazu wie man mit Googles neuem Dependency Injection Framework Guice
( http://code.google.com/p/google-guice/ ) auch Dependencies von Objekten verwalten kann die nicht über den Container sonder per
new (...) erzeugt werden. Dazu bedienen wir uns ein klein wenig der Technik des Springframeworks ( http://springframework.org/ )
in Verbindung mit AspectJ in dem wir einen Configuration Aspect schreiben, der die Konstruktor-
aufrufe abfängt und die daraus resultierenden Instanzen entsprechend konfiguriert.
Da Guice scheinbar an einigen Stellen noch nicht so gut mit generics klar kommt (oder ich sie falsch
Benutze hab ich diese aus meinem ursprünglichen Beispiel mal entfernt): Das Problem wird gerade hier:
http://groups.google.com/group/google-guice/browse_thread/thread/a48924f9434bfa6e diskutiert.

Hier das Beispiel:
Unser IService-Interface
Java:
/**
 * 
 */
package de.tutorials.services;

/**
 * @author Thomas.Darimont
 *
 */
public interface IService/*<TRequest extends IRequest, TResponse extends IResponse>*/{
    IResponse /*TResponse*/ process(IRequest /*TRequest*/ request);
}

Unser IRequest Interface:
Java:
/**
 * 
 */
package de.tutorials.services;

/**
 * @author Thomas.Darimont
 *
 */
public interface IRequest {
    String getData();
    void setData(String data);
}


Unser IResponse-Interface
Java:
package de.tutorials.services;

public interface IResponse {
    String getResult();
    void setResult(String result);
}

Unser MockService:
Java:
/**
 * 
 */
package de.tutorials.services.internal.mock;

import com.google.inject.Singleton;

import de.tutorials.services.IRequest;
import de.tutorials.services.IResponse;
import de.tutorials.services.IService;

/**
 * @author Thomas.Darimont
 * 
 */
@Singleton
public class MockService implements IService /*<MockRequest, MockResponse>*/{
    public IResponse /*MockRequest*/ process(IRequest /*MockResponse*/ request) {
        MockResponse response = new MockResponse();
        response.setResult(request.getData().toUpperCase());
        return response;
    }
}

Unser MockReuqest:
Java:
/**
 * 
 */
package de.tutorials.services.internal.mock;

import de.tutorials.services.IRequest;

/**
 * @author Thomas.Darimont
 *
 */
public class MockRequest implements IRequest {
    String data;

    /**
     * @return the data
     */
    public String getData() {
        return data;
    }

    /**
     * @param data the data to set
     */
    public void setData(String data) {
        this.data = data;
    }
    
}

Unsere MockResponse:
Java:
/**
 * 
 */
package de.tutorials.services.internal.mock;

import de.tutorials.services.IResponse;

/**
 * @author Thomas.Darimont
 *
 */
public class MockResponse implements IResponse {

    String result;

    /**
     * @return the result
     */
    public String getResult() {
        return result;
    }

    /**
     * @param result the result to set
     */
    public void setResult(String result) {
        this.result = result;
    }
}

unser Guice-Modle:
Java:
/**
 * 
 */
package de.tutorials;

import com.google.inject.Binder;
import com.google.inject.Module;

import de.tutorials.services.IService;
import de.tutorials.services.internal.mock.MockService;

/**
 * @author Thomas.Darimont
 * 
 */
public class MainModule implements Module {
    public void configure(Binder binder) {
        binder.bind(IService.class).to(MockService.class);
    }
}

unser Client der einen IService injected bekommen soll welcher die Configurable Annotation hat:
Java:
/**
 * 
 */
package de.tutorials;

import com.google.inject.Inject;

import de.tutorials.configuration.Configurable;
import de.tutorials.services.IRequest;
import de.tutorials.services.IResponse;
import de.tutorials.services.IService;
import de.tutorials.services.internal.mock.MockRequest;

/**
 * @author Thomas.Darimont
 * 
 */
@Configurable
public class Client {
    IService /*<IRequest, IResponse>*/ service;

    /**
     * @return the service
     */
    public IService/*<IRequest, IResponse>*/ getService() {
        return service;
    }

    /**
     * @param service
     *            the service to set
     */
    @Inject
    public void setService(IService/*<IRequest, IResponse>*/ service) {
        System.out.println("setService");
        this.service = service;
    }

    public void start() {
        IRequest request = new MockRequest();
        request.setData("bubu");
        IResponse response = getService().process(request);
        System.out.println(response.getResult());
    }
}

Unsere @Configurable-Annotation:
Java:
/**
 * 
 */
package de.tutorials.configuration;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author Thomas.Darimont
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
public @interface Configurable{
}

Unser ConfigurationAspect:
Java:
/**
 * 
 */
package de.tutorials.configuration.aspects;

import de.tutorials.Main;
/**
 * @author Thomas.Darimont
 *
 */
public aspect Configuration {
    protected pointcut beanCreation(Object beanInstance) :
        initialization((@de.tutorials.configuration.Configurable *).new(..)) 
        && this(beanInstance) 
        && !cflow(call(* com.google.inject..*(..)));
    
    after(Object beanInstance) returning : beanCreation(beanInstance) {
      System.out.println("Configuring " + beanInstance.getClass() );
      Main.INJECTOR.injectMembers(beanInstance);
    }
}
die beanCreation Pointcutdefinition ist von Spring geklaut... außerdem ist es noch etwas unschön
den INJECTOR "global" als Klassenvariable verfügbar zu machen... na ja ist ja nur ein einfaches
Beispiel ;-)

Hier unsere Main:
Java:
/**
 * 
 */
package de.tutorials;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;

import de.tutorials.services.IService;

/**
 * @author Thomas.Darimont
 *
 */
public class Main {

  //Bootstrap Guice Runtime
  public final static Injector INJECTOR = Guice.createInjector(new MainModule()); 
  
    /**
     * @param args
     */
    public static void main(String[] args) {
        //Start Client
        INJECTOR.getInstance(Client.class).start();
        //new Client().start();
    }
}

Ausgabe:
Code:
setService
BUBU

Starten wir das ganze so:
Java:
/**
 * 
 */
package de.tutorials;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;

import de.tutorials.services.IService;

/**
 * @author Thomas.Darimont
 *
 */
public class Main {

  //Bootstrap Guice Runtime
  public final static Injector INJECTOR = Guice.createInjector(new MainModule()); 
  
    /**
     * @param args
     */
    public static void main(String[] args) {
        //Start Client
        //INJECTOR.getInstance(Client.class).start();
        new Client().start();
    }
}
So erhalten wir die Ausgabe:
Code:
Configuring class de.tutorials.Client
setService
BUBU

War ja richtig einfach :)

Um das Beispiel ausführen zu können braucht ihr die neuste Version von Guice und von AspectJ bzw. das AJDT...
Für Eclipse gibts dazu zusätzlich noch eine komplette Entwicklungsumgebung (AJDT AspectJ Development Tools
dazu: http://www.eclipse.org/ajdt/)

Gruß Tom
 

Anhänge

  • de.tutorials.training.guice-with-aspectj.zip
    15 KB · Aufrufe: 48
Zurück