Страницы

Поиск по вопросам

понедельник, 8 октября 2018 г.

Как работает web приложение java?

Кто-нибудь может объяснить как работает java web приложение с аннотациями и spring-mvc совместно с сервером приложений? (В моем случае это Glassfish 4)
Когда мы работаем с консольным приложением java, мы указываем команде java имя скомилированного класса. Я так понимаю, JRE ищет в указанном классе метод public static void main(String args[]) и выполняет его. И этот метод main считается точкой входа в программу.
А когда у нас web приложение, у нас имеется какая-то точка входа? Я по началу думал, что точка входа это web.xml. Но если мы используем spring-mvc и конфигурируем его с помощью аннотаций, то я так понял web.xml вообще может быть пустым. Когда глассфишу скармливается war'ник, то я так понимаю в памяти создаются экземпляры классов которые помечены аннотациями @Service и @Controller, но кто их создает? Я читал, что это делает спринг, в рамках его IoC, DI и его контейнерной роли, но спринг тоже кто-то должен запустить? По логике, инициализация спринга должна происходить при явном или неявном вызове его конструкторов в точке входа, но где тогда она? Или его инициализацию производит сервер приложений? Но каким образом мы указываем ему это сделать?
Вот мой SpringInitializer, я так понимаю основная магия происходит здесь? Этот код выполняет Glassfish? Как он понимает, что именно этот класс надо выполнить?
import javax.servlet.MultipartConfigElement; import javax.servlet.ServletRegistration; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpringInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override protected Class[] getRootConfigClasses() { return new Class[] { SpringConfiguration.class }; }
@Override protected Class[] getServletConfigClasses() { return null; }
@Override protected String[] getServletMappings() { return new String[] { "/" }; } }
Я рылся в руководстве Шилдта по Java, а также в Spring in Action, и пару недель пробую различные запросы по сабжу в гугл. Или в этих книгах не было явных ответов на эти вопросы, или я их пропустил, или не понял.
Буду признателен за объяснение, или если кто тыкнет носом где читать.


Ответ

Я рылся в руководстве Шилдта по Java, а также в Spring in Action
Если вы хотите разобраться с базовыми механизмами работы веб-приложений начинать стоит со спецификации Servlet API (3.0, 3.1).
А когда у нас web приложение, у нас имеется какая-то точка входа?
Как таковой точки входа нет. Есть контейнер сервлетов, который берет ваш WAR и, если все хорошо, стартует жизненный цикл вашего веб-приложения: устанавливает параметры, оповещает слушателей, пробрасывает запросы в сервлеты, пропускает их через фильтры. По сути, контейнер сервлетов занимается тем, что оперирует объектами, которые можно описать в web.xml, а, начиная с 3й версии спецификации, он должен уметь искать их и без web.xml.
... но кто их создает? Я читал, что это делает спринг, в рамках его IoC, DI и его контейнерной роли, но спринг тоже кто-то должен запустить?
Все верно, IoC-контейнер спринга (а именно AnnotationConfigWebApplicationContext в вашем случае) сканирует видимый ему CLASSPATH и создает ваши бины, ориентируясь на аннотации. Осталось понять, кто создает экземпляр этого ApplicationContext и запускает его жизненный цикл (см. ниже).
По логике, инициализация спринга должна происходить при явном или неявном вызове его конструкторов в точке входа, но где тогда она? Или его инициализацию производит сервер приложений? Но каким образом мы указываем ему это сделать?
Вы очень верно рассуждаете. Начиная со спецификации Servlet API 3.0 появилась возможность не использовать web.xml. Но контейнер сервлетов должен как-то узнать, где находится код, инициализирующий веб-приложение. Тут помогает механизм, известный как Service Provider. Если коротко, он ищет в CLASSPATH для контейнера сервлетов файл-дескриптор META-INF/services/javax.servlet.ServletContainerInitializer, который указывает на реализацию интерфейса ServletContainerInitializer. В случае Spring-а это будет класс SpringServletContainerInitializer.
Давайте без стеснения посмотрим на его исходники. На объявлении класса висит аннотация @HandlesTypes
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer {}
По контракту интерфейса ServletContainerInitializer контейнер обязуется передать конкретной реализации в её единственный метод onStartup коллекцию классов, попадающих в перечисленные в аннотации @HandlesTypes. Здесь у нас указан только интерфейс WebApplicationInitializer, поэтому Glassfish передаст все найденные реализации WebApplicationInitializer. Для каждой реализации будет создан экземпляр и вызван метод onStartup
for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); }
И именно ваш SpringInitializer является такой реализацией. Кроме того SpringInitializer наследует AbstractDispatcherServletInitializer. И вот именно этот AbstractDispatcherServletInitializer и "запускает Spring", создавая WebApplicationContext. Все сошлось.

PS. Читайте спецификации и ознакомьтесь с JavaDoc упомянутых в моем ответе классов. В Spring вообще любят подробно описывать все "магические" механизмы в документации ключевых абстрактных классов и интерфейсов.

Комментариев нет:

Отправить комментарий