2010-11-21 32 views
41

Tôi muốn có thể tiêm thực thi chung một giao diện chung bằng cách sử dụng Guice.Tiêm thực hiện chung bằng cách sử dụng Guice

public interface Repository<T> { 
    void save(T item); 
    T get(int id); 
} 

public MyRepository<T> implements Repository<T> { 
    @Override 
    public void save(T item) { 
    // do saving 
    return item; 
    } 
    @Override 
    public T get(int id) { 
    // get item and return 
    } 
} 

Trong C# sử dụng Castle.Windsor, tôi muốn có thể to do:

Component.For(typeof(Repository<>)).ImplementedBy(typeof(MyRepository<>)) 

nhưng tôi không nghĩ là tương đương tồn tại trong Guice. Tôi biết tôi có thể sử dụng TypeLiteral trong Guice để đăng ký triển khai cá nhân, nhưng có cách nào để đăng ký tất cả cùng một lúc như trong Windsor?

Edit:

Dưới đây là một ví dụ về việc sử dụng:

Injector injector = Guice.createInjector(new MyModule()); 
Repository<Class1> repo1 = injector.getInstance(new Key<Repository<Class1>>() {}); 
Repository<Class2> repo2 = injector.getInstance(new Key<Repository<Class2>>() {}); 

Mặc dù việc sử dụng nhiều khả năng sẽ được tiêm vào lớp khác:

public class ClassThatUsesRepository { 
    private Repository<Class1> repository; 

    @Inject 
    public ClassThatUsesRepository(Repository<Class1> repository) { 
    this.repository = repository; 
    } 
} 
+0

Bạn có thể thêm một đoạn mã cho thấy làm thế nào bạn sẽ thích _use_ này? –

+2

Tôi ở bên bạn, tôi cũng muốn làm điều tương tự. Mọi người nên có vấn đề này. Phải có một cái gì đó họ không nói với chúng tôi. :) – PapaFreud

+0

Tôi muốn biết các giải pháp quá, tôi không biết gì về C#, nhưng rõ ràng cách C# là hiện đại hơn nhiều. – Mike

Trả lời

51

Để sử dụng Generics với Bạn cần sử dụng lớp TypeLiteral để liên kết các biến thể chung. Đây là một ví dụ về cách bạn Guice cấu hình injector có thể trông giống như:

package your-application.com; 

import com.google.inject.AbstractModule; 
import com.google.inject.TypeLiteral; 

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    bind(new TypeLiteral<Repository<Class1>>(){}) 
     .to(new TypeLiteral<MyRepository<Class1>>(){}); 
    } 
} 

(Repository là giao diện chung, MyRepository là việc thực hiện chung chung, Class1 là lớp học cụ thể được sử dụng trong generics).

+6

Đây là cách tôi đang làm. Những gì tôi đã hy vọng làm là để loại bỏ sự cần thiết phải đăng ký từng cá nhân thực hiện (MyRepository , MyRepository , vv). Đó là ví dụ của Windsor. –

+2

Xin lỗi về điều đó, tôi nên đọc kỹ câu hỏi của bạn hơn. Tôi đã nhìn vào loại Generics Generics đó, nhưng tôi cũng không thể giải quyết nó. Tôi đoán một cách để giải quyết nó sẽ là để mở rộng Guice và viết Module của riêng bạn (helper). Với việc sử dụng API phản chiếu Java, bạn có thể tìm thấy tất cả các biến thể Injection và liên kết chúng. – Kdeveloper

3

Các tác động không được giữ lại trong thời gian chạy chắc chắn khiến bạn khó nắm bắt khái niệm này lúc đầu. Dù sao, có lý do new ArrayList<String>().getClass() trả về Class<?> và không Class<String> và mặc dù an toàn để truyền nó đến Class<? extends String> bạn nên nhớ rằng generics chỉ có để kiểm tra loại biên dịch thời gian (loại giống như xác thực ngầm, nếu bạn muốn).

Vì vậy, nếu bạn muốn sử dụng Guice để tiêm MyRepository (với bất kỳ loại nào) triển khai bất cứ khi nào bạn cần một phiên bản mới của Repository (với bất kỳ loại nào) thì bạn không phải suy nghĩ về generics cả, nhưng bạn trên của riêng bạn để đảm bảo an toàn loại (đó là lý do tại sao bạn nhận được những cảnh báo "không được kiểm soát" pesky).

