2011-10-14 40 views
11

Tôi có ứng dụng web chạy với một impl mặc định của một dịch vụ phụ trợ. Người ta có thể thực hiện giao diện và thả jar vào thư mục plugins (không có trong classpath ứng dụng). Khi máy chủ được khởi động lại, ý tưởng là nạp jar mới vào trình nạp lớp, và có nó tham gia vào việc tiêm phụ thuộc. Tôi đang sử dụng Spring DI bằng cách sử dụng @Autowired. Plugin dịch vụ plugin mới sẽ có chú thích @Primary. Vì vậy, cho hai impls của giao diện, chính nên được nạp.Injection Dependency Spring và Plugin Jar

Tôi đã tải jar vào bộ nạp lớp và có thể gọi impl theo cách thủ công. Nhưng tôi đã không thể tham gia vào Dependency Injection, và có nó thay thế cho impl mặc định.

Dưới đây là một ví dụ đơn giản:

@Controller 
public class MyController { 
    @Autowired 
    Service service; 
} 

//default.jar 
@Service 
DefaultService implements Service { 
    public void print() { 
     System.out.println("printing DefaultService.print()"); 
    } 
} 

//plugin.jar not in classpath yet 
@Service 
@Primary 
MyNewService implements Service { 
    public void print() { 
     System.out.println("printing MyNewService.print()"); 
    } 
} 

// Đối với thiếu nơi tốt đẹp hơn, tôi nạp jar Plugin từ ContextListener

public class PluginContextLoaderListener extends org.springframework.web.context.ContextLoaderListener { 

     @Override 
     protected void customizeContext(ServletContext servletContext, 
             ConfigurableWebApplicationContext wac) { 
       System.out.println("Init Plugin"); 
       PluginManager pluginManager = PluginManagerFactory.createPluginManager("plugins"); 
       pluginManager.init(); 

        //Prints the MyNewService.print() method 
        Service service = (Service) pluginManager.getService("service"); 
        service.print();     
      } 
    } 

    <listener> 
      <listener-class>com.plugin.PluginContextLoaderListener</listener-class> 
    </listener> 

Ngay cả sau khi tôi đã tải jar vào classloader, DefaultService vẫn đang được tiêm dưới dạng dịch vụ. Bất kỳ ý tưởng làm thế nào tôi nhận được các plugin cắm để tham gia vào vòng đời DI của mùa xuân?

Đã chỉnh sửa: Để đơn giản, tôi có tệp chiến tranh có một vài lọ cắm trong thư mục plugin bên trong chiến tranh. Dựa trên một giá trị từ một tệp cấu hình mà ứng dụng xem xét, khi ứng dụng được bắt đầu, tôi muốn tải jar plugin cụ thể đó và chạy ứng dụng với nó. Bằng cách đó, tôi có thể phân phối chiến tranh cho bất kỳ ai và họ có thể chọn plugin để chạy dựa trên giá trị cấu hình mà không phải đóng gói lại mọi thứ. Đây là vấn đề tôi đang cố giải quyết.

Trả lời

7

Dường như tất cả những gì bạn cần là tạo ra Spring ApplicationContext đúng cách. Tôi nghĩ rằng có thể mà không cần trộn đường dẫn lớp. Điều quan trọng nhất là vị trí của các tệp cấu hình Spring trong vòng đường dẫn lớp. Vì vậy, đặt tất cả các plugin của bạn jar vào WEB-INF/lib và đọc tiếp.

