Home » Учебник Jersey Java — создание RESTful веб-сервисов
Учебник Jersey Java — создание RESTful веб-сервисов

Учебник Jersey Java — создание RESTful веб-сервисов

Если ты разрабатываешь или планируешь разрабатывать RESTful веб-сервисы на Java, то Jersey Framework — это один из самых популярных и мощных инструментов для этой задачи. Это reference implementation стандарта JAX-RS (Java API for RESTful Web Services), который значительно упрощает создание REST API. В этой статье мы разберём Jersey от простого к сложному, покажем практические примеры настройки и деплоя, а также сравним с альтернативными решениями.

Что такое Jersey и как он работает

Jersey — это фреймворк для создания RESTful веб-сервисов на Java, который реализует спецификацию JAX-RS. Основная фишка в том, что он позволяет создавать REST API с минимальным количеством boilerplate кода, используя аннотации для определения endpoints, HTTP методов и параметров.

Архитектура Jersey построена на следующих принципах:

  • Annotation-based configuration — используем аннотации вместо XML-конфигов
  • Resource-oriented approach — каждый класс представляет ресурс с определённым URI
  • Automatic serialization/deserialization — автоматическое преобразование объектов в JSON/XML
  • Dependency injection — встроенная поддержка DI через HK2

Быстрый старт: настройка проекта пошагово

Давайте создадим простой REST API с нуля. Для начала нужен Maven проект с правильными зависимостями:

<dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
        <version>3.1.3</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet-core</artifactId>
        <version>3.1.3</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.inject</groupId>
        <artifactId>jersey-hk2</artifactId>
        <version>3.1.3</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>3.1.3</version>
    </dependency>
</dependencies>

Создаём простой ресурс:

import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;

