Як працює web додаток java stack overflow російською
Хто-небудь може пояснити як працює 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? Як він розуміє, що саме цей клас треба виконати?
Я рився в керівництві Шілдт по 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.
За контрактом інтерфейсу ServletContainerInitializer контейнер зобов'язується передати конкретної реалізації в її єдиний метод onStartup колекцію класів, що потрапляють в перераховані в анотації @HandlesTypes. Тут у нас вказано тільки інтерфейс WebApplicationInitializer. тому Glassfish передасть всі знайдені реалізації WebApplicationInitializer. Для кожної реалізації буде створено екземпляр і викликаний метод onStartup:
І саме ваш SpringInitializer є такою реалізацією. Крім того SpringInitializer успадковує AbstractDispatcherServletInitializer. І ось саме цей AbstractDispatcherServletInitializer і "запускає Spring", створюючи WebApplicationContext. Все зійшлося.
До версії JEE 6, коли було введено Servlet 3.0 API, ця специфікація вимагала поміщати конфігурацію вашого застосування в deployment descriptor - web.xml. Починаючи з Servlet 3.0 API, цей файл не є обов'язковим, замість нього додаток може використовувати інтерфейс ServletContainerInitializer. Коли ви розвертаєте web-додаток, контейнер сервлетів сканує classpath цього додатка на предмет наявності імплементації зазначеного інтерфейсу, а коли знаходить, то виконує метод onStartup (). в якому повинна виконуватися ініціалізація програми.
Оскільки Spring Web побудований на сервлетах, все написане вище відноситься до нього самим прямим чином. Конфиг на анотаціях в Spring доступний з версії Servlet 3.0 + API. Реалізація інтерфейсу ServletContainerInitializer знаходиться в модулі spring-web. ви його отримуєте відразу як підключаєте відповідну залежність в maven або gradle. Погляньте що робить цей код. Витяг з документації:
Interface which allows a library / runtime to be notified of a web application's startup phase and perform any required programmatic registration of servlets, filters, and listeners in response to it.
Він отримує всі класи, які позначені анотацією WebApplicationInitializer і ініціалізує їх. AbstractAnnotationConfigDispatcherServletInitializer. якщо ви подивіться його ієрархію, як раз є таким класом.
Далі, вибачте, я просто процитую відповідь в параллеьного сайту. де те ж саме розписано покроково:
- A web application WAR is being deployed by user.
- Servlet container (Tomcat) reads web.xml.
- Servlet context listener ContextLoaderListener is being instantiated (if defined as
inside the web.xml) by servlet container. - ContextLoaderListener creates new WebApplicationContext with application context XML configuration.
- Your ROOT context beans are registered and instantiated by BeanFactory inside the application context.
- DispatcherServlet is being instantiated by servlet container.
- DispatcherServlet creates its own WebApplicationContext (WEB-INF / -servlet.xml by default) with the ROOT context as its parent.
- Your servlet beans are registered and instantiated by BeanFactory inside the application context.
- DispatcherServlet registers some default beans in case you did not provide them yourself.
This one is possible with Servlet 3 features.
- A web application WAR is being deployed by user.
- Servlet container searches for classes implementing ServletContainerInitializer via Java's ServiceLoader.
- Spring's SpringServletContainerInitializer is found and instantiated by servlet container.
- Spring's initializer reads web application's class-path and searches for WebApplicationInitializer implementations.
- Your WebApplicationInitializer is found (btw. Check its JavaDoc.) And instantiated by SpringServletContainerInitializer.
- Your WebApplicationInitializer creates new ROOT WebApplicationContext with XML or @Configuration based configuration.
- Your WebApplicationInitializer creates new servlet WebApplicationContext with XML or @Configuration based configuration.
- Your WebApplicationInitializer creates and registers new DispatcherServlet with the context from previous step.
- Servlet container finishes the web application initialization and instantiates components which were registered by their class in previous steps (none in my example).
Т.ч. щоб запустити додаток на Spring за допомогою анотацій, потрібно імплементувати інтерфейс WebApplicationInitializer - можна як безпосередньо, так і успадкувати від одного з абстрактних класів, які є в поставці, і передати туди конфігураційний файл (в вашому прикладі SpringConfiguration). Далі Spring отримує з нього аннтотаціі @EnableWebMvc. @ComponentScan (це найосновніші) і витягує решту конфігурацію.
Всі разом це утворює ApplicationContext (незважаючи на те що написано в однині, їх може бути кілька) - опис середовища оточення додатки, який якраз і надає можливість звертатися в бінам, змінним і інших ресурсів додатки за допомогою анотацій.
відповідь дан 6 Березня '16 о 18:38
Точкою входу і є сам web.xml до якого звертається контейнер сервлетів при Деплой, яким у вашому випадку є гласфіш. Коли ви деплоіте варіння або запускаєте проект в своєму середовищі розробки, глазфіш піднімає, все сервлети і конфігурації з web.xml.
1) web.xml в Спринг не залишається порожнім, просто його роботу з Сервлетами бере на себе диспетчер сервлетів спрінга, але в web.xml ми все одно вказуємо конфігурації диспетчера.
2) Це питання я кілька разів перечитав і не можу зрозуміти його суть. Який Варнік і причому тут анотації контролерів і сервісів? Спрінг ніхто запускати не повинен, це просто спрощена версія контейнера бінов, в якій використовуються анотації для швидкої і зрозумілою розробки. У пам'яті створюється один бін, який за допомогою анотацій Autowired або Inject можна Інжект не створюючи нових екземплярів цього класу, а в разі декількох реалізацій біна, можна вибрати яку саме ви хочете використовувати додавши анотацію Qualifier (name = "").
3) У вашому прикладі наведено, як я зрозумів, конфігураційний файл, який так само повинен бути прописаний в вашому web.xml. Якщо ви використовуєте xml конфігурації, тоді вони явно вказуються в веб.хмл в приблизно такому вигляді:
Якщо ви використовуєте класи, тоді як глазфіш розуміє, що йому треба смикати це вже зашито в анотаціях самими розробниками спрінга і чому воно так працює, дати відповідь я не можу.
відповідь дан 6 Березня '16 о 18:53
але в web.xml ми все одно вказуємо конфігурації диспетчера. - Ні, з 3й версії сервлетів це вже не обов'язково. - Nofate ♦ 6 Березня '16 о 18:56
Спрінг ніхто запускати не повинен - він таки не чарівним чином стартує. Хтось повинен десь створити new AnnotationConfigWebApplicationContext (). - Nofate ♦ 6 Березня '16 о 19:04