1. Введение
Spring MVC — это исходная веб-инфраструктура от Spring, построенная на API сервлетов. Он обеспечивает архитектуру Model-View-Controller, которую можно использовать для разработки гибких веб-приложений.
В этом уроке мы сосредоточимся на вопросах, связанных с ним, так как это часто является темой собеседования для разработчиков Spring.
Дополнительные вопросы по Spring Framework вы можете найти в другой статье из нашей серии вопросов для интервью , посвященной Spring .
2. Основные вопросы Spring MVC
Q1. Почему мы должны использовать Spring MVC?
Spring MVC реализует четкое разделение задач, что позволяет нам легко разрабатывать и тестировать наши приложения .
Такие понятия, как:
- Диспетчерский сервлет
- Контроллеры
- Посмотреть резольверы
- Виды, Модели
Модель и вид
- Атрибуты модели и сеанса
полностью независимы друг от друга и отвечают только за одно.
Поэтому MVC дает нам довольно большую гибкость . Он основан на интерфейсах (с предоставленными классами реализации), и мы можем настроить каждую часть фреймворка с помощью пользовательских интерфейсов.
Еще важно то, что мы не привязаны к конкретной технологии представления (например, JSP), а имеем возможность выбирать из тех, которые нам больше всего нравятся .
Кроме того, мы используем Spring MVC не только при разработке веб-приложений, но и при создании веб-сервисов RESTful .
Q2. Какова роль аннотации @Autowired
?
Аннотацию @Autowired
можно использовать с полями или методами для внедрения бина по типу . Эта аннотация позволяет Spring разрешать и внедрять сотрудничающие компоненты в ваш компонент.
Дополнительные сведения см. в учебнике по @Autowired
в Spring .
Q3. Объясните атрибут модели
Аннотация @ModelAttribute
— одна из самых важных аннотаций в Spring MVC. Он связывает параметр метода или возвращаемое значение метода с именованным атрибутом модели, а затем предоставляет его веб-представлению .
Если мы используем его на уровне метода, это указывает на то, что целью этого метода является добавление одного или нескольких атрибутов модели.
С другой стороны, при использовании в качестве аргумента метода это указывает на то, что аргумент должен быть получен из модели. Если он отсутствует, мы должны сначала создать его экземпляр, а затем добавить его в модель. После присутствия в модели мы должны заполнить поля аргументов из всех параметров запроса, имеющих совпадающие имена.
Подробнее об этой аннотации можно прочитать в нашей статье, посвященной аннотации @ModelAttribute
.
Q4. Объясните разницу между @Controller
и @RestController
?
Основное различие между аннотациями @Controller
и @RestController
заключается в том, что аннотация @ResponseBody
автоматически включается в @RestController
. Это означает, что нам не нужно аннотировать наши методы обработчика с помощью @ResponseBody
. Нам нужно сделать это в классе @Controller
, если мы хотим записать тип ответа непосредственно в тело ответа HTTP.
Q5. Описать переменную пути
Мы можем использовать аннотацию @PathVariable
в качестве параметра метода обработчика, чтобы извлечь значение переменной шаблона URI .
Например, если мы хотим получить пользователя по id из www.mysite.com/user/123
, мы должны отобразить наш метод в контроллере как /user/{id}
:
@RequestMapping("/user/{id}")
public String handleRequest(@PathVariable("id") String userId, Model map) {}
@PathVariable имеет
только один элемент с именем value
. Это необязательно, и мы используем его для определения имени переменной шаблона URI . Если мы опустим элемент value, то имя переменной шаблона URI должно совпадать с именем параметра метода.
Также разрешено иметь несколько аннотаций @PathVariable
, либо объявляя их одну за другой:
@RequestMapping("/user/{userId}/name/{userName}")
public String handleRequest(@PathVariable String userId,
@PathVariable String userName, Model map) {}
или поместить их все в Map<String, String>
или MultiValueMap<String, String>
:
@RequestMapping("/user/{userId}/name/{userName}")
public String handleRequest(@PathVariable Map<String, String> varsMap, Model map) {}
Q6. Проверка с использованием Spring MVC
Spring MVC по умолчанию поддерживает спецификации JSR-303. Нам нужно добавить JSR-303 и его зависимости от реализации в наше приложение Spring MVC . Hibernate Validator, например, является одной из реализаций JSR-303, находящихся в нашем распоряжении.
JSR-303 — это спецификация API Java для проверки компонентов, входящая в состав Jakarta EE и JavaSE, которая обеспечивает соответствие свойств компонента определенным критериям с использованием таких аннотаций, как @NotNull
, @Min
и @Max
. Дополнительные сведения о проверке доступны в статье Основы проверки Java Bean .
Spring предлагает аннотацию @Validator
и класс BindingResult
. Реализация Validator
вызовет ошибки в методе обработчика запросов контроллера, когда у нас будут недопустимые данные. Затем мы можем использовать класс BindingResult
для получения этих ошибок.
Помимо использования существующих реализаций, мы можем сделать свои собственные. Для этого мы сначала создаем аннотацию, которая соответствует спецификациям JSR-303. Затем мы реализуем класс Validator .
Другой способ - реализовать интерфейс Spring Validator
и установить его в качестве валидатора с помощью аннотации @InitBinder
в классе Controller .
Чтобы узнать, как реализовать и использовать собственные проверки, см. руководство, посвященное пользовательской проверке в Spring MVC .
Q7. Что такое аннотации @RequestBody
и @ResponseBody
?
Аннотация @RequestBody
, используемая в качестве параметра метода-обработчика, привязывает тело HTTP-запроса к передаче или объекту домена . Spring автоматически десериализует входящий HTTP-запрос в объект Java с помощью преобразователей Http-сообщений.
Когда мы используем аннотацию @ResponseBody
для метода-обработчика в контроллере Spring MVC, это указывает на то, что мы запишем возвращаемый тип метода непосредственно в тело ответа HTTP . Мы не будем помещать его в модель
, и Spring не будет интерпретировать его как имя представления.
Пожалуйста, ознакомьтесь со статьей @RequestBody
и @ResponseBody
, чтобы узнать больше об этих аннотациях.
Q8. Объясните Model
, ModelMap
и ModelAndView
?
Интерфейс модели
определяет держатель атрибутов модели . ModelMap имеет
аналогичную цель с возможностью передачи набора значений . Затем он обрабатывает эти значения, как если бы они находились внутри Map
. Следует отметить, что в Model
( ModelMap
) мы можем хранить только данные. Мы помещаем данные и возвращаем имя представления.
С другой стороны, с ModelAndView
мы возвращаем сам объект . Мы устанавливаем всю необходимую информацию, такую как данные и имя представления, в объекте, который мы возвращаем.
Вы можете найти более подробную информацию в статье о Model, ModelMap
и ModelView
.
Q9. Объясните SessionAttributes
и SessionAttribute
Аннотация @SessionAttributes
используется для хранения атрибута модели в сеансе пользователя. Мы используем его на уровне класса контроллера, как показано в нашей статье об атрибутах сеанса в Spring MVC :
@Controller
@RequestMapping("/sessionattributes")
@SessionAttributes("todos")
public class TodoControllerWithSessionAttributes {
@GetMapping("/form")
public String showForm(Model model,
@ModelAttribute("todos") TodoList todos) {
// method body
return "sessionattributesform";
}
// other methods
}
В предыдущем примере атрибут модели todos
будет добавлен к сеансу, если @ModelAttribute
и @SessionAttributes
имеют одинаковый атрибут имени.
Если мы хотим получить существующий атрибут из сеанса, управляемого глобально, мы будем использовать аннотацию @SessionAttribute
в качестве параметра метода :
@GetMapping
public String getTodos(@SessionAttribute("todos") TodoList todos) {
// method body
return "todoView";
}
Q10. Какова цель @EnableWebMVC
?
Цель аннотации @EnableWebMvc
— включить Spring MVC через конфигурацию Java . Это эквивалентно <mvc: на основе аннотаций>
в конфигурации XML. Эта аннотация импортирует конфигурацию Spring MVC из WebMvcConfigurationSupport
. Он обеспечивает поддержку классов с аннотациями @Controller
, которые используют @RequestMapping
для сопоставления входящих запросов с методом обработчика.
Подробнее об этой и подобных аннотациях можно узнать в нашем Руководстве по Spring @Enable
Annotations .
Q11. Что такое ViewResolver
в Spring?
ViewResolver позволяет
приложению отображать модели в браузере, не привязывая реализацию к конкретной технологии представления, путем сопоставления имен представлений с фактическими представлениями .
Дополнительные сведения о ViewResolver см
. в нашем Руководстве по ViewResolver в Spring MVC .
Q12. Что такое BindingResult
?
BindingResult
— это интерфейс из пакета org.springframework.validation
, который представляет результаты привязки. Мы можем использовать его для обнаружения и сообщения об ошибках в отправленной форме . Его легко вызвать — нам просто нужно убедиться, что мы поместили его в качестве параметра сразу после объекта формы, который мы проверяем. Необязательный параметр Model
должен идти после BindingResult
, как это видно из руководства по пользовательскому валидатору :
@PostMapping("/user")
public String submitForm(@Valid NewUserForm newUserForm,
BindingResult result, Model model) {
if (result.hasErrors()) {
return "userHome";
}
model.addAttribute("message", "Valid form");
return "userHome";
}
Когда Spring увидит аннотацию @Valid
, он сначала попытается найти валидатор для проверяемого объекта. Затем он подберет аннотации проверки и вызовет средство проверки. Наконец, он поместит найденные ошибки в BindingResult
и добавит последний в модель представления.
Q13. Что такое объект поддержки формы?
Объект поддержки формы или объект команды — это просто POJO, который собирает данные из формы, которую мы отправляем .
Мы должны иметь в виду, что он не содержит никакой логики, только данные.
Чтобы узнать, как использовать объект поддержки формы с формами в Spring MVC, ознакомьтесь с нашей статьей о формах в Spring MVC .
Q14. Какова роль аннотации @Qualifier
?
Он используется одновременно с аннотацией @Autowired
, чтобы избежать путаницы при наличии нескольких экземпляров типа компонента .
Давайте посмотрим пример. Мы объявили два похожих bean-компонента в конфигурации XML:
<bean id="person1" class="com.foreach.Person" >
<property name="name" value="Joe" />
</bean>
<bean id="person2" class="com.foreach.Person" >
<property name="name" value="Doe" />
</bean>
Когда мы попытаемся связать компонент, мы получим исключение org.springframework.beans.factory.NoSuchBeanDefinitionException.
Чтобы исправить это, нам нужно использовать @Qualifier
, чтобы сообщить Spring о том, какой компонент должен быть подключен:
@Autowired
@Qualifier("person1")
private Person person;
Q15. Какова роль аннотации @Required
?
Аннотация @Required
используется в методах установки и указывает, что свойство bean-компонента, имеющее эту аннотацию, должно быть заполнено во время настройки. В противном случае контейнер Spring выдаст исключение BeanInitializationException
.
Кроме того, @Required
отличается от @Autowired тем
, что он ограничен установщиком, а @Autowired
— нет. @Autowired
также можно использовать для связывания с конструктором и полем, а @Required
только проверяет, установлено ли свойство.
Давайте посмотрим пример:
public class Person {
private String name;
@Required
public void setName(String name) {
this.name = name;
}
}
Теперь имя
bean - компонента Person
необходимо установить в конфигурации XML следующим образом:
<bean id="person" class="com.foreach.Person">
<property name="name" value="Joe" />
</bean>
Обратите внимание, что @Required
по умолчанию не работает с классами @Configuration
на основе Java . Если вам нужно убедиться, что все ваши свойства установлены, вы можете сделать это при создании компонента в аннотированных методах @Bean .
Q16. Описать шаблон переднего контроллера
В шаблоне Front Controller все запросы сначала будут направляться на Front Controller, а не на сервлет . Он проверит готовность ответов и отправит их обратно в браузер. Таким образом, у нас есть одно место, где мы контролируем все, что приходит из внешнего мира.
Фронт-контроллер идентифицирует сервлет, который должен обработать запрос первым. Затем, когда он получит данные от сервлета, он решит, какое представление отображать, и, наконец, отправит обработанное представление обратно в качестве ответа:
Чтобы увидеть детали реализации, ознакомьтесь с нашим Руководством по шаблону Front Controller в Java .
Q17. Что такое архитектуры модели 1 и модели 2?
Модель 1 и Модель 2 представляют собой две часто используемые модели проектирования, когда речь идет о разработке веб-приложений Java.
В модели 1 запрос поступает в сервлет или JSP, где он обрабатывается . Сервлет или JSP обрабатывает запрос, обрабатывает бизнес-логику, извлекает и проверяет данные и генерирует ответ:
Поскольку эту архитектуру легко реализовать, мы обычно используем ее в небольших и простых приложениях.
С другой стороны, это неудобно для крупномасштабных веб-приложений. Функциональные возможности часто дублируются в JSP, где бизнес-логика и логика представления связаны.
Модель 2 основана на шаблоне проектирования Model View Controller и отделяет представление от логики, управляющей содержимым.
Кроме того, в шаблоне MVC можно выделить три модуля: модель, представление и контроллер . Модель представляет динамическую структуру данных приложения. Он отвечает за манипулирование данными и бизнес-логикой. Представление отвечает за отображение данных, а контроллер служит интерфейсом между двумя предыдущими.
В модели 2 запрос передается контроллеру, который обрабатывает необходимую логику, чтобы получить правильный контент, который должен отображаться. Затем контроллер помещает содержимое обратно в запрос, обычно в виде JavaBean или POJO. Он также решает, какое представление должно отображать содержимое, и, наконец, передает ему запрос. Затем представление отображает данные:
3. Продвинутые вопросы Spring MVC
Q18. В чем разница между аннотациями @Controller
, @Component
, @Repository
и @Service
в Spring?
Согласно официальной документации Spring, @Component
— это общий стереотип для любого компонента, управляемого Spring. @Repository
, @Service
и @Controller
являются специализациями @Component
для более специфических вариантов использования, например, на уровнях постоянства, обслуживания и представления соответственно.
Давайте посмотрим на конкретные варианты использования последних трех:
- @
Controller
— указывает, что класс выполняет роль контроллера, и обнаруживает аннотации@RequestMapping
внутри класса. - @
Сервис
— указывает, что класс содержит бизнес-логику и вызывает методы на уровне репозитория. - @
Repository
— указывает, что класс определяет хранилище данных; его работа состоит в том, чтобы перехватывать исключения для конкретной платформы и повторно выдавать их как одно из унифицированных непроверенных исключений Spring.
Q19. Что такое DispatcherServlet
и ContextLoaderListener
?
Проще говоря, в шаблоне проектирования Front Controller один контроллер отвечает за направление входящих запросов HttpRequest
ко всем другим контроллерам и обработчикам приложения.
DispatcherServlet
Spring реализует этот шаблон и, следовательно, отвечает за правильную координацию HttpRequests
с правильными обработчиками.
С другой стороны, ContextLoaderListener
запускается и закрывает корневой WebApplicationContext Spring
. Он связывает жизненный цикл ApplicationContext
с жизненным циклом ServletContext.
Мы можем использовать его для определения общих компонентов, работающих в разных контекстах Spring.
Дополнительные сведения о DispatcherServlet
см . в этом руководстве .
Q20. Что такое MultipartResolver
и когда мы должны его использовать?
Интерфейс MultipartResolver
используется для загрузки файлов . Платформа Spring предоставляет одну реализацию MultipartResolver
для использования с Commons FileUpload и другую для использования с анализом составных запросов Servlet 3.0.
Используя их, мы можем поддерживать загрузку файлов в наших веб-приложениях.
Q21. Что такое перехватчик Spring MVC и как его использовать?
Перехватчики Spring MVC позволяют нам перехватывать клиентский запрос и обрабатывать его в трех местах — перед обработкой, после обработки или после завершения (при отображении представления) запроса.
Перехватчик можно использовать для сквозных задач и во избежание повторяющегося кода обработчика, такого как ведение журнала, изменение глобально используемых параметров в модели Spring и т. д.
Подробности и различные реализации см. в статье Introduction to Spring MVC HandlerInterceptor .
Q22. Что такое инициализирующий биндер?
Метод с аннотацией @InitBinder
используется для настройки параметра запроса, шаблона URI и объектов поддержки/команды . Мы определяем его в контроллере, и это помогает контролировать запрос. В этом методе мы регистрируем и настраиваем наши пользовательские PropertyEditor'ы
, средство форматирования и валидаторы .
В аннотации есть элемент ' value '.
Если мы не установим его, аннотированные методы @InitBinder
будут вызываться при каждом HTTP-запросе. Если мы установим значение, методы будут применяться только для определенных атрибутов команды/формы и/или параметров запроса, имена которых соответствуют элементу ' значение '.
Важно помнить, что одним из аргументов должен быть WebDataBinder.
Другие аргументы могут относиться к любому типу, поддерживаемому методами обработчика, за исключением объектов команд/форм и соответствующих объектов результатов проверки.
Q23. Объясните совет контроллера
Аннотация @ControllerAdvice
позволяет нам писать глобальный код, применимый к широкому спектру контроллеров . Мы можем привязать диапазон контроллеров к выбранному пакету или конкретной аннотации.
По умолчанию @ControllerAdvice
применяется к классам, аннотированным с помощью @Controller
(или @RestController
) . У нас также есть несколько свойств, которые мы используем, если хотим быть более конкретными.
Если мы хотим ограничить применимые классы пакетом, мы должны добавить имя пакета в аннотацию :
@ControllerAdvice("my.package")
@ControllerAdvice(value = "my.package")
@ControllerAdvice(basePackages = "my.package")
Также можно использовать несколько пакетов, но на этот раз нам нужно использовать массив вместо String
.
Помимо ограничения пакета по его имени, мы можем сделать это, используя один из классов или интерфейсов из этого пакета :
@ControllerAdvice(basePackageClasses = MyClass.class)
Элемент assignableTypes
применяет @ControllerAdvice
к определенным классам, а элемент Annotations — к определенным аннотациям
.
Следует помнить, что мы должны использовать его вместе с @ExceptionHandler
. Эта комбинация позволит нам настроить глобальный и более конкретный механизм обработки ошибок без необходимости реализовывать его каждый раз для каждого класса контроллера.
Q24. Что делает аннотация @ExceptionHandler
?
Аннотация @ExceptionHandler
позволяет нам определить метод, который будет обрабатывать исключения . Мы можем использовать аннотацию независимо, но гораздо лучше использовать ее вместе с @ControllerAdvice
. Таким образом, мы можем настроить глобальный механизм обработки ошибок. Таким образом, нам не нужно писать код для обработки исключений в каждом контроллере .
Давайте посмотрим на пример из нашей статьи об обработке ошибок для REST с помощью Spring :
@ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
@ExceptionHandler(value = { IllegalArgumentException.class,
IllegalStateException.class })
protected ResponseEntity<Object> handleConflict(RuntimeException ex,
WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(),
HttpStatus.CONFLICT, request);
}
}
Мы также должны отметить, что это предоставит методы @ExceptionHandler
всем контроллерам, которые выдают IllegalArgumentException
или IllegalStateException
. Исключения, объявленные с помощью @ExceptionHandler
, должны соответствовать исключению, используемому в качестве аргумента метода. В противном случае механизм разрешения исключений выйдет из строя во время выполнения.
Здесь следует иметь в виду, что для одного и того же исключения можно определить более одного @ExceptionHandler .
Однако мы не можем сделать это в том же классе, поскольку Spring будет жаловаться, вызывая исключение и завершаясь ошибкой при запуске.
С другой стороны, если мы определим их в двух отдельных классах, приложение запустится, но будет использовать первый найденный обработчик, возможно, неправильный .
Q25. Обработка исключений в веб-приложениях
У нас есть три варианта обработки исключений в Spring MVC:
- за исключение
- на контроллер
- глобально
Если во время обработки веб-запроса возникнет необработанное исключение, сервер вернет ответ HTTP 500. Чтобы предотвратить это, мы должны аннотировать любое из наших пользовательских исключений аннотацией @ResponseStatus
. Этот тип исключений разрешается HandlerExceptionResolver
.
Это приведет к тому, что сервер вернет соответствующий HTTP-ответ с указанным кодом состояния, когда метод контроллера выдаст наше исключение. Мы должны помнить, что мы не должны обрабатывать наше исключение где-то еще, чтобы этот подход работал.
Другой способ обработки исключений — использование аннотации @ExceptionHandler
. Мы добавляем методы @ExceptionHandler
к любому контроллеру и используем их для обработки исключений, создаваемых внутри этого контроллера. Эти методы могут обрабатывать исключения без аннотации @ResponseStatus
, перенаправлять пользователя в специальное представление ошибок или создавать полностью настраиваемый ответ на ошибку.
Мы также можем передать объекты, связанные с сервлетом ( HttpServletRequest
, HttpServletResponse
, HttpSession
и Principal
) в качестве параметров методов обработчика. Но мы должны помнить, что мы не можем напрямую указать объект Model
в качестве параметра.
Третий вариант обработки ошибок — классы @ControllerAdvice
. Это позволит нам применять те же методы, только на этот раз на уровне приложения, а не только к конкретному контроллеру. Чтобы включить это, нам нужно использовать @ControllerAdvice
и @ExceptionHandler
вместе. Таким образом, обработчики исключений будут обрабатывать исключения, генерируемые любым контроллером.
Для получения более подробной информации по этой теме прочитайте статью Обработка ошибок для REST с Spring .
4. Вывод
В этой статье мы рассмотрели некоторые вопросы, связанные со Spring MVC, которые могут возникнуть на техническом интервью для разработчиков Spring. Вы должны принять во внимание эти вопросы в качестве отправной точки для дальнейших исследований, поскольку это ни в коем случае не исчерпывающий список.
Желаем вам удачи в любых предстоящих интервью!