Нам нужно сделать несколько веб-проектов с большим количеством общих функций. Для этого неплохо было бы использовать систему плагинов (в качестве альтернативы копированию). Некоторые фреймворки (например, Grails) имеют возможность создавать веб-плагины, но большинство этого не делают, поэтому необходимо реализовать что-то на заказ.
Во-первых, давайте определим, какова требуемая функциональность. «Плагин»:
- должны быть включены просто путем импорта через Maven / Ivy
- следует зарегистрировать все классы (либо автоматически, либо через однострочную конфигурацию) в контейнере внедрения зависимостей, если он используется
- должен быть вертикальным — т.е. содержать все файлы, от javascript, css и шаблонов, через контроллеры, до классов сервисного уровня
- не требует сложной конфигурации, которую нужно копировать из проекта в проект
- должно позволять легкую разработку и отладку без перераспределения
Классы java помещаются в файл jar и добавляются в каталог lib, следовательно, в путь к классам, так что это простая часть. Но нам нужно извлечь веб-ресурсы в соответствующие места, где они могут быть использованы остальной частью кода. Существует три основных подхода к этому: извлечение во время сборки, извлечение во время выполнения и загрузка во время выполнения из пути к классам.
Последний подход потребует контроллера (или сервлета), который загружает ресурсы из пути к классам (соответствующий jar), кэширует их и обслуживает их. Это имеет пару существенных недостатков, один из которых заключается в том, что, находясь в банке, их нельзя легко заменить во время разработки. Работать с ресурсами classpath также сложно, так как вы заранее не знаете имен файлов.
Два других подхода очень похожи. Например, Grails использует извлечение во время сборки — плагин представляет собой zip-файл, содержащий все необходимые ресурсы, и они извлекаются в соответствующие местоположения во время сборки проекта. Это хорошо, но для этого потребуется немного больше настроек (в нашем случае maven), которые также, вероятно, придется копировать из проекта в проект.
Поэтому мы выбрали подход извлечения во время выполнения. Это происходит при запуске — при загрузке приложения какой-либо слушатель запуска (в нашем случае это пружинные компоненты с @PostConstruct) перебирает все файлы jar в папке lib и извлекает файлы из определенной папки (например, «web»). «). Итак, структура файла JAR выглядит следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
com company pkg Foo.class Bar.classweb plugin-name css main.css js foo.js bar.js images logo.png views foo.jsp bar.jsp |
Конечным результатом является то, что после запуска приложения вы получаете все необходимые веб-ресурсы, доступные из приложения, поэтому вы можете включить их в страницы (представления) вашего основного приложения.
И код, который выполняет извлечение, довольно прост (используя zip4j для части zip). Это может быть слушатель контекста сервлета, а не пружинный компонент — это не имеет значения.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
/** * Component that locates modules (in the form of jar files) and extracts their web elements, if any, on startup * * @author Bozhidar */@Componentpublic class ModuleExtractor { private static final Logger logger = LoggerFactory.getLogger(ModuleExtractor.class); @Inject private ServletContext ctx; @SuppressWarnings("unchecked") @PostConstruct public void init() { File lib = new File(ctx.getRealPath("/WEB-INF/lib")); File[] jars = lib.listFiles(); String targetPath = ctx.getRealPath("/"); String viewPath = "/WEB-INF/views"; //that can be made configurable for (File jar : jars) { try { ZipFile file = new ZipFile(jar); for (FileHeader header : (List<FileHeader>) file.getFileHeaders()) { if (header.getFileName().startsWith("web/") && !fileExists(header)) { // extract views in WEB-INF (inaccessible to the outside world) // all other files are extracted in the root of the application if (header.getFileName().contains("/views/")) { file.extractFile(header, targetPath + viewPath); } else { file.extractFile(header, targetPath); } } } } catch (ZipException ex) { logger.warn("Error opening jar file and looking for a web-module in: " + jar, ex); } } } private boolean fileExists(FileHeader header) { return new File(ctx.getRealPath(header.getFileName())).exists(); }} |
Итак, чтобы создать плагин, вы просто создаете проект maven с jar-упаковкой и добавляете его в качестве зависимости к основному проекту, обо всем остальном позаботитесь. Вам может потребоваться зарегистрировать ModuleExtractor если сканирование пути к классам для bean-компонентов не включено (или вы решили сделать его слушателем), но это все.
Примечание: это решение не стремится быть полнофункциональной системой плагинов, которая решает все проблемы. Он не поддерживает версионирование, подмодули и т. Д. Поэтому название «просто». Но с этим можно многое сделать, и это имеет очень низкую сложность.