2013-07-10 39 views
11

Giả sử bạn có một giao diệnXuân Autowire Annotation với số Interface Triển khai

public interface A { 
    public void doSomething(); 
} 

và hai thi lớp

@Component(value="aImpl1") 
public class AImpl1 implements A { 

} 

@Component(value="aImpl2") 
public class AImpl2 implements A{ 

} 

Và cuối cùng là một lớp mà sẽ sử dụng một "A" thực hiện:

@Component 
public class MyClass { 
    @Autowire 
    A a; 
} 

Bây giờ nếu tôi muốn tiêm AImpl1 tôi thêm các @Qualifier ("aImpl1") trong khi nếu tôi muốn tiêm AImpl2 tôi thêm @Qualifier ("aImpl2")

Câu hỏi đặt ra là: Có thể hướng dẫn xuân bằng cách nào đó để tra cứu tất cả việc triển khai "A" trong trường hợp này là AImpl1AImpl2 và sử dụng một số quy ước cụ thể của ứng dụng để chọn triển khai phù hợp nhất? ví dụ trong trường hợp này, quy ước của tôi có thể sử dụng triển khai với hậu tố lớn nhất (tức là AImpl2)?

EDIT: lớp MyClass không nên biết gì về logic tra cứu triển khai, nó chỉ nên tìm thuộc tính "a" được đặt với đối tượng AImpl2.

+0

Bạn muốn giữ logic tra cứu ở đâu? Nếu trong tệp cấu hình ngữ cảnh XML, các bí danh có phải là một tùy chọn không? Còn về vòng loại tùy chỉnh thì sao? –

+0

Điều gì khiến bạn nghĩ rằng câu trả lời không được cập nhật? Có vấn đề với phiên bản Spring gần đây không? Hoặc nó không hoạt động với lớp Application Context cụ thể? –

+0

@AdrianBer logic nên có một số loại cấu hình cho Mùa xuân và được áp dụng cho tất cả các lần tra cứu. gargc đã trả lời những gì tôi cần. –

Trả lời

5

Giả sử bạn đã có hàng trăm giao diện và triển khai (như bạn đã nói trong nhận xét) và bạn không muốn cấu trúc lại tất cả mã ... thì đây là một vấn đề phức tạp:

Bạn có thể tạo tùy chỉnh BeanDefinitionRegistryPostProcessor và triển khai phương thức postProcessBeanDefinitionRegistry hoặc postProcessBeanFactory.

Bằng cách này, bạn có quyền truy cập vào tất cả các định nghĩa bean trước khi chúng được khởi tạo và được tiêm. Làm logic của bạn để tìm thấy việc triển khai được ưu tiên cho mỗi giao diện của bạn là gì và sau đó, thiết lập đó là chính.

@Component 
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { 

    @Override 
    public void postProcessBeanDefinitionRegistry(
      BeanDefinitionRegistry registry) throws BeansException { 

      // this method can be used to set a primary bean, although 
      // beans defined in a @Configuration class will not be avalable here. 

    } 

    @Override 
    public void postProcessBeanFactory(
      ConfigurableListableBeanFactory beanFactory) throws BeansException {  

     // here, all beans are available including those defined by @configuration, @component, xml, etc. 

     // do some magic to somehow find which is the preferred bean name for each interface 
     // you have access to all bean-definition names with: beanFactory.getBeanDefinitionNames() 
     String beanName = "aImpl2"; // let's say is this one 

     // get the definition for that bean and set it as primary 
     beanFactory.getBeanDefinition(beanName).setPrimary(true) 

    } 



} 

Phần khó khăn là tìm tên bean, nó phụ thuộc vào chi tiết cụ thể của đơn đăng ký của bạn. Tôi đoán rằng có một quy ước đặt tên nhất quán sẽ giúp ích.

Cập nhật:

Dường như cả hai phương pháp trong giao diện BeanDefinitionRegistryPostProcessor thể được sử dụng cho mục đích này. Lưu ý rằng trong pha postProcessBeanDefinitionRegistry, các bean được cấu hình thông qua các lớp @configuration chưa có sẵn, như đã lưu ý trong các bình luận bên dưới.

Mặt khác, chúng thực sự có sẵn trong postProcessBeanFactory.

+1

+1 Tôi đang nghĩ về điều gì đó dọc theo những dòng này. Chỉ cần nghĩ rằng tôi muốn thêm rằng phương pháp này sẽ không hoạt động cho các dự án thuần '' Cấu hình' - nghĩa là các dự án không sử dụng cấu hình xml. – Jonathan

+0

Chính xác những gì tôi đang tìm kiếm! –

+2

@ Jonathan, bạn nói đúng ... BeanDefinitionRegistry không chứa các bean được định nghĩa trong lớp @Configuration (mặc dù nó có chứa @Component, @Service, etc bean được chú thích). Nhưng chúng có sẵn trong phương thức khác: 'postProcessBeanFactory', thông qua' ConfigurableListableBeanFactory'. Tôi vừa làm một bài kiểm tra nhanh và nó hoạt động. Tôi sẽ thêm nó vào câu trả lời. – gargc

10

Bạn có thể tiêm tất cả implentations như List:

@Autowired 
List<A> as; 

hoặc như Map với tên đậu như chính:

@Autowired 
Map<String, A> as; 

và sau đó chọn thực hiện đúng bằng tay (có lẽ, trong một phương pháp setter):

@Autowired 
public void setAs(Map<String, A> as) { 
    this.a = ...; 
} 
+0

Cảm ơn câu trả lời nhưng tôi không muốn sửa đổi lớp cấu trúc MyClass. Nó phải có một và chỉ một tài sản (A a); –

2

Nếu bạn có Cấu hình lớp ion bạn có thể sử dụng một phương pháp trong đó để đưa ra quyết định thực hiện A để trả về. Sau đó, autowired sẽ tiêm cá thể thích hợp cho lớp đó.

@Configuration 
public class ApplicationConfiguration { 

    @Bean 
    A getA() { 
     // instantiate the implementation of A that you would like to have injected 
     // or you could use reflection to find the correct class from the classpath. 
     // return the instance 
    } 
} 

này giả định bạn luôn muốn sử dụng cùng một ví dụ ở khắp mọi nơi bạn đang tiêm A. Nếu không, sau đó bạn có thể có phương pháp chú thích @Bean khác nhau với những cái tên để có được phiên bản khác nhau.

+0

Có giả định là luôn sử dụng cùng một triển khai ở mọi nơi. Vấn đề là tôi có thể có hàng trăm giao diện như "A" và tôi không muốn tạo một phương thức cho mỗi giao diện. Vì vậy, những gì tôi thực sự cần là một cách để tùy chỉnh hành vi của chú thích @Autowire và hướng dẫn nó thực hiện logic này. –

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