Статьи

Добавьте REST к автономной Java с помощью Jetty и Spring WebMVC

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

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

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

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

Для начала, чтобы сделать веб-приложение, мы добавим файл web.xml . С Servlet 3.0 мы могли бы избежать наличия web.xml , но было бы неплохо иметь его, поскольку он поддерживает совместимость с Servlet 2.x. Мы используем Maven, так что он входит src/main/webapp/WEB-INF.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC 
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
 "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>

  <display-name>REST API</display-name>
  <description>Sample Spring WebMVC REST API</description>

  <servlet>
    <servlet-name>rest</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>rest</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>

</web-app>

Там не так много для этой конфигурации. Главное, что нужно отметить, это то, что мы не реализуем сервлет сами; мы позволяем Spring WebMVC DispatcherServletделать работу. Он DispatcherServletделает то, на что это похоже: он принимает запросы и выясняет, куда они должны идти.

В том же WEB-INFкаталоге нам теперь нужно настроить Spring WebMVC. Для этого нам нужен файл с именем rest-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:mvc="http://www.springframework.org/schema/mvc"
  xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:component-scan base-package="org.anvard.webmvc.server"/>
    <mvc:annotation-driven/>

</beans>

Имя файла важно, так как Spring WebMVC будет искать файл с этим именем из-за того, что <servlet-name>мы предоставили в web.xml. Важно помнить, чтобы изменить один, если вы измените другой.

Это обычный файл конфигурации Spring XML. Мы могли бы создавать здесь произвольные компоненты, связывать их вместе, настраивать управление транзакциями, постоянство, что бы мы ни выбрали. В этом случае мы используем два элемента, которые были добавлены в Spring за последние несколько лет и связаны с новой конфигурацией Spring на основе аннотаций.

  • <context:component-scan>сообщает Spring, что вместо перечисления всех bean-компонентов в XML-файле мы хотим, чтобы он сканировал путь к классам. Мы даем ему base-packageвозможность сделать поиск более эффективным и убедиться, что он не подбирает вещи, которые нам не нужны. Существует ряд аннотаций на уровне классов Spring, которые сообщают Spring, что экземпляр класса должен создаваться как бин, но для целей WebMVC мы будем использовать @Controller.
  • <mvc:annotation-driven/>сообщает Spring, что по мере добавления bean-компонентов в контекст приложения Spring он должен искать в них аннотации WebMVC, чтобы найти цели для диспетчера. Подробнее об этом в следующий раз, но есть и отличная справочная документация Spring .

Как следует из названия, Spring WebMVC — это привнесение шаблона проектирования модель-представление-контроллер в веб-программирование. Контроллеры обрабатывают запросы, опционально извлекая данные из модели. В максимально возможной степени среда Spring сама обрабатывает представление, что для интерфейсов REST в основном означает преобразование в JSON или XML.

Наш класс контроллера Java выглядит следующим образом:

package org.anvard.webmvc.server; 

 import org.anvard.webmvc.api.Calculation;
 import org.springframework.stereotype.Controller;
 import org.springframework.util.Assert;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.ResponseBody;

 @Controller
 public class Calculator {

     @RequestMapping(value = "/calc/{op}/{left}/{right}", 
                     method = RequestMethod.GET)
     @ResponseBody
     public Calculation calculate(@PathVariable("op") String op, 
             @PathVariable("left") Integer left,
             @PathVariable("right") Integer right) {
         Assert.notNull(op);
         Assert.notNull(left);
         Assert.notNull(right);
         Calculation result = new Calculation();
         result.setOperation(op);
         result.setLeft(left);
         result.setRight(right);
         return doCalc(result);
     }

     @RequestMapping(value = "/calc2", method = RequestMethod.POST)
     @ResponseBody
     public Calculation calculate(@RequestBody Calculation calc) {
         Assert.notNull(calc);
         Assert.notNull(calc.getOperation());
         Assert.notNull(calc.getLeft());
         Assert.notNull(calc.getRight());
         return doCalc(calc);
     }

     private Calculation doCalc(Calculation c) {
         String op = c.getOperation();
         int left = c.getLeft();
         int right = c.getRight();
         if (op.equalsIgnoreCase("subtract")) {
             c.setResult(left - right);
         } else if (op.equalsIgnoreCase("multiply")) {
             c.setResult(left * right);
         } else if (op.equalsIgnoreCase("divide")) {
             c.setResult(left / right);
         } else {
             c.setResult(left + right);
         }
         return c;
     }

 }

В следующий раз я подробно расскажу о различных аннотациях WebMVC и о том, как они используются для представления различных типов интерфейсов REST. На сегодня я хочу закончить тем, что это полный пример REST-приложения Spring WebMVC. Мы можем создать WAR-файл с этими тремя файлами (и зависимостями Spring), и он будет развертываться в контейнере сервлета и предоставлять интерфейсы REST. С правильными зависимостями этот пример вернет клиенту XML или JSON, в зависимости от того, что запросил клиент.