С выпуском Apache CXF 3.0 пару недель назад, проект делает еще один важный шаг для выполнения требований спецификации JAX-RS 2.0: интеграция с CDI 1.1 . В этом сообщении мы рассмотрим несколько примеров совместной работы Apache CXF 3.0 и Apache CXF 3.0 .
Начиная с версии 3.0 , Apache CXF включает новый модуль с именем cxf -gration-cdi, который можно легко добавить в POM-файл Apache Maven :
1
2
3
4
5
|
< dependency > < groupId >org.apache.cxf</ groupId > < artifactId >cxf-integration-cdi</ artifactId > < version >3.0.0</ version > </ dependency > |
Этот новый модуль содержит всего два компонента (на самом деле, немного больше, но это ключевые):
- CXFCdiServlet : сервлет для загрузки приложения Apache CXF , служащий той же цели, что и CXFServlet и CXFNonSpringJaxrsServlet ,…
- JAXRSCdiResourceExtension : портативное расширение CDI 1.1, где происходит вся магия
При запуске в среде с поддержкой CDI 1.1 переносимые расширения обнаруживаются контейнером CDI 1.1 и инициализируются с использованием событий жизненного цикла. И это буквально все, что вам нужно! Давайте посмотрим на настоящее приложение в действии.
Мы собираемся создать очень простое приложение JAX-RS 2.0 для управления людьми, используя Apache CXF 3.0 и JBoss Weld 2.1 , эталонную реализацию CDI 1.1 . Класс Person, который мы собираемся использовать для представления лица, является простым Java-бином:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
package com.example.model; public class Person { private String email; private String firstName; private String lastName; public Person() { } public Person( final String email, final String firstName, final String lastName ) { this .email = email; this .firstName = firstName; this .lastName = lastName; } // Getters and setters are ommited // ... } |
Как это сейчас довольно часто, мы собираемся запустить наше приложение во встроенном контейнере Jetty 9.1, и наш класс Starter делает именно это:
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
|
package com.example; import org.apache.cxf.cdi.CXFCdiServlet; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.jboss.weld.environment.servlet.BeanManagerResourceBindingListener; import org.jboss.weld.environment.servlet.Listener; public class Starter { public static void main( final String[] args ) throws Exception { final Server server = new Server( 8080 ); // Register and map the dispatcher servlet final ServletHolder servletHolder = new ServletHolder( new CXFCdiServlet() ); final ServletContextHandler context = new ServletContextHandler(); context.setContextPath( "/" ); context.addEventListener( new Listener() ); context.addEventListener( new BeanManagerResourceBindingListener() ); context.addServlet( servletHolder, "/rest/*" ); server.setHandler( context ); server.start(); server.join(); } } |
Обратите внимание на наличие CXFCdiServlet и двух обязательных прослушивателей, которые были добавлены в контекст:
- org.jboss.weld.environment.servlet.Listener отвечает за инъекции CDI
- org.jboss.weld.environment.servlet.BeanManagerResourceBindingListener привязывает ссылку на BeanManager к расположению JNDI java: comp / env / BeanManager, чтобы сделать его доступным из приложения.
При этом вся мощь CDI 1.1 в вашем распоряжении. Давайте представим класс PeopleService с аннотацией @Named и с методом инициализации, объявленным и аннотированным с помощью @PostConstruct, только для создания одного человека.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Named public class PeopleService { private final ConcurrentMap< String, Person > persons = new ConcurrentHashMap< String, Person >(); @PostConstruct public void init() { } // Additional methods // ... } |
До сих пор мы ничего не говорили о настройке приложений и ресурсов JAX-RS 2.0 в среде CDI 1.1 . Причина этого очень проста: в зависимости от приложения вы можете использовать конфигурацию с минимальными усилиями или полностью настраиваемую. Давайте рассмотрим оба подхода.
При конфигурации без усилий вы можете определить пустое приложение JAX-RS 2.0 и любое количество ресурсов JAX-RS 2.0: Apache CXF 3.0 неявно свяжет их вместе, связав каждый класс ресурсов с этим приложением. Вот пример приложения JAX-RS 2.0:
1
2
3
4
5
6
7
8
|
package com.example.rs; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath ( "api" ) public class JaxRsApiApplication extends Application { } |
А вот ресурс PeopleRestService JAX-RS 2.0, который внедряет управляемый компонент PeopleService :
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
45
46
47
48
49
50
51
52
|
package com.example.rs; import java.util.Collection; import javax.inject.Inject; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import com.example.model.Person; import com.example.services.PeopleService; @Path ( "/people" ) public class PeopleRestService { @Inject private PeopleService peopleService; @Produces ( { MediaType.APPLICATION_JSON } ) @GET public Collection< Person > getPeople( @QueryParam ( "page" ) @DefaultValue ( "1" ) final int page ) { // ... } @Produces ( { MediaType.APPLICATION_JSON } ) @Path ( "/{email}" ) @GET public Person getPerson( @PathParam ( "email" ) final String email ) { // ... } @Produces ( { MediaType.APPLICATION_JSON } ) @POST public Response addPerson( @Context final UriInfo uriInfo, @FormParam ( "email" ) final String email, @FormParam ( "firstName" ) final String firstName, @FormParam ( "lastName" ) final String lastName ) { // ... } // More HTTP methods here // ... } |
Больше ничего не требуется: приложение Apache CXF 3.0 может запускаться таким образом и быть полностью функциональным. Полный исходный код примера проекта доступен на GitHub . Помните, что если вы придерживаетесь этого стиля, должно быть объявлено только одно пустое приложение JAX-RS 2.0.
При настраиваемом подходе доступно больше опций, но нужно сделать немного больше. Каждое приложение JAX-RS 2.0 должно предоставлять непустую реализацию коллекций getClasses () или / и getSingletons () . Однако классы ресурсов JAX-RS 2.0 остаются неизменными. Вот пример (который в основном приводит к той же конфигурации приложения, которую мы видели ранее):
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
|
package com.example.rs; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import javax.enterprise.inject.Produces; import javax.inject.Inject; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; @ApplicationPath ( "api" ) public class JaxRsApiApplication extends Application { @Inject private PeopleRestService peopleRestService; @Produces private JacksonJsonProvider jacksonJsonProvider = new JacksonJsonProvider(); @Override public Set< Object > getSingletons() { return new HashSet<>( Arrays.asList( peopleRestService, jacksonJsonProvider ) ); } } |
Обратите внимание, что расширение JAXRSCdiResourceExtension portable CDI 1.1 автоматически создает управляемые bean-компоненты для каждого приложения JAX-RS 2.0 (те, которые расширяют Application ) и ресурсы ( помеченные @Path ). Как таковые, они сразу же доступны для внедрения (как, например, PeopleRestService во фрагменте выше). Класс JacksonJsonProvider помечен аннотацией @Provider и как таковой будет рассматриваться как поставщик JAX-RS 2.0. Для приложений JAX-RS 2.0 нет ограничений, которые могут быть определены таким образом. Полный исходный код примера проекта с использованием этого appoarch доступен на GitHub .
Независимо от того, какой подход вы выбрали, наше приложение будет работать одинаково. Давайте построим и запустим:
1
2
|
> mvn clean package > java -jar target/jax-rs- 2.0 -cdi- 0.0 . 1 -SNAPSHOT.jar |
Вызов пары реализованных API REST подтверждает, что приложение функционирует и настроено правильно. Давайте выполним команду GET, чтобы гарантировать, что метод PeopleService, аннотированный @PostConstruct , был вызван при создании управляемого компонента.
1
2
3
4
5
6
7
8
9
|
> curl http: //localhost:8080/rest/api/people HTTP/ 1.1 200 OK Content-Type: application/json Date: Thu, 29 May 2014 22 : 39 : 35 GMT Transfer-Encoding: chunked Server: Jetty( 9.1 .z-SNAPSHOT) |
А вот пример команды POST :
01
02
03
04
05
06
07
08
09
10
|
> curl -i http: //localhost:8080/rest/api/people -X POST -d "[email protected]&firstName=Tom&lastName=Knocker" HTTP/ 1.1 201 Created Content-Type: application/json Date: Thu, 29 May 2014 22 : 40 : 08 GMT Location: http: //localhost:8080/rest/api/people/[email protected] Transfer-Encoding: chunked Server: Jetty( 9.1 .z-SNAPSHOT) |
В этом посте мы только что коснулись того, что теперь возможно с интеграцией Apache CXF и CDI 1.1 . Напомним, что встроенные Apache Tomcat 7.x / 8.x, а также развертывание Apache CXF на основе WAR с CDI 1.1 возможны на большинстве серверов приложений JEE и контейнеров сервлетов.
Пожалуйста, ознакомьтесь с официальной документацией и попробуйте!
- Полный исходный код доступен на GitHub .
Ссылка: | Apache CXF 3.0: поддержка CDI 1.1 в качестве альтернативы Spring от нашего партнера по JCG Андрея Редько в блоге Андрея Редько (devmind) . |