Spring Инъекция зависимостей и плагинов

У меня есть веб-приложение, работающее со стандартным импортом бэкэнд-сервиса. Нужно иметь возможность реализовать интерфейс и отбросить банку в папку плагинов (чего нет в пути к классам приложений). После перезапуска сервера идея состоит в том, чтобы загрузить новую банку в загрузчик классов и принять участие в инъекции зависимостей. Я использую Spring DI, используя @Autowired. Новый модуль плагина будет иметь @Primary аннотацию. Поэтому, учитывая два интерфейса интерфейса, первичный должен быть загружен.

Я получил флягу, загруженную в загрузчик классов, и могу вызвать ее вручную. Но я не смог принять участие в Injection Dependency и заменить его по умолчанию.

Здесь приведен упрощенный пример:

@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()");
   } 
}

//Из-за отсутствия лучшего места я загрузил плагин с помощью 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>

Даже после того, как я загрузил jar в загрузчик классов, DefaultService все еще вводится как служба. Любая идея, как я получаю банку плагина для участия в жизненном цикле Spring DI?

Отредактировано: Проще говоря, у меня есть файл войны, в котором есть несколько плагинов в каталоге плагинов внутри войны. Основываясь на значении из файла конфигурации, на которое приложение смотрит, когда приложение запущено, я хочу загрузить эту конкретную банку с плагином и запустить приложение с ней. Таким образом, я могу распределить войну кому угодно, и они могут выбрать, какой плагин запускаться на основе значения конфигурации, не переупаковывая все. Это проблема, которую я пытаюсь решить.

Ответ 1

Кажется, все, что вам нужно, - это правильно создать Spring ApplicationContext. Я думаю, что возможно без classpath mingling. Наиболее важными являются расположение конфигурационных файлов Spring внутри пути к классам. Поэтому поставьте все свои плагины в WEB-INF/lib и продолжайте читать.

Давайте начнем с основного модуля. Мы создадим его ApplicationContext из файлов, расположенных в classpath*:META-INF/spring/*-corecontext.xml.

Теперь мы сделаем все плагины для их конфигурационных файлов в другом месте. То есть "myplugin1" будет иметь конфигурационное расположение следующим образом: classpath*:META-INF/spring/*-myplugin1context.xml. И anotherplugin будет иметь конфиги в classpath*:META-INF/spring/*-anotherplugincontext.xml.

Что вы видите, это конвент. Вы также можете использовать подкаталоги, если хотите:

  • Ядро: classpath*:META-INF/spring/core/*.xml
  • myplugin1: classpath*:META-INF/spring/myplugin1/*.xml
  • anotherplugin: classpath*:META-INF/spring/anotherplugin/*.xml

Важно то, что местоположения должны быть непересекающимися.

Все, что остается, - передать правильные местоположения создателю ApplicationContext. Для веб-приложений правильным местом для этого было бы расширение ContextLoaderListener и переопределение метода customizeContext(ServletContext, ConfigurableWebApplicationContext).

Осталось только прочитать ваш конфигурационный файл (его местоположение можно передать как параметр init сервлета). Чем вам нужно создать список мест конфигурации:

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()]));

Таким образом, вы легко можете управлять тем, что есть и что не загружено в Spring ApplicationContext.

Update:

Чтобы заставить его работать там еще одно скрытое предположение, которое я сделал, что я сейчас объясню. Базовый пакет основного модуля и каждого плагина также должен быть непересекающимся. То есть, например:

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

Таким образом, каждый модуль может использовать <context:componet-scan /> (по эквиваленту в JavaConfig) легко добавить сканирование в classpath для только собственных классов. Основной модуль не должен содержать любое сканирование пакетов любых пакетов плагинов. Плагины должны расширить конфигурацию ApplicationContext, чтобы добавить свои собственные пакеты в сканирование классов.

Ответ 2

Если вы перезапустите сервер, я не вижу причин, по которым вы не можете просто добавить JAR в WEB-INF/lib и иметь его в CLASSPATH. Все усложнение пользовательского загрузчика классов и прослушивателя контекста уходит, потому что вы относитесь к нему так же, как и любой другой класс под управлением Spring.

Если вы сделаете это так, потому что вы не хотите открывать или изменять WAR, почему бы не поместить его в каталог server/lib? Позвольте загрузчику класса сервера поднять его. Это делает все классы плагинов доступными для всех развернутых приложений.

Ответ зависит от того, насколько важен отдельный каталог/плагин. Если это ключ к решению, и вы не можете добавить JAR в каталог server/lib, то это. У меня ничего нет. Но я думаю, что стоило бы хотя бы вернуться к решению, которое вы должны сделать, чтобы убедиться, что это единственный способ выполнить то, что вы хотите.