Dưới đây là một ví dụ về mã chỉ làm việc tốt:

public class GuiceTest extends AbstractModule { 

    @Inject 
    List collection; 

    public static void main(String[] args) { 
     GuiceTest app = new GuiceTest(); 
     app.test(); 
    } 

    public void test(){ 
     Injector injector = Guice.createInjector(new GuiceTest()); 
     injector.injectMembers(this); 

     List<String> strCollection = collection; 
     strCollection.add("I'm a String"); 
     System.out.println(collection.get(0)); 

     List<Integer> intCollection = collection; 
     intCollection.add(new Integer(33)); 
     System.out.println(collection.get(1)); 
    } 

    @Override 
    protected void configure() { 
     bind(List.class).to(LinkedList.class); 
    } 
} 

in này:

I'm a String 
33 

Nhưng danh sách đó thực hiện bởi một LinkedList. Mặc dù trong ví dụ này, nếu bạn cố gắng gán int thứ gì đó là Chuỗi bạn sẽ nhận được ngoại lệ.

int i = collection.get(0) 

Nhưng nếu bạn muốn để có được một đối tượng tiêm đã Type-đúc và Dandy bạn có thể yêu cầu List<String> thay vì chỉ List, nhưng sau đó Guice sẽ đối xử mà Loại biến như là một phần của khóa ràng buộc (tương tự như một vòng loại như @Named).Điều này có nghĩa là nếu bạn muốn tiêm cụ thể List<String> để được thực hiện ArrayList<String>List<Integer>LinkedList<Integer>, Guice cho phép bạn thực hiện điều đó (không được thử nghiệm, được giáo dục đoán).

Nhưng có một nhược điểm:

@Override 
    protected void configure() { 
     bind(List<String>.class).to(LinkedList<String>.class); <-- *Not Happening* 
    } 

Như bạn có thể nhận thấy literals lớp không chung. Đó là nơi bạn sử dụng số TypeLiterals của Guice.

@Override 
    protected void configure() { 
     bind(new TypeLiteral<List<String>>(){}).to(new TypeLiteral<LinkedList<String>>(){}); 
    } 

TypeLiterals giữ lại các biến kiểu generic như một phần của meta-thông tin để lập bản đồ để thực hiện mong muốn. Hi vọng điêu nay co ich.

0

Bạn có thể sử dụng (lạm dụng?) Các @ImplementedBy chú thích để làm Guice tạo ra các ràng buộc chung cho bạn:

@ImplementedBy(MyRepository.class) 
interface Repository<T> { ... } 

class MyRepository<T> implements Repository<T> { ... } 

Chừng nào just-in-time bindings được bật, bạn có thể tiêm Repository<Whatever> mà không cần bất kỳ rõ ràng ràng buộc :

Injector injector = Guice.createInjector(); 
    System.out.println(injector.getBinding(new Key<Repository<String>>(){})); 
    System.out.println(injector.getBinding(new Key<Repository<Integer>>(){})); 

việc nắm bắt được rằng mục tiêu của các ràng buộc là MyRepository, chứ không phải là MyRepository<T>:

LinkedKeyBinding{key=Key[type=Repository<java.lang.String>, annotation=[none]], source=interface Repository, scope=Scopes.NO_SCOPE, target=Key[type=MyRepository, annotation=[none]]} 
LinkedKeyBinding{key=Key[type=Repository<java.lang.Integer>, annotation=[none]], source=interface Repository, scope=Scopes.NO_SCOPE, target=Key[type=MyRepository, annotation=[none]]} 

Đó thường không phải là vấn đề, nhưng có nghĩa là MyRepository không thể tiêm TypeLiteral<T> để tìm ra loại riêng của nó khi chạy, điều này đặc biệt hữu ích trong trường hợp này. Bên cạnh đó, theo hiểu biết tốt nhất của tôi, điều này hoạt động tốt.

(Nếu ai đó cảm thấy như sửa chữa này, tôi khá chắc chắn rằng nó sẽ chỉ cần thêm một số tính toán around here để điền vào các thông số loại mục tiêu từ khóa nguồn.)

Các vấn đề liên quan