Надпись:В этой статье обобщены соответствующие знания о Spring MVC, кратко объяснены его рабочий процесс и основные компоненты, а также представлены основы использования и распространенные технологии.Объясните, помогите понять поведение SpringMVC после получения запроса и, наконец, просто интегрируйте инфраструктуру SSM. .
- Источник вывода содержания статьи: Lagou Education Java High-paying Training Camp;
Введение в Spring MVC
Архитектура MVC
Трехуровневая архитектура
В архитектуре B/S трехуровневая архитектура системного стандарта включает:
-
Слой сохраняемости [слой dao]
Отвечает за сохранность данных,
Включает уровни базы данных и доступа к данным.
-
бизнес-уровень [сервисный уровень]
Отвечает за обработку бизнес-логики,
Зависит от уровня сохраняемости
-
Уровень представления [веб-уровень]
Отвечает за прием клиентских запросов, реагирование на результаты клиенту,
Включая уровень представления и уровень управления,
Уровень представления зависит от бизнес-уровня;
При проектировании уровня представления обычно используется модель MVC (MVC — это модель проектирования уровня представления и не имеет ничего общего с другими уровнями).
Шаблон проектирования MVC
MVC: Контроллер представления модели, многослойность предназначена для разъединения, простоты обслуживания и разделения труда
- Модель
- Бизнес-модель: Ведение бизнеса
- Модель данных: инкапсуляция данных
- Вид
- jsp/html, отображать данные
- Контроллер
- Часть, которая обрабатывает взаимодействие с пользователем, обычно выполняет логику обработки
Spring MVC
Полное название Spring MVC — Spring Web MVC.Это облегченная веб-инфраструктура на основе Java, которая реализует шаблон проектирования MVC и тип, управляемый запросами.Это продукт, следующий за SpringFrameWork. даЛучший MVC-фреймворк. Суть Spring MVC можно рассматривать как инкапсуляцию сервлетов, что упрощает разработку наших сервлетов.
Рабочий процесс Spring MVC и базовое использование
Поток обработки запросов Spring MVC
Описание основных компонентов Spring MVC
-
HandlerMapping
Роль: Найдите соответствующий обработчик (Handler) и обработчик-перехватчик (HandlerInterceptor) для запроса.
-
HandlerAdapter
Обработчик в Spring MVC может быть в любой форме (например, каждый метод, помеченный @RequestMapping, является обработчиком), но когда запрос передается сервлету, структура метода сервлета — doService (запрос HttpServletRequest, запрос HttpServletResponse). ) Формально HandlerAdapter отвечает за то, чтобы фиксированный метод обработки сервлета вызывал обработчик для обработки.
-
HandlerExceptionResolver
При работе с исключениями, созданными обработчиком, ModelAndView будет установлен в соответствии с исключением, а затем передан методу рендеринга для рендеринга в виде страницы.
-
ViewResolver
Преобразует имя представления и Locale типа String в представление типа View. Spring MVC по умолчанию использует InternalResourceViewResolver для представлений типа JSP.
-
RequestToViewNameTranslator
Получить ViewName из запроса
-
LocaleResolver
Для метода resolveViewName компонента ViewResolver требуется два параметра: один — имя представления, а другой — Locale. LocaleResolver используется для разрешения локали из запроса.Например, китайская локаль — zh-CN, которая используется для представления региона.
-
ThemeResolver
Компонент ThemeResolver используется для разрешения тем.
-
MultipartResolver
MultipartResolver используется для загрузки запросов путем упаковки обычных запросов в MultipartHttpServletRequest. MultipartHttpServletRequest может получить файл напрямую через метод getFile(). Если вы загружаете несколько файлов, вы также можете вызвать метод getFileMap(), чтобы получить структуру типа Map
Роль MultipartResolver заключается в инкапсуляции обычных запросов, чтобы он имел функцию загрузки файлов. -
FlashMapManager
FlashMap используется для передачи параметров при перенаправлении
Spring MVC перехватывает настройки правила запроса URL
-
Способ 1: с суффиксом
<!-- 举例 --> <url-pattern>*.do</url-pattern>
-
Способ 2: /, / не будет перехватывать .jsp, но будет перехватывать статические ресурсы (.html и т. д.), потому что правила DefaultServlet по умолчанию в родительском файле web.xml в контейнере tomcat в это время перезаписываются.
<!-- 举例 --> <url-pattern>/</url-pattern>
Решение:
- Добавить в файл конфигурации springmvc
<mvc:dafault-servlet-handler>
Тег будет определять объект DefaultServletHttpRequestHandler в контексте SpringMVC, фильтровать URL-адрес, входящий в DispatchServlet, и передавать статические ресурсы непосредственно в веб-контейнер для обработки. - настроить
<mvc:resources>
Метка, когда правило доступа указывает ресурс под отображением, оно перейдет в соответствующее место, чтобы найти его.
- Добавить в файл конфигурации springmvc
Spring MVC инкапсулирует данные в поля запроса
Метод: объявить формальный параметр Model, Map или ModelMap, затем вызвать метод addAttribute() или put().
Принцип. Конкретным типом во время выполнения является BindingAwareModelMap, BindingAwareModelMap наследует ExtendedModelMap, ExtendedModelMap наследует ModelMap и реализует интерфейс Model.
Привязка параметров запроса Spring MVC
-
API Servlet поддерживается по умолчанию в качестве параметра метода.
- Когда вам нужно использовать собственные объекты сервлета, такие как HttpServletRequest, HttpServletResponse и HttpSession, вы можете напрямую объявить и использовать их в методе обработчика.
-
Связать простые типы данных
- Чтобы связать параметры простого типа данных, вам нужно только напрямую объявить формальные параметры (имя параметра формального параметра и имя переданного параметра должны быть согласованы, рекомендуется использовать тип оболочки, когда имя параметра формального параметра и переданное имя параметра несовместимо, вы можете использовать аннотацию @RequestParam для ручного сопоставления)
-
Привязка параметров типа Pojo
- Чтобы получить параметры типа pojo, вы можете напрямую объявить формальные параметры.Тип — это тип Pojo.Имя параметра не имеет значения, но переданное имя параметра должно соответствовать имени атрибута Pojo.
-
Привязка параметров объекта-оболочки Pojo
- При привязке можно напрямую объявить формальные параметры
- Имя параметра и атрибут pojo совпадают. Если элемент данных не может быть расположен, данные дополнительно блокируются именем атрибута + "."
-
Параметр типа даты привязки
-
Определить преобразователь типа SpringMVC
implement Converter<String, Date>
-
Расширение реализует интерфейс
Переопределить общедоступный метод преобразования даты (источник строки)
-
Зарегистрируйте свою реализацию
<!--注册⾃定义类型转换器--> <bean id="conversionServiceBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.test.converter.DateConverter"></bean> </set> </property> </bean> <!-- ⾃动注册最合适的处理器映射器,处理器适配器(调⽤handler⽅法)--> <mvc:annotation-driven conversionservice="conversionServiceBean"/>
-
Поддержка стиля RESTful
RESTful
Restful — это стиль архитектуры веб-приложений, который не является ни стандартом, ни протоколом, он поддерживает стиль позиционирования ресурсов и операций с ними.
REST(Representational State Transfer)
Ресурс передает состояние в той или иной форме представления в сети
-
Ресурс: Объект в сети или определенная информация в сети.
-
Уровень представления: форма, в которой конкретно представлен ресурс, называется его уровнем представления (Representation), например, JSON, XML, JPEG и т. д.;
-
Передача состояния: изменение состояния. Реализовано через HTTP-глаголы.
Каждый раз, когда делается запрос, он представляет собой взаимодействие между клиентом и сервером. Протокол HTTP является протоколом без сохранения состояния, то есть все состояния сохраняются на стороне сервера. Следовательно, если клиент хочет управлять сервером, он должен передать определенный метод, чтобы «передача состояния» произошла на стороне сервера. И это преобразование построено на уровне представления, так что это «преобразование состояния уровня представления». В частности, в протоколе HTTP есть четыре глагола, которые выражают режим работы: GET, POST, PUT, DELETE. Они соответствуют четырем основным операциям: GET используется для получения ресурсов, POST используется для создания новых ресурсов, PUT используется для обновления ресурсов и DELETE используется для удаления ресурсов.
Поддержка Spring MVC для RESTful
-
Аннотация @PathVariable получает переменную пути в URL-адресе запроса в формате RESTful.
-
Настройте фильтр преобразования режима запроса springmvc, он проверит, есть ли в параметрах запроса параметр _method, и если да, то будет преобразован в соответствии с указанным режимом запроса
<filter> <filter-name>hiddenHttpMethodFilter</filter-name> <filterclass>org.springframework.web.filter.HiddenHttpMethodFilter</filterclass> </filter> <filter-mapping> <filter-name>hiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Взаимодействие Ajax Json
- От внешнего интерфейса к внутреннему: внешний ajax отправляет строку формата json, а серверная часть напрямую получает ее как параметр pojo, используя аннотацию @RequstBody.
- Серверная часть для внешнего интерфейса: серверная часть напрямую возвращает объект pojo, а внешний интерфейс напрямую получает его как объект json или строку, используя аннотацию @ResponseBody.
Передовая технология Spring MVC
Использование перехватчика
Сравнение слушателей, фильтров и перехватчиков
- Слушатель: Реализовано
javax.servlet.ServletContextListener
Серверная составляющая интерфейса, которая запускается при старте веб-приложения, инициализируется только один раз, затем все время работает мониторинг, и уничтожается при остановке веб-приложения- Функция 1: выполните некоторую работу по инициализации, запустите контейнер Spring в веб-приложении.
ContextLoaderListener
- Роль 2: Прослушивание определенных событий в Интернете, таких как
HttpSession
,ServletRequest
Создание и уничтожение переменных Создание, уничтожение и модификация переменных и т.д. Обработка может быть добавлена до и после определенных действий для достижения мониторинга, таких как подсчет онлайн-людей, использованиеHttpSessionLisener
Ждать.
- Функция 1: выполните некоторую работу по инициализации, запустите контейнер Spring в веб-приложении.
- Фильтр (Фильтр): фильтрует запрос запроса перед сервлетом, если он настроен как
/*
Может фильтровать доступ ко всем ресурсам (сервлет, статические ресурсы js/css и т. д.) - Перехватчик: это структура уровня представления, такая как SpringMVC, Struts и т. д. Он не будет перехватывать доступ к jsp/html/css/image и т. д., а только метод контроллера (обработчик) доступа.
- Перехватить один раз перед выполнением бизнес-логики обработчика
- Перехватить один раз, прежде чем логика обработчика будет выполнена, но страница не перепрыгнет
- Перехват один раз после перехода на страницу
Поток выполнения одного перехватчика
Поток выполнения нескольких перехватчиков
Примечание. Сначала настраивается Interceptor1.
пользовательский перехватчик
-
Определите класс, реализующий интерфейс HandlerInterceptor.
-
Переопределить соответствующий метод
-
Регистрация перехватчиков Spring MVC
<mvc:interceptors> <!--拦截所有handler--> <!--<bean class="com.lagou.edu.interceptor.MyIntercepter01"/>--> <mvc:interceptor> <!--配置当前拦截器的url拦截规则,**代表当前⽬录下及其⼦⽬录下的所有url--> <mvc:mapping path="/**"/> <!--exclude-mapping可以在mapping的基础上排除⼀些url拦截--> <!--<mvc:exclude-mapping path="/demo/**"/>--> <bean class="com.test.interceptor.MyIntercepter01"/> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.test.interceptor.MyIntercepter02"/> </mvc:interceptor> </mvc:interceptors>
Обрабатывать данные в составном формате
-
требуемые зависимости
<!--⽂件上传所需jar坐标--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>
-
Парсер загрузки файла конфигурации
<!--配置⽂件上传解析器,id是固定的multipartResolver--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--设置上传⼤⼩,单位字节--> <property name="maxUploadSize" value="1000000000"/> </bean>
-
параметр объявления дескриптора
MultipartFile uploadFile
, имя параметра совпадает с именем загружаемого файла
обработка исключения в контроллере
-
Перехватывать исключения под текущим контроллером
Создайте метод под текущим контроллером, вставьте
@ExceptionHandler
Аннотировать, объявить тип исключения и обработать его -
глобальная обработка исключений
создать класс, вставить
@ControllerAdvice
Аннотация, в которой можно создать несколько методов для перехвата разных исключений.
Передача данных запроса перекрестного перенаправления на основе атрибутов Flash
Решаем проблему отсутствия параметров перенаправления:
-
Ручные параметры сварки
Недостатки: относится к запросу на получение, а длина параметра и безопасность ограничены.
-
объявить параметры
RedirectAttributes redirectAttributes
, используя redirectAttributes.addAttribute(ключ, значение)Недостатки: те же, что и выше
-
объявить параметры
RedirectAttributes redirectAttributes
, используйте redirectFlashAttributes.addAttribute(key, value), этот метод устанавливает атрибут типа flash, который будет временно храниться в сессии, и будет уничтожен после перехода на страницу.
Анализ исходного кода Spring MVC
Структура наследования Front Controller DispatcherServlet
Анализ исходного кода doDispatch()
Исходный код doDispatch() выглядит следующим образом.
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 1 检查是否是文件上传的请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 2 取得处理当前请求的Controller,这里也称为Handler,即处理器
// 这里并不是直接返回 Controller,而是返回 HandlerExecutionChain 请求处理链对象,该对象封装了Handler和Inteceptor
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果 handler 为空,则返回404
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 3 获取处理请求的处理器适配器 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 处理 last-modified 请求头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 4 实际处理器处理请求,返回结果视图对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 结果视图对象的处理
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}catch (Exception ex) {
dispatchException = ex;
}catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 5 跳转页面,渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}catch (Exception ex) {
//最终会调用HandlerInterceptor的afterCompletion 方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}catch (Throwable err) {
//最终会调用HandlerInterceptor的afterCompletion 方法
triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err));
}finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
Анализ исходного кода getHandler()
пытаясь получить отBeanNameUrlHandlerMapping
а такжеRequestMappingHandlerMapping
Получите цепочку выполнения, которая может обработать текущий запрос в
Анализ исходного кода gethandlerAdapter()
Пройдитесь по каждому HandlerAdapter, чтобы увидеть, какой адаптер поддерживает обработку текущего обработчика.
Анализ исходного кода ha.handle()
ha.handle() вызывает handleInternal()
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 判断当前是否需要支持在同一个session中只能线性地处理请求
if (this.synchronizeOnSession) {
// 获取当前请求的session对象
HttpSession session = request.getSession(false);
if (session != null) {
// 为当前session生成一个唯一的可以用于锁定的key
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 对HandlerMethod进行参数等的适配处理,并调用目标handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// No HttpSession available -> no mutex necessary
// 如果当前不存在session,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// No synchronization on session demanded at all...
// 如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
invokeHandlerMethod()
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中配置的InitBinder,用于进行参数的绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller中配置的ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 将handlerMethod封装为一个ServletInvocableHandlerMethod对象
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 设置当前容器中配置的所有ArgumentResolver
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 设置当前容器中配置的所有ReturnValueHandler
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 将前面创建的WebDataBinderFactory设置到ServletInvocableHandlerMethod中
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 这里initModel()方法主要作用是调用前面获取到的@ModelAttribute标注的方法,
// 从而达到@ModelAttribute标注的方法能够在目标Handler调用之前调用的目的
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
// 还会判断是否需要将FlashAttributes封装到新的请求中
return getModelAndView(mavContainer, modelFactory, webRequest);
} finally {
webRequest.requestCompleted();
}
}
invokeAndHandle()
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 对目标handler的参数进行处理,并且调用目标handler
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置相关的返回状态
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
invokeForRequest()
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 将request中的参数转换为当前handler的参数形式
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 这里doInvoke()方法主要是结合处理后的参数,使用反射对目标方法进行调用
return doInvoke(args);
}
Анализ исходного кода processDispatchResult()
processDispatchResult() вызывает метод render()
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
// 根据视图解析器解析出View视图对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'");
}
} else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 调用 view 对象的render方法
view.render(mv.getModelInternal(), request, response);
} catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
Метод resolvViewName()
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
// 视图解析器解析出View视图对象
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
-
resolveViewName() вызывает метод createView()
-
Метод createView() будет определять, следует ли перенаправлять, пересылать ли и т. д. в процессе разбора объекта View.Разные ситуации инкапсулируют разные реализации View.
-
Вызовите метод super.createView()
-
createView() вызывает loadView()
-
loadView() вызывает buildView()
-
Преобразование имен логических представлений в имена физических представлений в buildView()
view.render()
public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("View " + formatViewName() + ", model " + (model != null ? model : Collections.emptyMap()) + (this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
// 渲染数据
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
renderMergedOutputModel()
protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
// 把modelMap中的数据暴露到request域中
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
} else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
}
rd.forward(request, response);
}
}
exposeModelAsRequestAttributes()
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
model.forEach((name, value) -> {
if (value != null) {
// 将数据设置到请求域中
request.setAttribute(name, value);
} else {
request.removeAttribute(name);
}
});
}
Инициализация компонента Spring MVC
-
В DispatcherServlet определены девять атрибутов, каждый атрибут соответствует компоненту.
/** MultipartResolver used by this servlet. */ // 多部件解析器 @Nullable private MultipartResolver multipartResolver; /** LocaleResolver used by this servlet. */ // 区域化 国际化解析器 @Nullable private LocaleResolver localeResolver; /** ThemeResolver used by this servlet. */ // 主题解析器 @Nullable private ThemeResolver themeResolver; /** List of HandlerMappings used by this servlet. */ // 处理器映射器组件 @Nullable private List<HandlerMapping> handlerMappings; /** List of HandlerAdapters used by this servlet. */ // 处理器适配器组件 @Nullableprivate List<HandlerAdapter> handlerAdapters; /** List of HandlerExceptionResolvers used by this servlet. */ // 异常解析器组件 @Nullable private List<HandlerExceptionResolver> handlerExceptionResolvers; /** RequestToViewNameTranslator used by this servlet. */ // 默认视图名转换器组件 @Nullable private RequestToViewNameTranslator viewNameTranslator; /** FlashMapManager used by this servlet. */ // flash属性管理组件 @Nullable private FlashMapManager flashMapManager; /** List of ViewResolvers used by this servlet. */ // 视图解析器 @Nullable private List<ViewResolver> viewResolvers;
- Все девять основных компонентов определяют интерфейс.Интерфейс на самом деле является спецификацией, определяющей компонент.Например, ViewResolver, HandlerAdapter и т. д. — все это интерфейсы.
-
Время инициализации девяти компонентов
-
onRefresh() в DispatcherServlet, который инициализирует девять компонентов.
protected void onRefresh(ApplicationContext context) { // 初始化策略 initStrategies(context); }
-
initStrategies()
protected void initStrategies(ApplicationContext context) { // 多文件上传的组件 initMultipartResolver(context); // 初始化本地语言环境 initLocaleResolver(context); // 初始化模板处理器 initThemeResolver(context); // 初始化HandlerMapping initHandlerMappings(context); // 初始化参数适配器 initHandlerAdapters(context); // 初始化异常拦截器 initHandlerExceptionResolvers(context); // 初始化视图预处理器 initRequestToViewNameTranslator(context); // 初始化视图转换器 initViewResolvers(context); // 初始化 FlashMap 管理器 initFlashMapManager(context); }
-
-
initHandlerMappings(context)
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. // 在IoC 容器中按照 HandlerMapping.class 找到所有的HandlerMapping Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { // 否则在ioc中按照固定名称id(handlerMapping)去找 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { // 最后还为空则按照默认策略生成 this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
getDefaultStrategies()
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { String key = strategyInterface.getName(); // DispatcherServlet.properties String value = defaultStrategies.getProperty(key); if (value != null) { String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List<T> strategies = new ArrayList<>(classNames.length); for (String className : classNames) { try { Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); Object strategy = createDefaultStrategy(context, clazz); strategies.add((T) strategy); } catch (ClassNotFoundException ex) { throw new BeanInitializationException("Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", ex); } catch (LinkageError err) { throw new BeanInitializationException("Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", err); } } return strategies; } else { return new LinkedList<>(); } }
DispatcherServlet.properties
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
-
initMultipartResolver()
private void initMultipartResolver(ApplicationContext context) { try { // MULTIPART_RESOLVER_BEAN_NAME = mulitipartResolver this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); if (logger.isTraceEnabled()) { logger.trace("Detected " + this.multipartResolver); } else if (logger.isDebugEnabled()) { logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName()); } } catch (NoSuchBeanDefinitionException ex) { // Default is no multipart resolver. this.multipartResolver = null; if (logger.isTraceEnabled()) { logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared"); } } }
- Примечание. При инициализации составного распознавателя объект должен быть зарегистрирован в соответствии с идентификатором (multipartResolver).
Интеграция SSM-фреймворка
обработать
-
импортировать зависимости
-
Конфигурация файла конфигурации Spring applicationContext.xml
- сканирование пакетов
<context:component-scan base-package="com.test.xx"/>
- bean dataSource
- Соответствующие параметры могут быть извлечены, а затем могут быть введены внешние файлы ресурсов.
<context:property-placeholder location="classpath:jdbc.properties"/>
- Соответствующие параметры могут быть извлечены, а затем могут быть введены внешние файлы ресурсов.
- bean sqlSessionFactory
- Оказывается, содержимое в SqlMapConfig.xml, необходимое для построения sqlSessionFactory в mybaits, можно задать, ища соответствующие свойства под этим тегом
- Динамический прокси-объект Mapper передается Spring для управления
- Управление транзакциями: bean transactionManager
- Драйвер аннотации управления транзакциями
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--包扫描--> <context:component-scan base-package="com.test.xx"/> <!--数据库连接池以及事务管理都交给Spring容器来完成--> <!--引⼊外部资源⽂件--> <context:property-placeholder location="classpath:jdbc.properties"/> <!--第三⽅jar中的bean定义在xml中--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!--SqlSessionFactory对象应该放到Spring容器中作为单例对象管理 原来mybaits中sqlSessionFactory的构建是需要素材的:SqlMapConfig.xml中的内容 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--别名映射扫描--> <property name="typeAliasesPackage" value="com.test.xx.pojo"/> <!--数据源dataSource--> <property name="dataSource" ref="dataSource"/> </bean> <!--Mapper动态代理对象交给Spring管理,我们从Spring容器中直接获得Mapper的代理对 象--> <!--扫描mapper接⼝,⽣成代理对象,⽣成的代理对象会存储在ioc容器中--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--mapper接⼝包路径配置--> <property name="basePackage" value="com.test.xx.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> <!--事务管理--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--事务管理注解驱动--> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
- сканирование пакетов
-
файл конфигурации springmvc.xml
- Контроллер сканирования
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/springcontext.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--扫描controller--> <context:component-scan base-package="com.lagou.edu.controller"/> <mvc:annotation-driven/> </beans>
Контейнер Spring и контейнер SpringMVC являются иерархическими (родительский контейнер)
Контейнер Spring: сервисный объект + дао-объект
Контейнер SpringMVC: объект контроллера может ссылаться на объекты в контейнере Spring.
-
web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext*.xml</param-value> </context-param> <!--spring框架启动--> <listener> <listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass> </listener> <!--springmvc启动--> <servlet> <servlet-name>springmvc</servlet-name><servletclass>org.springframework.web.servlet.DispatcherServlet</servletclass> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
Код каждого уровня Дао, Сервиса и Контроллера совершенен