Spring обеспечивает поддержку удаленного доступа с помощью различных технологий. Поддержка удаленного доступа облегчает разработку служб удаленного доступа, реализованных с помощью интерфейсов Java и объектов в качестве ввода и вывода. В настоящее время Spring поддерживает следующие технологии удаленного доступа:

  • Веб-сервисы Java: Spring обеспечивает поддержку удаленного доступа для веб-сервисов через JAX-WS.

  • AMQP: Удаленное взаимодействие через AMQP в качестве базового протокола поддерживается отдельным проектом Spring AMQP.

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

Следующие технологии удаленного доступа уже устарели и не будут заменены:

  • Удаленный вызов метода (RMI): Благодаря использованию RmiProxyFactoryBean и RmiServiceExporter, Spring поддерживает как традиционный RMI (через интерфейсы java.rmi.Remote и java.rmi.RemoteException), так и прозрачное удаленное взаимодействие через инициаторы вызова RMI (через любой Java-интерфейс).

  • Инициатор вызова по протоколу HTTP в Spring (устарело): Spring предусматривает специальную стратегию удаленного доступа, которая позволяет осуществлять Java-сериализацию через протокол HTTP с поддержкой любого Java-интерфейса (как это делает инициатор вызова RMI). Соответствующими вспомогательными классами являются HttpInvokerProxyFactoryBean и HttpInvokerServiceExporter.

  • Hessian: Используя HessianProxyFactoryBean и HessianServiceExporter из Spring, вы можете прозрачно открывать свои службы через облегченный двоичный протокол на основе HTTP, предоставляемый Caucho.

  • JMS (устарело): Удаленное взаимодействие через JMS в качестве базового протокола поддерживается с помощью классов JmsInvokerServiceExporter и JmsInvokerProxyFactoryBean в модуле spring-jms.

Описывая возможности удаленного доступа в Spring, мы используем следующую модель предметной области и соответствующие службы:

