Ызкштп. Оглавление Введение 61. Что такое инверсия контроля (IoC) и внедрение зависимостей (DI) Как они реализованы в
Скачать 1.17 Mb.
|
HandlerMapping, который определяет, какой Контроллер (Controller) должен быть вызван, после чего HandlerAdapter, отправляет запрос в нужный метод Контроллера. ❖ Контроллер принимает запрос и вызывает соответствующий служебный метод, основанный на GET, POST и т.д. Вызванный метод формирует данные Модели (например, набор данных из БД) и возвращает их в DispatcherServlet вместе с именем Представления (View) (как правило имя html-файла). ❖ При помощи интерфейса ViewResolver DispatcherServlet определяет, какое Представление нужно использовать на основании полученного имени и получает в ответе имя представления View. ➢ если это REST-запрос на сырые данные (JSON/XML), то DispatcherServlet сам его отправляет; ➢ если обычный запрос, то DispatcherServlet отправляет данные Модели в виде атрибутов в Представление (View) - шаблонизаторы Thymeleaf, FreeMarker и т.д., которые сами отправляют ответ. Как мы видим все действия происходят через один единственный DispatcherServlet. Сконфигурировать наше Spring MVC-приложение мы можем с помощью Java-config, добавив зависимость spring-webmvc и установив над классом конфигурации @EnableWebMvc , которая применит дефолтные настройки - зарегистрирует некоторые специальные бины из Spring MVC и адаптирует их к нашим бинам. Но, если требуется тонкая настройка, то мы можем имплементировать интерфейс WebMvcConfigurer и переопределить необходимые методы. Теперь нужно зарегистрировать конфигурацию в Spring Context это позволит сделать созданный нами класс MyWebAppInitializer, который нужно унаследовать от AbstractAnnotationConfigDispatcherServletInitializer, и передать в его методы классы нашей конфигурации RootConfig.class и App1Config.class: public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class>[] getRootConfigClasses() { return new Class>[] { RootConfig.class }; } @Override protected Class>[] getServletConfigClasses() { return new Class>[] { App1Config.class }; } @Override protected String[] getServletMappings() { return new String[] { "/*" }; } } Своими внутренними методами он создает два экземпляра WebApplicationContext в виде объектов класса AnnotationConfigWebApplicationContext. Если же у нас только один класс конфигурации, то его нужно передать в метод getRootConfigClasses(), а getServletConfigClasses() должен возвращать null. 25. Что такое ViewResolver? Источники: Spring - View Resolution Javastudy - Spring MVC – описание интерфейса ViewResolver Baeldung - A Guide to the ViewResolver in Spring MVC Все платформы MVC предоставляют способ работы с представлениями. Spring делает это с помощью ViewResolver, который позволяет отображать модели в браузере, не привязывая реализацию к определенной технологии представления. ViewResolver сопоставляет имена представлений, возвращаемых методами контроллеров, с фактическими представлениями (html-файлами). Spring Framework поставляется с довольно большим количеством ViewResolver, например InternalResourceViewResolver, XmlViewResolver, ResourceBundleViewResolver и несколькими другими. По умолчанию реализацией интерфейса ViewResolver является класс InternalResourceViewResolver. Любым реализациям ViewResolver желательно поддерживать интернационализацию, то есть множество языков. 26. Расскажите про шаблон проектирования Front Controller, как он реализован в Spring? Источники: Baeldung - A Guide to the Front Controller Pattern in Java Howtodoinjava - ContextLoaderListener vs DispatcherServlet Spring - DispatcherServlet Spring - The DispatcherServlet Паттерн Front Controller обеспечивает единую точку входа для всех входящих запросов. Все запросы обрабатываются одним фрагментом кода, который затем может делегировать ответственность за обработку запроса другим объектам приложения. Он также обеспечивает интерфейс для общего поведения, такого как безопасность, интернационализация и передача определенных представлений определенным пользователям. В Spring в качестве Front Controller выступает DispatcherServlet , все действия проходят через него. Как правило в приложении задаётся только один DispatcherServlet с маппингом “/”, который перехватывает все запросы. Это и есть реализация паттерна Front Controller. Однако иногда необходимо определить два и более DispatcherServlet-а, которые будут отвечать за свой собственный функционал. Например, чтобы один обрабатывал REST-запросы с маппингом “/api”, а другой обычные запросы с маппингом “/default”. Spring предоставляет нам такую возможность, и для начала нужно понять, что: ❖ Spring может иметь несколько контекстов одновременно. Одним из них будет корневой контекст, а все остальные контексты будут дочерними. ❖ Все дочерние контексты могут получить доступ к бинам, определенным в корневом контексте, но не наоборот. Корневой контекст не может получить доступ к бинам дочерних контекстов. ❖ Каждый дочерний контекст внутри себя может переопределить бины из корневого контекста. Каждый DispatcherServlet имеет свой дочерний контекст приложения. DispatcherServlet по сути является сервлетом (он расширяет HttpServlet), основной целью которого является обработка входящих веб- запросов, соответствующих настроенному шаблону URL. Он принимает входящий URI и находит правильную комбинацию контроллера и вида. Веб-приложение может определять любое количество DispatcherServlet-ов. Каждый из них будет работать в своем собственном пространстве имен, загружая свой собственный дочерний WebApplicationContext (на рисунке - Servlet WebApplicationContext) с вьюшками, контроллерами и т.д. Например, когда нам нужно в одном Servlet WebApplicationContext определить обычные контроллеры, а в другом REST-контроллеры. WebApplicationContext расширяет ApplicationContext (создаёт и управляет бинами и т.д.), но помимо этого он имеет дополнительный метод getServletContext(), через который у него есть возможность получать доступ к ServletContext-у. ContextLoaderListener создает корневой контекст приложения (на рисунке - Root WebApplicationContext) и будет использоваться всеми дочерними контекстами, созданными всеми DispatcherServlet. Напомню, что корневой контекст приложения будет общим и может быть только один. Root WebApplicationContext содержит компоненты, которые видны всем дочерним контекстам, такие как сервисы, репозитории, компоненты инфраструктуры и т.д. После создания корневого контекста приложения он сохраняется в ServletContext как атрибут, имя которого: WebApplicationContext.class.getName() + ".ROOT" Чтобы из контроллера любого дочернего контекста обратиться к корневому контексту приложения, мы можем использовать класс WebApplicationContextUtils, содержащий статические методы: @Autowired ServletContext context; ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(context); if (ac == null ){ return "root application context is null" ; } ContextLoaderListener vs DispatcherServlet 1. ContextLoaderListener создает корневой контекст приложения. 2. Каждый DispatcherServlet создаёт себе один дочерний контекст. 3. Дочерние контексты могут обращаться к бинам, определенным в корневом контексте. 4. Бины в корневом контексте не могут получить доступ к бинам в дочерних контекстах (напрямую). 5. Все контексты добавляются в ServletContext. 6. Мы можем получить доступ к корневому контексту, используя класс WebApplicationContextUtils. 27. Чем отличаются Model, ModelMap и ModelAndView? Источники: Baeldung - Model, ModelMap, and ModelView in Spring MVC Spring - Interface Model Spring - Class ModelMap Spring - Class ModelAndView Model Интерфейс, лежит в пакете spring-context. В методах контроллера мы можем использовать объекты Model для того, чтобы складывать туда данные, предназначенные для формирования представлений. Кроме того, в Model мы можем передать даже Map с атрибутами: @GetMapping ( "/showViewPage" ) public String passParametersWithModel (Model model) { Map HashMap<>(); map.put( "spring" , "mvc" ); model.addAttribute( "message" , "Baeldung" ); model.mergeAttributes(map); return "viewPage" ; } ModelMap Этот класс наследуется от LinkedHashMap Имеет все преимущества LinkedHashMap плюс несколько удобных методов: @GetMapping ( "/printViewPage" ) public String passParametersWithModelMap (ModelMap map) { map.addAttribute( "welcomeMessage" , "welcome" ); map.addAttribute( "message" , "Baeldung" ); return "viewPage" ; } ModelAndView Этот класс лежит в пакете spring-webmvc и может одновременно хранить модели и представление, чтобы контроллер мог отдавать их в одном возвращаемом значении. Внутри содержит поле private Object view, куда записывает нужное представление, а также поле private ModelMap model, куда и складывает все атрибуты модели: @GetMapping ( "/goToViewPage" ) public ModelAndView passParametersWithModelAndView () { ModelAndView modelAndView = new ModelAndView( "viewPage" ); modelAndView.addObject( "message" , "Baeldung" ); return modelAndView; } 28. Расскажите про аннотации @Controller и @RestController. Чем они отличаются? Как вернуть ответ со своим статусом (например 213)? Источники: Baeldung - @Controller and @RestController Baeldung - Using Spring ResponseEntity @Controller @Controller помечает класс как контроллер HTTP-запросов. @Controller обычно используется в сочетании с аннотацией @RequestMapping, используемой в методах обработки запросов. Это просто дочерняя аннотация аннотации @Component и позволяет автоматически определять классы при сканировании пакетов. @RestController Аннотация @RestController была введена в Spring 4.0 для упрощения создания RESTful веб-сервисов. Это удобная аннотация, которая объединяет @Controller и @ResponseBody, что устраняет необходимость аннотировать каждый метод обработки запросов аннотацией @ResponseBody. @ResponseBody сообщает контроллеру, что возвращаемый объект автоматически сериализуется в json или xml и передается обратно в объект HttpResponse. Контроллер использует Jackson message converter для конвертации входящих/исходящих данных. Как правило целевые данные представлены в json или xml. ResponseEntity Данный класс используется для формирования ответа HTTP с пользовательскими параметрами (заголовки, код статуса и тело ответа). ResponseEntity необходим, только если мы хотим кастомизировать ответ. Во всех остальных случаях достаточно использовать @ResponseBody. Если мы хотим использовать ResponseEntity, то просто должны вернуть его из метода, Spring позаботится обо всем остальном. @GetMapping ( "/customHeader" ) ResponseEntity () { HttpHeaders headers = new HttpHeaders(); headers.add( "Custom-Header" , "foo" ); return new ResponseEntity<>( "Custom header set" , headers, HttpStatus.OK); } Если клиент ждет от нас JSON/XML, мы можем параметризовать ResponseEntity конкретным классом и добавить к ответу заголовки и Http статус: @RequestMapping (value = "/employees/{id}" ) public ResponseEntity (@PathVariable( "id" ) int id){ if (id <= 3 ) { EmployeeVO employee = new EmployeeVO( 1 , "Lokesh" , "Gupta" , "howtodoinjava@gmail.com" ); return new ResponseEntity } return new ResponseEntity(HttpStatus.NOT_FOUND); } } 29. В чем разница между Filters, Listeners и Interceptors? Источники: Javadoc - Interface Filter Javadoc - Interface ServletContextListener Spring API - Interface Interceptor Spring API - Interface HandlerInterceptor O7planning - Руководство Spring MVC Interceptor Baeldung - Introduction to Spring MVC HandlerInterceptor Programmer - Filters, Interceptors, Listeners Mkjava - Filter vs. Interceptor Oracle - Servlet Filters and Event Listeners Mkyong - ServletContextListener Example Filter Это интерфейс из пакета javax.servlet, имплементации которого выполняют задачи фильтрации либо по пути запроса к ресурсу (сервлету, либо по статическому контенту), либо по пути ответа от ресурса, либо в обоих направлениях. Фильтры выполняют фильтрацию в методе doFilter. Каждый фильтр имеет доступ к объекту FilterConfig, из которого он может получить параметры инициализации, и ссылку на ServletContext, который он может использовать, например, для загрузки ресурсов, необходимых для задач фильтрации. Фильтры настраиваются в дескрипторе развертывания веб-приложения. В веб-приложении мы можем написать несколько фильтров, которые вместе называются цепочкой фильтров. Веб-сервер решает, какой фильтр вызывать первым, в соответствии с порядком регистрации фильтров. Когда вызывается метод doFilter(ServletRequest request, ServletResponse response, FilterChain chain) первого фильтра, веб-сервер создает объект FilterChain, представляющий цепочку фильтров, и передаёт её в метод. Interceptor Это интерфейс из пакета org.aopalliance.intercept, предназначенный для аспектно- ориентированного программирования. В Spring, когда запрос отправляется в Controller, перед тем как он в него попадёт, он может пройти через перехватчики Interceptor (0 или более). Это одна из реализаций АОП в Spring. Вы можете использовать Interceptor для выполнения таких задач, как запись в Log, добавление или обновление конфигурации перед тем, как запрос обработается Controller-ом. Стек перехватчиков: он предназначен для связывания перехватчиков в цепочку в определенном порядке. При доступе к перехваченному методу или полю перехватчик в цепочке перехватчиков вызывается в том порядке, в котором он был определен. Мы можем использовать Interceptor-ы для выполнения логики до попадания в контроллер, после обработки в контроллере, а также после формирования представления. Также можем запретить выполнение метода контроллера. Мы можем указать любое количество перехватчиков. Перехватчики работают с HandlerMapping и поэтому должны реализовывать интерфейс HandlerInterceptor или наследоваться от готового класса HandlerInterceptorAdapter. В случае реализации HandlerInterceptor нам нужно переопределить 3 метода, а в случае HandlerInterceptor, только необходимые нам: ❖ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) - вызывается после того, как HandlerMapping определил соответствующий контроллер, но до того, как HandlerAdapter вызовет метод контроллера. С помощью этого метода каждый перехватчик может решить, прервать цепочку выполнения или направить запрос на испольнение дальше по цепочке перехватчиков до метода контроллера. Если этот метод возвращает true, то запрос отправляется следующему перехватчику или в контроллер. Если метод возвращает false, то исполнение запроса прекращается, обычно отправляя ошибку HTTP или записывая собственный ответ в response. ❖ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) - отработает после контроллера, но перед формированием представления. Мы можем использовать этот метод для добавления дополнительных атрибутов в ModelAndView или для определения времени, затрачиваемого методом-обработчиком на обработку запроса клиента. Вы можете добавить больше объектов модели в представление, но вы не можете изменить HttpServletResponse, так как он уже зафиксирован. ❖ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) - отработает после формирования представления. Вызывается только в том случае, если метод preHandle этого перехватчика успешно завершен и вернул true! Следует знать, что HandlerInterceptor связан с бином DefaultAnnotationHandlerMapping, который отвечает за применение перехватчиков к любому классу, помеченному аннотацией @Controller. Чтобы добавить наши перехватчики в конфигурацию Spring, нам нужно переопределить метод addInterceptors () внутри класса, который реализует WebMvcConfigurer: @Override public void addInterceptors (InterceptorRegistry registry) { // LogInterceptor applies to all URLs. registry.addInterceptor( new LogInterceptor()); // This interceptor applies to URL /admin/oldLogin. // Using OldURLInterceptor to redirect to new URL. registry.addInterceptor( new OldLoginInterceptor()) .addPathPatterns( "/admin/oldLogin" ); // This interceptor applies to URLs like /admin/* // Exclude /admin/oldLogin registry.addInterceptor( new AdminInterceptor()) .addPathPatterns( "/admin/*" ) // .excludePathPatterns( "/admin/oldLogin" ); } Filter vs. Interceptor ❖ Перехватчик основан на механизме Reflection, а фильтр основан на обратном вызове функции. ❖ Фильтр зависит от контейнера сервлета, тогда как перехватчик не зависит от него. ❖ Перехватчики могут работать только с запросами к контроллерам, в то время как фильтры могут работать почти со всеми запросами (например, js, .css и т.д.). |