@Path("/api/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
    
    private static Map<Integer, User> users = new HashMap<>();
    
    @GET
    public Response getAllUsers() {
        return Response.ok(users.values()).build();
    }
    
    @GET
    @Path("/{id}")
    public Response getUser(@PathParam("id") int id) {
        User user = users.get(id);
        if (user == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.ok(user).build();
    }
    
    @POST
    public Response createUser(User user) {
        users.put(user.getId(), user);
        return Response.status(Response.Status.CREATED).entity(user).build();
    }
    
    @PUT
    @Path("/{id}")
    public Response updateUser(@PathParam("id") int id, User user) {
        if (!users.containsKey(id)) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        user.setId(id);
        users.put(id, user);
        return Response.ok(user).build();
    }
    
    @DELETE
    @Path("/{id}")
    public Response deleteUser(@PathParam("id") int id) {
        if (users.remove(id) == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
        return Response.noContent().build();
    }
}

Простой POJO для User:

public class User {
    private int id;
    private String name;
    private String email;
    
    // Конструкторы, геттеры и сеттеры
    public User() {}
    
    public User(int id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
    
    // геттеры и сеттеры...
}

Теперь создаём Application класс:

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

@ApplicationPath("/")
public class RestApplication extends Application {
    // Jersey автоматически найдёт все ресурсы с аннотациями
}

Конфигурация web.xml:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    
    <servlet>
        <servlet-name>Jersey REST Service</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jakarta.ws.rs.Application</param-name>
            <param-value>com.example.RestApplication</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>Jersey REST Service</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

Практические примеры и кейсы

Давайте рассмотрим несколько практических сценариев использования Jersey:

Обработка исключений

import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;

@Provider
public class CustomExceptionMapper implements ExceptionMapper<Exception> {
    
    @Override
    public Response toResponse(Exception exception) {
        ErrorResponse error = new ErrorResponse(
            "INTERNAL_ERROR", 
            exception.getMessage()
        );
        return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                      .entity(error)
                      .build();
    }
}

Валидация данных

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Email;

public class User {
    @NotNull
    private String name;
    
    @Email
    private String email;
    
    // геттеры и сеттеры
}

// В ресурсе
@POST
public Response createUser(@Valid User user) {
    // Jersey автоматически валидирует объект
    return Response.ok(user).build();
}

Фильтры и перехватчики

import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.ext.Provider;

@Provider
public class AuthenticationFilter implements ContainerRequestFilter {
    
    @Override
    public void filter(ContainerRequestContext requestContext) {
        String authHeader = requestContext.getHeaderString("Authorization");
        
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            requestContext.abortWith(
                Response.status(Response.Status.UNAUTHORIZED).build()
            );
        }
    }
}

Сравнение с альтернативными решениями

Фреймворк Производительность Простота настройки Экосистема Размер JAR
Jersey Высокая Средняя Богатая ~15MB
Spring Boot Средняя Очень простая Огромная ~20MB
Quarkus Очень высокая Простая Растущая ~8MB
Micronaut Высокая Средняя Средняя ~10MB

Продвинутые возможности

Асинхронная обработка

import jakarta.ws.rs.container.AsyncResponse;
import jakarta.ws.rs.container.Suspended;
import java.util.concurrent.CompletableFuture;

@GET
@Path("/async")
public void getAsyncData(@Suspended AsyncResponse asyncResponse) {
    CompletableFuture
        .supplyAsync(() -> {
            // Долгая операция
            return heavyComputation();
        })
        .thenAccept(result -> asyncResponse.resume(result))
        .exceptionally(ex -> {
            asyncResponse.resume(Response.status(500).entity(ex.getMessage()).build());
            return null;
        });
}

Server-Sent Events (SSE)

import jakarta.ws.rs.sse.Sse;
import jakarta.ws.rs.sse.SseEventSink;
import jakarta.ws.rs.core.Context;

@GET
@Path("/events")
@Produces(MediaType.SERVER_SENT_EVENTS)
public void streamEvents(@Context SseEventSink eventSink, @Context Sse sse) {
    new Thread(() -> {
        try {
            for (int i = 0; i < 10; i++) {
                eventSink.send(sse.newEvent("Event " + i));
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            eventSink.close();
        }
    }).start();
}

Деплой на сервер

Для деплоя Jersey приложения на production сервер вам понадобится VPS сервер или выделенный сервер. Рассмотрим несколько вариантов:

Tomcat

# Собираем WAR файл
mvn clean package

# Копируем в Tomcat
sudo cp target/myapp.war /opt/tomcat/webapps/

# Перезапускаем Tomcat
sudo systemctl restart tomcat

Jetty как embedded сервер

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.servlet.ServletContainer;

public class JettyServer {
    public static void main(String[] args) throws Exception {
        Server server = new Server(8080);
        
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        
        ServletHolder jerseyServlet = context.addServlet(ServletContainer.class, "/*");
        jerseyServlet.setInitOrder(0);
        jerseyServlet.setInitParameter("jakarta.ws.rs.Application", "com.example.RestApplication");
        
        server.setHandler(context);
        server.start();
        server.join();
    }
}

Docker контейнер

FROM openjdk:17-jdk-slim
COPY target/myapp.war /app.war
EXPOSE 8080
CMD ["java", "-jar", "/app.war"]

Мониторинг и производительность

Jersey предоставляет встроенные возможности для мониторинга:

import org.glassfish.jersey.server.monitoring.ApplicationEvent;
import org.glassfish.jersey.server.monitoring.ApplicationEventListener;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.glassfish.jersey.server.monitoring.RequestEventListener;

@Provider
public class MonitoringListener implements ApplicationEventListener {
    
    @Override
    public void onEvent(ApplicationEvent event) {
        switch (event.getType()) {
            case INITIALIZATION_FINISHED:
                System.out.println("Application initialized");
                break;
        }
    }
    
    @Override
    public RequestEventListener onRequest(RequestEvent requestEvent) {
        return new RequestEventListener() {
            @Override
            public void onEvent(RequestEvent event) {
                switch (event.getType()) {
                    case RESOURCE_METHOD_START:
                        System.out.println("Request started: " + event.getUriInfo().getPath());
                        break;
                    case FINISHED:
                        System.out.println("Request finished");
                        break;
                }
            }
        };
    }
}

Интеграция с базами данных

Пример интеграции с JPA:

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;

@Stateless
@Path("/users")
public class UserService {
    
    @PersistenceContext
    private EntityManager em;
    
    @GET
    public List<User> getAllUsers() {
        return em.createQuery("SELECT u FROM User u", User.class).getResultList();
    }
    
    @POST
    @Transactional
    public Response createUser(User user) {
        em.persist(user);
        return Response.status(Response.Status.CREATED).entity(user).build();
    }
}

Тестирование Jersey приложений

import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import static org.junit.Assert.*;

public class UserResourceTest extends JerseyTest {
    
    @Override
    protected Application configure() {
        return new RestApplication();
    }
    
    @Test
    public void testGetAllUsers() {
        Response response = target("/api/users").request().get();
        assertEquals(200, response.getStatus());
    }
    
    @Test
    public void testCreateUser() {
        User user = new User(1, "John", "john@example.com");
        Response response = target("/api/users")
            .request()
            .post(Entity.json(user));
        assertEquals(201, response.getStatus());
    }
}

Интересные факты и нестандартные применения

Jersey можно использовать не только для классических REST API:

  • WebSocket endpoints — Jersey поддерживает WebSocket через Tyrus
  • GraphQL интеграция — можно создать GraphQL endpoint через Jersey
  • Microservices mesh — Jersey отлично работает в Service Mesh архитектуре
  • Reactive programming — поддержка RxJava для реактивных приложений

Пример интеграции с RxJava:

import rx.Observable;
import rx.schedulers.Schedulers;

@GET
@Path("/reactive")
public Observable<String> getReactiveData() {
    return Observable.fromCallable(() -> {
        // Долгая операция
        return "Reactive result";
    }).subscribeOn(Schedulers.io());
}

Безопасность и аутентификация

Jersey предоставляет несколько способов реализации безопасности:

import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.Context;

@Path("/secure")
public class SecureResource {
    
    @GET
    @RolesAllowed("ADMIN")
    public Response getSecureData(@Context SecurityContext securityContext) {
        String username = securityContext.getUserPrincipal().getName();
        return Response.ok("Hello, " + username).build();
    }
}

Заключение и рекомендации

Jersey — это мощный и гибкий фреймворк для создания RESTful веб-сервисов на Java. Он отлично подходит для:

  • Enterprise приложений — благодаря полной поддержке JAX-RS стандарта
  • Microservices — легковесность и быстрая настройка
  • API Gateway — отличные возможности для маршрутизации и фильтрации
  • Интеграционных решений — богатая экосистема для работы с различными форматами данных

Когда использовать Jersey:

  • Нужна максимальная совместимость с JAX-RS стандартом
  • Планируется интеграция с Java EE/Jakarta EE
  • Требуется высокая производительность
  • Нужны продвинутые возможности работы с HTTP

Когда лучше выбрать альтернативы:

  • Если нужна максимальная простота настройки — Spring Boot
  • Для облачных native приложений — Quarkus или Micronaut
  • Для простых API без сложной логики — Spark Java

Jersey продолжает активно развиваться и остаётся одним из лучших выборов для создания RESTful веб-сервисов на Java. Официальная документация доступна на официальном сайте, а исходный код — на GitHub.


В этой статье собрана информация и материалы из различных интернет-источников. Мы признаем и ценим работу всех оригинальных авторов, издателей и веб-сайтов. Несмотря на то, что были приложены все усилия для надлежащего указания исходного материала, любая непреднамеренная оплошность или упущение не являются нарушением авторских прав. Все упомянутые товарные знаки, логотипы и изображения являются собственностью соответствующих владельцев. Если вы считаете, что какой-либо контент, использованный в этой статье, нарушает ваши авторские права, немедленно свяжитесь с нами для рассмотрения и принятия оперативных мер.

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

Leave a reply

Your email address will not be published. Required fields are marked