Hãy bắt đầu với mô-đun lõi. Chúng tôi sẽ tạo nó để tạo ra nó là ApplicationContext từ các tệp có tại classpath*:META-INF/spring/*-corecontext.xml.

Bây giờ, chúng tôi sẽ tạo tất cả các plugin để có tệp cấu hình ở nơi khác. I E. 'myplugin1' sẽ có vị trí cấu hình như sau: classpath*:META-INF/spring/*-myplugin1context.xml. Và anotherplugin sẽ có các cấu hình tại classpath*:META-INF/spring/*-anotherplugincontext.xml.

Những gì bạn thấy là một convension.Bạn cũng có thể sử dụng subdirectiries nếu bạn thích:

  • lõi: classpath*:META-INF/spring/core/*.xml
  • myplugin1: classpath*:META-INF/spring/myplugin1/*.xml
  • anotherplugin: classpath*:META-INF/spring/anotherplugin/*.xml

Điều quan trọng là các địa điểm phải rời nhau.

Tất cả những gì còn lại là chuyển đúng vị trí cho người sáng tạo ApplicationContext. Đối với các ứng dụng web, nơi thích hợp cho việc này sẽ là mở rộng ContextLoaderListener và ghi đè phương thức customizeContext(ServletContext, ConfigurableWebApplicationContext).

Tất cả những gì còn lại là đọc tệp cấu hình của bạn (vị trí của nó có thể được chuyển thành tham số init servlet). Than Bạn cần phải xây dựng danh sách các vị trí cấu hình:

String locationPrefix = "classpath*:META-INF/spring/"; 
String locationSiffix = "/*.xml"; 

List<String> configLocations = new ArrayList<String>(); 
configLocations.add(locationPrefix + "core" + locationSiffix); 

List<String> pluginsTurnedOn = getPluginsTurnedOnFromConfiguration(); 
for (String pluginName : pluginsTurnedOn) { 
    configLocations.add(locationPrefix + pluginName + locationSiffix); 
} 

applicationContext.setConfigLocations(configLocations.toArray(new String[configLocations.size()])); 

Bằng cách này Bạn có thể dễ dàng quản lý những gì và những gì không được nạp vào Spring ApplicationContext.

Cập nhật:

Để làm cho nó làm việc có thêm một giả định ẩn tôi đưa ra là tôi sắp giải thích bây giờ. Gói cơ sở cơ sở của mô-đun lõi và mỗi plugin cũng phải là tách rời. Đó là ví dụ:

  • com.mycompany.myapp.core
  • com.mycompany.myapp.myplugin1
  • com.mycompany.myapp.anotherplugin

Bằng cách này mỗi module có thể sử dụng <context:componet-scan /> (tương đương trong JavaConfig) dễ dàng để thêm quét đường dẫn lớp cho đó là các lớp riêng chỉ. Mô-đun lõi không được chứa mọi gói quét của bất kỳ gói plugin nào. Các plugin sẽ mở rộng cấu hình của ApplicationContext để thêm các gói của riêng họ vào quét đường dẫn lớp.

+0

Cảm ơn. Có vẻ như điều đó sẽ hiệu quả. Nhưng tôi đang sử dụng chú thích mùa xuân rất nhiều, và không có bất kỳ xml mùa xuân nào trong các lọ cắm. Tôi tự hỏi làm thế nào nó sẽ dễ dàng để làm tương tự như trên cho tiêm phụ thuộc chú thích theo định hướng. – Langali

+0

Bạn nên có một số loại cấu hình trong mỗi plugin của bạn (@xem câu trả lời mở rộng của tôi). Thậm chí nếu nó chỉ là một xml nhỏ chỉ với một phần tử '' của gói plugin, nó sẽ là ** trong plugin ** và không có nơi nào khác. Đây là plugin tự biết rõ nhất và biết cách cấu hình chính nó. Mô-đun cốt lõi không nên biết về sự tồn tại của plugin - nó chỉ nên cung cấp cho chúng khả năng tham gia các cấu hình của riêng chúng tới 'ApplicationContext'. – Roadrunner

2

Nếu bạn khởi động lại máy chủ, tôi không thấy lý do tại sao bạn không thể thêm JAR vào WEB-INF/lib và có nó trong CLASSPATH. Tất cả các biến chứng của trình nạp lớp tùy chỉnh và trình lắng nghe ngữ cảnh biến mất, bởi vì bạn đối xử với nó giống như bất kỳ lớp nào khác dưới sự kiểm soát của Spring.

Nếu bạn làm theo cách này bởi vì bạn không muốn mở hoặc sửa đổi một WAR, tại sao không đặt nó trong thư mục máy chủ/lib? Hãy để trình nạp lớp máy chủ chọn nó. Điều này làm cho tất cả các lớp plugin có sẵn cho tất cả các ứng dụng được triển khai.

Câu trả lời tùy thuộc vào mức độ quan trọng của thư mục riêng biệt/plugin. Nếu đó là chìa khóa để giải pháp, và bạn không thể thêm JAR vào thư mục/lib của máy chủ, thì đó là điều đó. Tôi không có gì cả. Nhưng tôi nghĩ rằng nó sẽ là đáng giá để ít nhất là xem lại các giải pháp bạn phải chắc chắn rằng đó là cách duy nhất để thực hiện những gì bạn muốn.

+0

Điều đó luôn là tùy chọn. Nhưng ý tưởng là để có một số bổ sung được chứng nhận trong chiến tranh (nhưng không phải trong classpath), và người dùng có thể tải một plugin hoặc khác dựa trên một giá trị cấu hình bên ngoài ứng dụng. Bằng cách đó, người dùng trung bình sẽ chỉ phải quan tâm đến thuộc tính cấu hình chứ không phải nội bộ của hệ thống plugin. – Langali

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