public class Account implements Serializable {
    private String name;
    public String getName(){
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public interface AccountService {
    public void insertAccount(Account account);
    public List<Account> getAccounts(String name);
}
// реализация не делает ничего в данный момент
public class AccountServiceImpl implements AccountService {
    public void insertAccount(Account acc) {
        // делаем что-нибудь...
    }
    public List<Account> getAccounts(String name) {
        // делаем что-нибудь...
    }
}

Этот раздел начинается с экспонирования службы удаленному клиенту при помощи RMI и немного касается недостатков использования RMI. Далее приводится пример, в котором в качестве протокола используется Hessian.

AMQP

Удаленное взаимодействие через AMQP в качестве базового протокола поддерживается в проекте Spring AMQP.

Автоматическое определение не реализовано для удаленных интерфейсов.

Основная причина, по которой автоматическое определение внедренных интерфейсов не осуществляется для удаленных интерфейсов, заключается в том, чтобы не давать слишком много доступа удаленным вызывающим программам. Целевой объект может реализовывать внутренние интерфейсы обратного вызова, такие как InitializingBean или DisposableBean, которые нежелательно открывать вызывающим программам.

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

Это компромисс между удобством конфигурации и риском случайного раскрытия внутренних методов. Постоянное задание интерфейса службы не требует слишком больших усилий и обеспечивает безопасность в отношении контролируемого использования определенных методов.

Соображения при выборе технологии

Каждая из представленных здесь технологий имеет свои недостатки. При выборе технологии следует тщательно подумать над своими потребностями, службами, которые вы предоставляете, и объектами, которые отправляете по сети.

При использовании RMI получить доступ к объектам через протокол HTTP нельзя, если только не туннелировать трафик RMI. RMI является достаточно нагруженным протоколом, поскольку поддерживает полную сериализацию объектов, что важно, если используется сложная модель данных, требующая сериализации по сети. Однако RMI-JRMP привязан к Java-клиентам. Это решение для удаленного взаимодействия типа "Java-Java".

Инициатор вызова по протоколу HTTP из Spring – это хороший выбор, если требуется удаленное взаимодействие на основе HTTP, но при этом вы прибегаете к Java-сериализации. Он имеет общую базовую инфраструктуру с инициаторами вызова RMI, но использует протокол HTTP в качестве механизма передачи данных. Обратите внимание, что инициаторы вызова по протоколу HTTP не ограничиваются только лишь удаленным взаимодействие типа "Java-Java", но также поддерживают и Spring как на стороне клиента, так и на стороне сервера. (Последнее также касается инициатора вызова RMI из Spring для не-RMI интерфейсов).

Протокол Hessian может быть ощутимо полезен при работе в разнородном окружении, поскольку явно допускает использование не-Java клиентов. Однако поддержка не-Java все еще ограничена. Среди известных проблем сериализация объектов Hibernate в сочетании с коллекциями с отложенной инициализацией. Если у вас есть такая модель данных, рассмотрите возможность использования инициаторов вызова RMI или HTTP вместо Hessian.

JMS может быть полезен для создания кластеров служб, что позволяет JMS-брокеру заниматься балансировкой нагрузки, обнаружением и автоматическим аварийным переключением. По умолчанию для удаленного взаимодействия на основе JMS используется Java-сериализация, но JMS-поставщик может использовать другой механизм для форматирования сети, например, XStream, чтобы можно было реализовать серверы на других технологиях.

И последнее, но не менее важное, преимущество EJB перед RMI заключается в том, что он поддерживает стандартную аутентификацию и авторизацию на основе ролей и удаленное распространение транзакций. Можно заставить инициаторы вызовов RMI или HTTP также поддерживать распространение контекста безопасности, хотя это не предусмотрено ядром Spring. Spring предусматривает только подходящие перехватчики для подключения сторонних или кастомных решений.

Веб-сервисы Java

Spring обеспечивает полную поддержку стандартных API веб-сервисов Java:

  • Экспонирование веб-сервисов с помощью JAX-WS

  • Доступ к веб-сервисам с помощью JAX-WS

В дополнение к поддержке JAX-WS в Spring Core, в портфеле Spring также есть Spring Web Services, который является решением для веб-сервисов, основанных на первичном определении контракта типа и управляемых документами, – настоятельно рекомендуется его использование для создания современных веб-сервисов с заделом на будущее.

Экспонирование веб-сервисов на основе сервлетов с помощью JAX-WS

Spring предусматривает удобный базовый класс для реализации конечных точек сервлетов JAX-WS: SpringBeanAutowiringSupport. Чтобы открыть наш AccountService, мы расширяем класс SpringBeanAutowiringSupport и реализуем в нем нашу бизнес-логику, обычно делегируя вызов бизнес-уровню. Мы используем аннотацию @Autowired из Spring для выражения таких зависимостей от бинов, управляемых Spring. В следующем примере показан наш класс, расширяющий SpringBeanAutowiringSupport:

/**
 * Совместимая с JAX-WS реализация AccountService, которая просто делегирует полномочия
 * реализации AccountService в корневом контексте веб-приложения.
 *
 * Этот класс-обёртка необходим, поскольку JAX-WS требуется работать с выделенными.
 * классами конечных точек. Если необходимо экспортировать существующую службу, создание функции-обёртки, которая
 * расширяет SpringBeanAutowiringSupport для простого автоматического обнаружения и связывания бинов Spring (через
 * аннотацию @Autowired), является самым простым способом, совместимым с JAX-WS.
 *
 * Это класс, зарегистрированный в серверной реализации JAX-WS.
 * В случае сервера Java EE он просто определяется как сервлет.
 * в web.xml, при этом сервер определяет, что это конечная точка JAX-WS, и реагирует на это
 * соответственно. Имя сервлета обычно должно соответствовать указанному имени службы WS.
 *
 * Механизм веб-сервиса управляет жизненным циклом экземпляров этого класса.
 * Здесь будут просто подключены ссылки на бины Spring.
 */
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
@WebService(serviceName="AccountService")
public class AccountServiceEndpoint extends SpringBeanAutowiringSupport {
    @Autowired
    private AccountService biz;
    @WebMethod
    public void insertAccount(Account acc) {
        biz.insertAccount(acc);
    }
    @WebMethod
    public Account[] getAccounts(String name) {
        return biz.getAccounts(name);
    }
}

Наша AccountServiceEndpoint должна работать в том же веб-приложении, что и контекст Spring, чтобы обеспечить доступ к средствам Spring. Это происходит по умолчанию в средах Java EE при использовании стандартного контракта на развертывания конечных точек сервлетов JAX-WS. Подробнее см. в различных руководствах по веб-сервисам на Java EE.

Экспорт автономных веб-сервисов с помощью JAX-WS

Встроенный поставщик JAX-WS в составе JDK от Oracle, поддерживает открытие веб-сервисов с помощью встроенного HTTP-сервера, который также входит в состав JDK. SimpleJaxWsServiceExporter из Spring обнаруживает все помеченные аннотацией @WebService бины в контексте приложения Spring и экспортирует их через JAX-WS-сервер по умолчанию (HTTP-сервер JDK).

В этом сценарии экземпляры конечных точек определяются и управляются как бины Spring. Они регистрируются в механизме JAX-WS, но их жизненный цикл зависит от контекста приложения Spring. Это означает, что можно применять функциональность Spring (например, явное внедрение зависимостей) к экземплярам конечных точек. Внедрение зависимостей, управляемое аннотациями, через аннотацию @Autowired также работает. В следующем примере показано, как определить эти бины:

<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter">
    <property name="baseAddress" value="http://localhost:8080/"/>
</bean>
<bean id="accountServiceEndpoint" class="example.AccountServiceEndpoint">
    ...
</bean>
...

AccountServiceEndpoint может быть – но не обязательно – производной от SpringBeanAutowiringSupport, поскольку конечная точка в этом примере является полностью управляемым бином Spring. Это означает, что реализация конечной точки может быть следующей (без объявления какого-либо суперкласса – а конфигурационная аннотация @Autowired из Spring все еще будет соблюдена):

@WebService(serviceName="AccountService")
public class AccountServiceEndpoint {
    @Autowired
    private AccountService biz;
    @WebMethod
    public void insertAccount(Account acc) {
        biz.insertAccount(acc);
    }
    @WebMethod
    public List<Account> getAccounts(String name) {
        return biz.getAccounts(name);
    }
}

Экспорт веб-сервисов с помощью средств поддержки JAX-WS RI в Spring

Фреймворк JAX-WS RI от Oracle, разработанный в рамках проекта GlassFish, поставляется со средствами поддержки Spring в рамках проекта JAX-WS Commons. Это позволяет определять конечные точки JAX-WS как бины, управляемые Spring, аналогично автономному режиму – но на сей раз в сервлетном окружении.

Возможность переноса на Java EE отсутствует. В основном этот фреймворк предназначен для не-EE окружений, таких как Tomcat, которые предопределяют JAX-WS RI в качестве составляющей веб-приложения.

Отличия от стандартного стиля экспорта конечных точек на основе сервлетов заключаются в том, что жизненный цикл самих экземпляров конечных точек управляется Spring и что в web.xml определен только один сервлет JAX-WS. В стандартном стиле Java EE (как показано ранее), есть одно определение сервлета для каждой конечной точки службы, при этом каждая конечная точка обычно делегируется бинам Spring (с помощью аннотации @Autowired, как показано ранее).

Подробности о настройке и стиле использования см. на сайте metro-jaxws-commons.

Доступ к веб-сервисам при помощи JAX-WS

Spring предусматривает два бина-фабрики для создания прокси веб-сервисов JAX-WS, а именно LocalJaxWsServiceFactoryBean и JaxWsPortProxyFactoryBean. Первый может вернуть только класс сервиса JAX-WS, с которым нам и предстоит работать. Последний – это полноценная версия, которая может вернуть прокси, реализующий наш интерфейс бизнес-сервиса. В следующем примере используется JaxWsPortProxyFactoryBean для создания прокси для конечной точки AccountService (снова):

<bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
    <property name="serviceInterface" value="example.AccountService"/> 
    <property name="wsdlDocumentUrl" value="http://localhost:8888/AccountServiceEndpoint?WSDL"/>
    <property name="namespaceUri" value="https://example/"/>
    <property name="serviceName" value="AccountService"/>
    <property name="portName" value="AccountServiceEndpointPort"/>
</bean>
  1. Где serviceInterface – это наш бизнес-интерфейс, который используют клиенты.

wsdlDocumentUrl – это URL-адрес WSDL-файла. Spring эти данные требуются при запуске для создания службы JAX-WS. namespaceUri соответствует targetNamespace в файле .wsdl. serviceName соответствует имени службы в файле .wsdl. portName соответствует имени порта в файле .wsdl.

Получить доступ к веб-сервису просто, поскольку у нас есть фабрика бинов для него, которая открывает этот сервис в качестве интерфейса под названием AccountService. В следующем примере показано, как можно все это связать в Spring:

<bean id="client" class="example.AccountClientImpl">
    ...
    <property name="service" ref="accountWebService"/>
</bean>

Из клиентского кода можно получить доступ к веб-сервису, как если бы это был обычный класс, что и показано в следующем примере:

public class AccountClientImpl {
    private AccountService service;
    public void setService(AccountService service) {
        this.service = service;
    }
    public void foo() {
        service.insertAccount(...);
    }
}
Вышесказанное немного упрощено в том смысле, что JAX-WS требует, чтобы интерфейсы конечных точек и классы реализации были аннотированы с помощью @WebService, @SOAPBinding и т.д. Это означает, что нельзя (трудно) использовать обычные Java-интерфейсы и классы реализации в качестве артефактов конечных точек JAX-WS; требуется сначала аннотировать их соответствующим образом. Подробнее об этих требованиях читайте в документации по JAX-WS.

RMI (устарело)

Начиная с версии Spring Framework 5.3, средства поддержки RMI устарели не подлежат замене.

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

Экспорт службы с помощью RmiServiceExporter

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

Сначала необходимо настроить нашу службу в контейнере Spring. В следующем примере показано, как это сделать:

<bean id="accountService" class="example.AccountServiceImpl">
    <!-- какие-либо дополнительные свойства, может быть DAO? -->
</bean>

Далее нужно открыть нашу службу с помощью RmiServiceExporter. В следующем примере показано, как это сделать:

<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
    <!-- не обязательно должно совпадать с именем экспортируемого бина  -->
    <property name="serviceName" value="AccountService"/>
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
    <!-- умолчанию 1099 -->
    <property name="registryPort" value="1199"/>
</bean>

В предыдущем примере мы переопределили порт для RMI-реестра. Зачастую сервер приложений также поддерживает RMI-реестр, поэтому разумно не лезть в него. Кроме того, имя службы используется для привязки службы. Так, в предыдущем примере служба привязана по адресу "rmi://HOST:1199/AccountService". В дальнейшем этот URL-адрес используется для ссылки на службу на стороне клиента.

Свойство servicePort было опущено (по умолчанию оно равно 0). Это означает, что для связи со службой используется анонимный порт.

Привязка службы на стороне клиента

Наш клиент – это простой объект, который использует AccountService для управления учетными записями, как показано в следующем примере:

public class SimpleObject {
    private AccountService accountService;
    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }
    // дополнительные методы, использующие accountService
}

Чтобы привязать службу на стороне клиента, создается отдельный контейнер Spring, содержащий следующий простой объект и конфигурационные биты привязки службы:

<bean class="example.SimpleObject">
    <property name="accountService" ref="accountService"/>
</bean>
<bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
    <property name="serviceUrl" value="rmi://HOST:1199/AccountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

Это все, что нужно сделать для обеспечения поддержки службы удаленного доступа к учетной записи на стороне клиенте. Spring прозрачно создает инициатор вызова и удаленно активирует службу доступа к учетным записям через RmiServiceExporter. На стороне клиента мы привязываем его с помощью RmiProxyFactoryBean.

Использование Hessian для удаленного вызова служб через протокол HTTP (устарело)

Начиная со Spring Framework 5.3, средства поддержки протокола Hessian устарели и не подлежат замене.

Hessian – это двоичный протокол удаленного доступа на основе HTTP. Он разработан компанией Caucho, а более подробную информацию о самом протоколе Hessian можно найти на сайте caucho.

Hessian

Hessian взаимодействует через HTTP и делает это с помощью кастомного сервлета. Используя принципы DispatcherServlet из Spring, можно подключить такой сервлет для открытия ваших служб. Во-первых, нужно создать новый сервлет в приложении, как показано в следующей выдержке из web.xml:

<servlet>
    <servlet-name>remoting</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>remoting</servlet-name>
    <url-pattern>/remoting/*</url-pattern>
</servlet-mapping>

Если вы знакомы с принципами работы DispatcherServlet из Spring, то, вероятно, знаете, что теперь необходимо создать ресурс конфигурации контейнера Spring с именем remoting-servlet.xml (по имени вашего сервлета) в каталоге WEB-INF. Контекст приложения используется в следующем разделе.

В качестве альтернативы можно использовать более простой HttpRequestHandlerServlet из Spring. Это позволит встроить определения удаленного экспортера в корневой контекст приложения (по умолчанию в WEB-INF/applicationContext.xml), при этом отдельные определения сервлетов будут указывать на конкретные экспортируемые бины. В этом случае имя каждого сервлета должно соответствовать имени бина его целевого экспортера.

Экспонирование ваших бинов с помощью HessianServiceExporter

Во вновь созданном контексте приложения под названием remoting-servlet.xml мы создаем HessianServiceExporter для экспорта наших служб, как показано в следующем примере:

<bean id="accountService" class="example.AccountServiceImpl">
    <!-- какие-либо дополнительные свойства, может быть DAO? -->
</bean>
<bean name="/AccountService" class="org.springframework.remoting.caucho.HessianServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

Теперь можно подключить службу на стороне клиента. Явное отображение обработчиков не задано (для отображения URL-адресов запросов на службы), поэтому мы используем BeanNameUrlHandlerMapping. Таким образом, служба экспортируется по URL-адресу, указанному через имя его бина в содержащемся отображении экземпляра DispatcherServlet (как определено ранее): https://HOST:8080/remoting/AccountService.

Кроме того, можно создать HessianServiceExporter в корневом контексте приложения (например, в WEB-INF/applicationContext.xml), как показано в следующем примере:

<bean name="accountExporter" class="org.springframework.remoting.caucho.HessianServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

В последнем случае следует определить соответствующий сервлет для этого экспортера в web.xml, с тем же конечным результатом: Экспортер сопоставляется с путем запроса по адресу /remoting/AccountService. Обратите внимание, что имя сервлета должно совпадать с именем бина целевого экспортера. В следующем примере показано, как это сделать:

<servlet>
    <servlet-name>accountExporter</servlet-name>
    <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>accountExporter</servlet-name>
    <url-pattern>/remoting/AccountService</url-pattern>
</servlet-mapping>

Привязка службы к клиенту

Используя HessianProxyFactoryBean, можно привязать службу к клиенту. Применяются те же принципы, что и в примере с RMI. Мы создаем отдельную фабрику бинов или контекст приложения и упоминаем следующие бины там, где находится SimpleObject, используя AccountService для управления учетными записями, как показано в следующем примере:

<bean class="example.SimpleObject">
    <property name="accountService" ref="accountService"/>
</bean>
<bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
    <property name="serviceUrl" value="https://remotehost:8080/remoting/AccountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

Применение базовой аутентификации через протокол HTTP к службе, открываемой через Hessian

Одним из преимуществ протокола Hessian является то, что мы можем легко применять базовую аутентификацию HTTP, поскольку оба протокола основаны на HTTP. Обычный механизм безопасности HTTP-сервера можно применить, например, с помощью функций безопасности из web.xml Обычно в этом случае не требуется использовать набор данных безопасности для каждого пользователя. Вместо этого можно использовать общие учетные данные, которые определяются на уровне HessianProxyFactoryBean (аналогично DataSource мз JDBC), как показано в следующем примере:

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="interceptors" ref="authorizationInterceptor"/>
</bean>
<bean id="authorizationInterceptor"
        class="org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor">
    <property name="authorizedRoles" value="administrator,operator"/>
</bean>

В предыдущем примере мы явно упомянули BeanNameUrlHandlerMapping и установили перехватчик, чтобы позволить исключительно администраторам и операторам вызывать бины, упомянутые в данном контексте приложения.

В предыдущем примере гибкая инфраструктура безопасности не показана. Для получения дополнительной информации по безопасности см. проект Spring Security на сайте https://spring.io/projects/spring-security/.

Инициатор вызова по протоколу HTTP в Spring (устарело)

Начиная со Spring Framework 5.3, средства поддержки инициатора вызова HTTP устарели и не подлежат замене.

В отличие от протокола Hessian, иницаторы вызова HTTP в Spring выступают в роли облегченных протоколов, которые используют или собственные тонкие механизмы сериализации, или стандартный механизм сериализации Java для открытия служб через протокол HTTP. В этом кроется ощутимое преимущество, если аргументы и возвращаемые типы представляют собой сложные типы, которые нельзя сериализовать с помощью механизмов сериализации, используемых протоколом Hessian (см. следующий раздел, чтобы узнать больше о выборе технологии удаленного взаимодействия).

С точки зрения внутренней структуры Spring использует либо стандартные средства, предоставляемые JDK, либо Apache HttpComponents для выполнения HTTP-вызовов. Если требуется более продвинутая и простая в использовании функциональность, используйте последний вариант.

Помните об уязвимостях, связанных с небезопасной Java-десериализацией: Манипулирование потоками вводных данных может привести к нежелательному выполнению кода на стороне сервера во время этапа десериализации. Как следствие, не следует открывать конечные точки инициатора вызова HTTP ненадежным клиентам. Вместо этого открывайте их для использования только между своими собственными службами. В общем, мы настоятельно рекомендуем использовать вместо этого любой другой формат сообщений (например, JSON).

Если вы обеспокоены уязвимостями в безопасности, связанными с Java-сериализацией, рассмотрите механизм фильтра сериализации общего назначения на уровне ядра JVM, который изначально был разработан для JDK 9, но со временем был перенесен в JDK 8, 7 и 6. См. https://blogs.oracle.com/java-platform-group/entry/incoming_filter_serialization_data_a и https://openjdk.java.net/jeps/290.

Экспонирование объекта службы

Процесс настройки инфраструктуры инициатора вызова HTTP для объекта-службы очень похож на процесс настройки при использовании протокола Hessian. Так же, как средства поддержки Hessian предусматривают HessianServiceExporter, средства поддержки в виде HttpInvoker из Spring предусматривают org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter.

Чтобы открыть AccountService (упомянутый ранее) в DispatcherServlet из Spring Web MVC, в контексте приложения диспетчера должна содержаться следующая конфигурация, как показано в примере далее:

<bean name="/AccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

Такое определение экспортера открывается через стандартные средства отображения экземпляра DispatcherServlet.

Как вариант, можно создать HttpInvokerServiceExporter в корневом контексте приложения (например, в 'WEB-INF/applicationContext.xml'), как показано в следующем примере:

<bean name="accountExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
    <property name="service" ref="accountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

Кроме того, можно определить соответствующий сервлет для этого экспортера в web.xml, причем имя сервлета будет соответствовать имени бина целевого экспортера, как показано в следующем примере:

<servlet>
    <servlet-name>accountExporter</servlet-name>
    <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>accountExporter</servlet-name>
    <url-pattern>/remoting/AccountService</url-pattern>
</servlet-mapping>

Привязка службы на стороне клиента

Опять же, процедура привязка службы из клиента очень похожа на аналогичную процедуру при использовании протокола Hessian. Используя прокси, Spring может преобразовывать ваши вызовы в запросы через HTTP-метод POST по URL-адресу, указывающему на экспортируемую службу. В следующем примере показано, как сконфигурировать эту схему:

<bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
    <property name="serviceUrl" value="https://remotehost:8080/remoting/AccountService"/>
    <property name="serviceInterface" value="example.AccountService"/>
</bean>

Как упоминалось ранее, можно выбрать, какой HTTP-клиент использовать. По умолчанию HttpInvokerProxy использует HTTP-функциональность из JDK, но также можно использовать клиент Apache HttpComponents, установив свойство httpInvokerRequestExecutor. В следующем примере показано, как это сделать:

<property name="httpInvokerRequestExecutor">
    <bean class="org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor"/>
</property>