Прошёл почти год с тех пор, как релиз Java 9 наконец доставил Project Jigsaw в массы Это был долгий, долгий путь, но он есть, так что же изменилось? Это очень хороший вопрос, и ответ на него не очевиден и однозначен.
В общем и целом, Project Jigsaw — это разрушительное изменение, и есть много причин, почему. Хотя в основном все наши существующие приложения будут работать на Java 10 (скоро будет заменен на JDK 11 ) с минимальными изменениями или без изменений, у Project Jigsaw есть глубокие и глубокие последствия для разработчиков Java: использование модульных приложений Java Платформа способ.
С множеством потрясающих фреймворков и библиотек, несомненно, потребуется время, много времени, чтобы преобразовать их в модули Java (многие даже не смогут это сделать). Этот путь тернист, но есть определенные вещи, которые уже возможны даже сегодня. В этом довольно коротком посте мы узнаем, как использовать потрясающий проект Apache CXF для создания по — настоящему модульных веб-API JAX-RS 2.1 с использованием последней версии JDK 10 .
Начиная с версии 3.2.5 , все артефакты Apache CXF имеют свои манифесты, обогащенные директивой Automatic-Module-Name . Это не делает их полноценными модулями , но это первый шаг в правильном направлении. Итак, давайте начнем …
Если вы используете Apache Maven в качестве инструмента сборки, который здесь не сильно изменился, зависимости объявляются так же, как и раньше.
|
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
|
<dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxrs</artifactId> <version>3.2.5</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-json-provider</artifactId> <version>2.9.6</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> <version>9.4.11.v20180605</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-webapp</artifactId> <version>9.4.11.v20180605</version> </dependency></dependencies> |
Упаковка uber-jar или fat-jar на самом деле не применима к модульным Java-приложениям, поэтому мы должны собирать модули сами, например, в папке target / modules .
|
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
|
<plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.1.0</version> <configuration> <outputDirectory>${project.build.directory}/modules</outputDirectory> </configuration></plugin><plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.1.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/modules</outputDirectory> <includeScope>runtime</includeScope> </configuration> </execution> </executions></plugin> |
Хорошо, следующий шаг — создать module-info.java и указать там имя нашего модуля (в нашем случае com.example.cxf ) и, среди прочего , все необходимые модули, необходимые для его работоспособности.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
module com.example.cxf { exports com.example.rest; requires org.apache.cxf.frontend.jaxrs; requires org.apache.cxf.transport.http; requires com.fasterxml.jackson.jaxrs.json; requires transitive java.ws.rs; requires javax.servlet.api; requires jetty.server; requires jetty.servlet; requires jetty.util; requires java.xml.bind;} |
Как вы можете сразу заметить, org.apache.cxf.frontend.jaxrs и org.apache.cxf.transport.http приходят из дистрибутива Apache CXF ( полный список доступен в документации ), тогда как java.ws.rs — это JAX -RS 2.1 API модуль. После этого мы можем приступить к реализации наших ресурсов JAX-RS так же, как раньше.
|
1
2
3
4
5
6
7
8
|
@Path("/api/people")public class PeopleRestService { @GET @Produces(MediaType.APPLICATION_JSON) public Collection<Person> getAll() { return List.of(new Person("John", "Smith", "john.smith@somewhere.com")); }} |
Это выглядит просто, как насчет добавления некоторого острого соуса, такого как, например, отправленные сервером события ( SSE ) и RxJava ? Давайте посмотрим, насколько это просто, начиная с зависимостей.
|
01
02
03
04
05
06
07
08
09
10
11
|
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-rs-sse</artifactId> <version>3.2.5</version></dependency><dependency> <groupId>io.reactivex.rxjava2</groupId> <artifactId>rxjava</artifactId> <version>2.1.14</version></dependency> |
Кроме того, мы не должны забывать обновить наш module-info.java , добавив директиву require к этим новым модулям.
|
1
2
3
4
5
6
7
8
|
module com.example.cxf { ... requires org.apache.cxf.rs.sse; requires io.reactivex.rxjava2; requires transitive org.reactivestreams; ...} |
Для простоты наша конечная точка SSE будет транслировать каждого нового человека, добавленного через API. Вот фрагмент реализации, который делает это.
|
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
|
private SseBroadcaster broadcaster;private Builder builder;private PublishSubject<Person> publisher; public PeopleRestService() { publisher = PublishSubject.create();}@Contextpublic void setSse(Sse sse) { this.broadcaster = sse.newBroadcaster(); this.builder = sse.newEventBuilder(); publisher .subscribeOn(Schedulers.single()) .map(person -> createEvent(builder, person)) .subscribe(broadcaster::broadcast);}@POST@Produces(MediaType.APPLICATION_JSON)@Consumes(MediaType.APPLICATION_JSON)public Response add(@Context UriInfo uriInfo, Person payload) { publisher.onNext(payload); return Response .created( uriInfo .getRequestUriBuilder() .path(payload.getEmail()) .build()) .entity(payload) .build();} @GET@Path("/sse")@Produces(MediaType.SERVER_SENT_EVENTS)public void people(@Context SseEventSink sink) { broadcaster.register(sink);} |
Теперь, когда мы его построим:
|
1
|
mvn clean package |
И запустите его, используя путь к модулю:
|
1
2
3
|
java --add-modules java.xml.bind \ --module-path target/modules \ --module com.example.cxf/com.example.Starter |
Мы должны быть в состоянии дать нашему JAX-RS API тест-драйв. Самый простой способ убедиться, что все работает как положено, — это перейти в Google Chrome к конечной точке SSE http: // localhost: 8686 / api / people / sse и добавить несколько случайных людей через запросы POST , используя старый скручиваемость командная строка:
|
1
2
3
|
curl -X POST http://localhost:8686/api/people \ -d '{"email": "john@smith.com", "firstName": "John", "lastName": "Smith"}' \ -H "Content-Type: application/json" |
|
1
2
3
|
curl -X POST http://localhost:8686/api/people \ -d '{"email": "tom@tommyknocker.com", "firstName": "Tom", "lastName": "Tommyknocker"}' \ -H "Content-Type: application/json" |
В Google Chrome мы должны видеть необработанные события SSE , выдвигаемые сервером (они выглядят не очень красиво, но достаточно хорошо, чтобы проиллюстрировать ход).
Итак, что насчет упаковки приложений? Docker и контейнеры, безусловно, являются жизнеспособным вариантом, но с Java 9 и выше у нас есть другой игрок: jlink . Он собирает и оптимизирует набор модулей и их зависимостей в пользовательский, вполне достаточный образ времени выполнения. Давайте попробуем это.
|
1
2
3
4
5
6
7
8
|
jlink --add-modules java.xml.bind,java.management \ --module-path target/modules \ --verbose \ --strip-debug \ --compress 2 \ --no-header-files \ --no-man-pages \ --output target/cxf-java-10-app |
Здесь мы попадаем в первую стену. К сожалению, поскольку в основном все зависимости нашего приложения являются автоматическими модулями , это проблема для jlink, и мы все равно должны явно указывать путь к модулю при запуске из образа времени выполнения:
|
1
2
3
4
|
target/cxf-java-10-app/bin/java \ --add-modules java.xml.bind \ --module-path target/modules \ --module com.example.cxf/com.example.Starter |
В конце дня все оказалось не так страшно. Мы, безусловно, находимся на самой ранней стадии принятия JPMS , это только начало. Когда каждая библиотека, каждый используемый фреймворк добавляет module-info.java к своим артефактам (JAR), делая их настоящими модулями, несмотря на все причуды , тогда мы можем объявить победу. Но маленькие победы уже происходят, сделайте их своими!
Полный исходный код проекта доступен на Github .
| Опубликовано на Java Code Geeks с разрешения Андрея Редько, партнера нашей программы JCG . См. Оригинальную статью здесь: Использование модульной платформы Java: Apache CXF на Java 10
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |
