Как будет выполнять служба отдыха, когда пользователь получает доступ к ресурсу из класса ресурсов?
Когда несколько пользователей запрашивают один и тот же метод ресурса из класса обслуживания, как будут обрабатываться запросы на сервере?
Как служба отдыха будет выполнять для каждого запроса? Чем отличается жизненный цикл выполнения службы покоя от выполнения сервлета?
Например, если ниже указан Ресурс, как он будет создан и выполнен в следующих сценариях:
Случай 1: два пользователя вызывают два разных метода одновременно
Случай 2: два пользователя вызывают один и тот же метод одновременно
@Path("greet")
public class GreetingResource {
@GET
@Path("welcome/{username}")
@Produces(MediaType.TEXT_PLAIN)
public String sayWelcome(@PathParam("username") String User) {
return "Welcome!" + User;
}
@GET
@Path("hello/{username}")
@Produces(MediaType.TEXT_PLAIN)
public String sayHello(@PathParam("username") String User) {
return "Hello " + User;
}
}
2 ответа
Классы сервлетов
Контейнер сервлета создает один экземпляр класса сервлета для обработки всех запросов к этому сервлету. Смотрите следующую цитату из JSR 369, которая определяет спецификацию Servlet 4.0:
Для сервлета, не размещенного в распределенной среде (по умолчанию), контейнер сервлета должен использовать только один экземпляр для каждого объявления сервлета. [...]
Servlet
Интерфейс определяет методы для инициализации сервлета, обслуживания запросов и удаления сервлета с сервера:
- Сервлет создается, а затем инициализируется
init
метод. - Любые звонки от клиентов на
service
метод обрабатываются. - Сервлет выведен из эксплуатации, а затем уничтожен
destroy
метод, затем мусор собрал и доработал.
Не стесняйтесь проверить спецификацию для получения дополнительной информации.
Ресурсные классы
В то время как классы сервлетов управляются контейнером сервлетов, классы ресурсов управляются средой выполнения JAX-RS. Кстати, приложения JAX-RS даже не требуют развертывания контейнера сервлета.
По умолчанию классы ресурсов (помеченные @Path
) находятся в области запроса. Это означает, что среда выполнения JAX-RS будет создавать новый экземпляр класса ресурса для каждого запроса к этому ресурсу. Цитируя JSR 370, документ определяет спецификацию JAX-RS 2.1:
По умолчанию новый экземпляр класса ресурса создается для каждого запроса к этому ресурсу. Сначала вызывается конструктор, затем вводятся все запрошенные зависимости, затем вызывается соответствующий метод и, наконец, объект становится доступным для сборки мусора. [...]
Согласно документации на Джерси, это вряд ли может быть причиной проблем с производительностью. Построение классов и сборка мусора JVM значительно улучшились за эти годы, и многие объекты будут создаваться и отбрасываться для обслуживания и обработки HTTP-запроса и возврата HTTP-ответа.
Также это создает очень естественную модель программирования, в которой можно использовать конструкторы и поля, не заботясь о множественных одновременных запросах к одному и тому же ресурсу.
EJB и CDI
Для реализации, которая поддерживает EJB, классы ресурсов могут быть аннотированы @Stateless
или же @Singleton
,
Реализации JAX-RS также могут поддерживать CDI, однако JAX-RS и CDI имеют несколько разные модели компонентов. По умолчанию классы ресурсов JAX-RS управляются в области запроса, и для указания области не требуется никаких аннотаций. Управляемые компоненты CDI с пометкой @RequestScoped
или же @ApplicationScoped
могут быть преобразованы в классы ресурсов JAX-RS.
См. Учебное пособие по Java EE 8 для примеров.
Провайдерские классы
В отличие от классов ресурсов, классов провайдеров (те, которые отмечены @Provider
) ограничены областью применения по умолчанию. Это означает, что среда выполнения JAX-RS создаст только один экземпляр для каждого класса провайдера. Из JSR 370:
По умолчанию один экземпляр каждого класса провайдера создается для каждого приложения JAX-RS. Сначала вызывается конструктор, затем вводятся любые запрошенные зависимости, затем соответствующие методы провайдера могут вызываться несколько раз (одновременно), и, наконец, объект становится доступным для сборки мусора. [...]
Из документации Джерси, которая является реализацией JAX-RS:
Жизненный цикл по умолчанию (применяется, когда аннотации отсутствуют). В этой области экземпляр ресурса создается для каждого нового запроса и используется для обработки этого запроса. Если ресурс используется более одного раза при обработке запроса, всегда будет использоваться один и тот же экземпляр. Это может произойти, когда ресурс является подресурсом и возвращается больше раз во время сопоставления. В этой ситуации только один экземпляр будет обслуживать запросы.
Из жизненного цикла класса ресурсов JAX-RS:
По умолчанию жизненный цикл корневых классов ресурсов является для каждого запроса, а именно то, что новый экземпляр корневого класса ресурсов создается каждый раз, когда путь URI запроса совпадает с корневым ресурсом.
Короче говоря, следующие вещи происходят последовательно.
Path’s are matched with Resource classes. Constructor is called. Dependencies are injected. Appropriate method is called. Resource is garbage collected.
Как ни странно, классы ресурсов JAX-RS по умолчанию не являются одноэлементными. В целом это вряд ли может быть причиной проблем с производительностью. Построение классов и сборка мусора JVM значительно улучшились за эти годы, и многие объекты будут создаваться и отбрасываться для обслуживания и обработки HTTP-запроса и возврата HTTP-ответа.
Хотя класс конечных точек по умолчанию создается новым по запросу, вы можете сделать его одноэлементным, чтобы иметь по одному экземпляру для каждого приложения JAX-RS: Влияние производительности жизненного цикла ресурса JAX-RS
Говоря о ваших примерах, в обоих случаях экземпляры 1 и 2 не будут отличаться, и пользователи будут использовать 2 экземпляра GreetingResource
и получить их имя по возвращении. В случае 2, если метод будет использовать базу данных, а 2 пользователя изменят один и тот же ресурс, вам потребуется управлять одновременным доступом с оптимистической блокировкой или другим решением.