SpringMVC [валидатор, унифицированная обработка исключений, RESTful, перехватчик]

Java задняя часть Spring

предисловие

Основные моменты знаний, объясненные в этом сообщении в блоге, следующие:

  • валидатор
  • Унифицированная обработка исключений
  • RESTful
  • перехватчик

Validation

В нашем Struts2 мы наследуем ActionSupport для реализации проверки... У него есть два способа реализации функции проверки

  • рукописный код
  • XML-конфигурация
    • Эти два метода также могут быть конкретными методами обработки или всем действием.

В то время как SpringMVC использует спецификацию проверки JSR-303 (часть спецификации javaEE6),springmvc использует Hibernate Validator (не связанный с Hibernate ORM)

Быстрый старт

импортный пакет jar

这里写图片描述

Настроить валидатор



	
	<!-- 校验器 -->
	<bean id="validator"
		class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
		<!-- 校验器 -->
		<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
		<!-- 指定校验使用的资源文件,如果不指定则默认使用classpath下的ValidationMessages.properties -->
		<property name="validationMessageSource" ref="messageSource" />
	</bean>


Проверьте конфигурацию файла на наличие сообщений об ошибках


	<!-- 校验错误信息配置文件 -->
	<bean id="messageSource"
		class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
		<!-- 资源文件名 -->
		<property name="basenames">
			<list>
				<value>classpath:CustomValidationMessages</value>
			</list>
		</property>
		<!-- 资源文件编码格式 -->
		<property name="fileEncodings" value="utf-8" />
		<!-- 对资源文件内容缓存时间,单位秒 -->
		<property name="cacheSeconds" value="120" />
	</bean>

Добавить в WebBindingInitializer для привязки пользовательских параметров


	<!-- 自定义webBinder -->
	<bean id="customBinder"
		class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
		<!-- 配置validator -->
		<property name="validator" ref="validator" />
	</bean>

наконец-то добавлен в адаптер


	<!-- 注解适配器 -->
	<bean
		class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
		<!-- 在webBindingInitializer中注入自定义属性编辑器、自定义转换器 -->
		<property name="webBindingInitializer" ref="customBinder"></property>
	</bean>

Создайте файл конфигурации CustomValidationMessages

这里写图片描述

определить правила

package entity;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Date;

public class Items {
    private Integer id;

    //商品名称的长度请限制在1到30个字符
    @Size(min=1,max=30,message="{items.name.length.error}")
    private String name;

    private Float price;

    private String pic;

    //请输入商品生产日期
    @NotNull(message="{items.createtime.is.notnull}")
    private Date createtime;

    private String detail;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Float getPrice() {
        return price;
    }

    public void setPrice(Float price) {
        this.price = price;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic == null ? null : pic.trim();
    }

    public Date getCreatetime() {
        return createtime;
    }

    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail == null ? null : detail.trim();
    }
}

тестовое задание:




<%--
  Created by IntelliJ IDEA.
  User: ozc
  Date: 2017/8/11
  Time: 9:56
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试文件上传</title>
</head>
<body>


<form action="${pageContext.request.contextPath}/validation.action" method="post" >
    名称:<input type="text" name="name">
    日期:<input type="text" name="createtime">
    <input type="submit" value="submit">
</form>

</body>
</html>



Контроллеру необходимо добавить аннотацию @Validation к параметрам проверки... получить объект BindingResult...


    @RequestMapping("/validation")
    public void validation(@Validated Items items, BindingResult bindingResult) {

        List<ObjectError> allErrors = bindingResult.getAllErrors();
        for (ObjectError allError : allErrors) {
            System.out.println(allError.getDefaultMessage());
        }

    }

Так как я отключил преобразователь даты при тестировании, мне выдается сообщение о том, что строка не может быть преобразована в дату, но проверка имени уже отсутствует...

这里写图片描述


Проверка пакетов

Групповая проверка предназначена для того, чтобы сделать нашу проверку более гибкой.Нет необходимости проверять все свойства нашей текущей конфигурации, но текущий метод должен проверять только некоторые свойства.. Затем, в этот момент, мы можем использовать групповую проверку...

шаг:

  • Определить интерфейс группы [в основном идентификация]
  • Зависит от того, к какой группе относится правило проверки
  • Определите использование группировки проверок в методе Controller.

这里写图片描述

这里写图片描述

这里写图片描述


Унифицированная обработка исключений

Прежде чем использовать SSH, мы также настроили унифицированную обработку исключений при использовании Struts2...

Вот что было сделано в то время:

  • Пользовательские исключения на сервисном уровне
  • Также настройте исключения в слое действий
  • Нас не волнует ненормальность слоя Дао [потому что мы не можем это контролировать, ненормальность слоя Дао слишком смертоносна]
  • Уровень службы выдает исключение, действие перехватывает исключение уровня службы и решает, следует ли пропустить запрос через исключение, созданное службой.
  • Если не пройдет, то сгенерировать исключение Action
  • Определите глобальное представление в файле конфигурации Struts, на странице отобразится сообщение об ошибке.

Подробности можно найти по адресу:blog.CSDN.net/hon_3 has/Aretti…

Так что же представляет собой наше унифицированное решение для обработки исключений на этот раз? ? ? ?

Мы знаем, что исключения в Java можно разделить на две категории.

  • исключение времени компиляции
  • исключение времени выполнения

Мы не можем контролировать исключения во время выполнения, и мы можем устранить исключения во время выполнения только за счет качества кода, детального тестирования во время тестирования системы и т. д.

Для исключений во время компиляции мы можем вручную обрабатывать исключения в коде, которые могут быть перехвачены с помощью try/catch и переданы вверх.

Мы можем изменить свое мышление и настроить модульную информацию об исключениях, например:Исключение для категории продукта


public class CustomException extends Exception {
	
	//异常信息
	private String message;
	
	public CustomException(String message){
		super(message);
		this.message = message;
		
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
	
	

}

Когда мы просмотрели исходный код Spring, мы обнаружили, что когда интерфейсный контроллер DispatcherServlet выполняет HandlerMapping и вызывает HandlerAdapter для выполнения Handler, если он сталкивается с исключением,Настройте единый обработчик исключений в системе и напишите собственный системный код обработки исключений. .

这里写图片描述

这里写图片描述

Мы также можем чему-то научиться и определить унифицированный класс обработчика для обработки исключений...

Определить унифицированный класс обработчика исключений


public class CustomExceptionResolver implements HandlerExceptionResolver  {

	//前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常就会执行此方法
	//handler最终要执行的Handler,它的真实身份是HandlerMethod
	//Exception ex就是接收到异常信息
	@Override
	public ModelAndView resolveException(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex) {
		//输出异常
		ex.printStackTrace();
		
		//统一异常处理代码
		//针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面展示
		//异常信息
		String message = null;
		CustomException customException = null;
		//如果ex是系统 自定义的异常,直接取出异常信息
		if(ex instanceof CustomException){
			customException = (CustomException)ex;
		}else{
			//针对非CustomException异常,对这类重新构造成一个CustomException,异常信息为“未知错误”
			customException = new CustomException("未知错误");
		}

		//错误 信息
		message = customException.getMessage();
		
		request.setAttribute("message", message);

		
		try {
			//转向到错误 页面
			request.getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(request, response);
		} catch (ServletException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return new ModelAndView();
	}

}

Настройка унифицированных обработчиков исключений

	<!-- 定义统一异常处理器 -->
	<bean class="cn.itcast.ssm.exception.CustomExceptionResolver"></bean>

这里写图片描述

REST-поддержка

Возможно, мы слышали термин RESTful, когда изучали веб-сервис, и сравнивали его с SOAP в то время... Так что же такое RESTful? ? ?

Концепция разработки программного обеспечения RESTful (Representational State Transfer), RESTful имеет очень хорошую интерпретацию http.

Архитектура называется архитектурой RESTful, если она поддерживает RESTful...

Следующие статьи для нашего понимания:

Вууху. Руан Ифэн.com/blog/2011/0…

Основываясь на приведенном выше объяснении, давайте резюмируем, что такое архитектура RESTful:

  • (1)Каждый URI представляет ресурс;
  • (2)Между клиентом и сервером проходит какой-то уровень представления этого ресурса;
  •    (3)Клиент работает с ресурсами на стороне сервера с помощью четырех глаголов HTTP для достижения «преобразования состояния уровня представления»..

Понимание идемпотентности RESTful:Woohoo.OSCHINA.net/translate/Боюсь…

Проще говоря, если объект изменяется во время запроса (например, в Java изменяются свойства), то это неидемпотентно. Если запрос повторяется много раз, а результат остается прежним, то он идемпотентный.

PUT используется для идемпотентных запросов, поэтому при обновлении все атрибуты прописываются полностью, поэтому после множественных запросов другие наши атрибуты не изменятся

В приведенной выше статье идемпотентность переводится как «единство государства». Это лучше понять.

На самом деле общая архитектура не полностью поддерживает RESTful, поэтомуПока наша система поддерживает некоторые функции RESTful, мы обычно называем ее поддержкой архитектуры RESTful....

RESTful реализация URL

URL-адрес http без RESTful: http://localhost:8080/items/editItems.action?id=1&....

URL-адрес RESTful краток: http://localhost:8080/items/editItems/1

Измените конфигурацию DispatcherServlet.

Из приведенного выше мы можем обнаружить, что URL-адрес не имеет суффикса .action, поэтому нам нужно изменить конфигурацию основного распределителя.


	<!-- restful的配置 -->
	<servlet>
		<servlet-name>springmvc_rest</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- 加载springmvc配置 -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<!-- 配置文件的地址 如果不配置contextConfigLocation, 默认查找的配置文件名称classpath下的:servlet名称+"-serlvet.xml"即:springmvc-serlvet.xml -->
			<param-value>classpath:spring/springmvc.xml</param-value>
		</init-param>

	</servlet>
	<servlet-mapping>
		<servlet-name>springmvc_rest</servlet-name>
		<!-- rest方式配置为/ -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>

Используйте аннотацию PathVariable в контроллере для привязки соответствующих параметров.


	//根据商品id查看商品信息rest接口
	//@RequestMapping中指定restful方式的url中的参数,参数需要用{}包起来
	//@PathVariable将url中的{}包起参数和形参进行绑定
	@RequestMapping("/viewItems/{id}")
	public @ResponseBody ItemsCustom viewItems(@PathVariable("id") Integer id) throws Exception{
		//调用 service查询商品信息
		ItemsCustom itemsCustom = itemsService.findItemsById(id);
		
		return itemsCustom;
		
	}

Когда DispatcherServlet перехватывает все запросы в начале /, доступ к статическим ресурсам сообщает об ошибке: нам нужно настроить анализ статических ресурсов


	<!-- 静态资源 解析 -->
	<mvc:resources location="/js/" mapping="/js/**" />
	<mvc:resources location="/img/" mapping="/img/**" />

/**Это означает, что независимо от того, сколько слоев есть, он будет разобран./*Представляет все ресурсы текущего слоя.


Перехватчики SpringMVC

В Struts2 нашим ядром на тот момент был перехватчик, оказалось, что в SpringMVC тоже есть перехватчик.

Пользователь запрашивает DispatherServlet, DispatherServlet вызывает HandlerMapping, чтобы найти Handler, HandlerMapping возвращает перехваченную цепочку (несколько перехватов), а перехватчик в springmvc инициируется через HandlerMapping.

Интерфейс, реализующий перехватчик:


public class HandlerInterceptor1 implements HandlerInterceptor {

	//在执行handler之前来执行的
	//用于用户认证校验、用户权限校验
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		
		System.out.println("HandlerInterceptor1...preHandle");
		
		//如果返回false表示拦截不继续执行handler,如果返回true表示放行
		return false;
	}
	//在执行handler返回modelAndView之前来执行
	//如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("HandlerInterceptor1...postHandle");
		
	}
	//执行handler之后执行此方法
	//作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
	//实现 系统 统一日志记录
	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("HandlerInterceptor1...afterCompletion");
	}

}

Настроить перехватчик


	<!--拦截器 -->
	<mvc:interceptors>
		<!--多个拦截器,顺序执行 -->
		<!-- <mvc:interceptor>
			<mvc:mapping path="/**" />
			<bean class="cn.itcast.ssm.controller.interceptor.HandlerInterceptor1"></bean>
		</mvc:interceptor>
		<mvc:interceptor>
			<mvc:mapping path="/**" />
			<bean class="cn.itcast.ssm.controller.interceptor.HandlerInterceptor2"></bean>
		</mvc:interceptor> -->
		
		<mvc:interceptor>
			<!-- /**可以拦截路径不管多少层 -->
			<mvc:mapping path="/**" />
			<bean class="cn.itcast.ssm.controller.interceptor.LoginInterceptor"></bean>
		</mvc:interceptor>
	</mvc:interceptors>

Порядок выполнения теста

Если оба перехватчика выпущены


测试结果:
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle

HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle

HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion

总结:
执行preHandle是顺序执行。
执行postHandle、afterCompletion是倒序执行

Выпуск №1 и №2 без выпуска


测试结果:
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion

总结:
如果preHandle不放行,postHandle、afterCompletion都不执行。
只要有一个拦截器不放行,controller不能执行完成

№ 1 не выпущен и № 2 не выпущен


测试结果:
HandlerInterceptor1...preHandle
总结:
只有前边的拦截器preHandle方法放行,下边的拦截器的preHandle才执行。

Требования к перехватчику журнала или перехватчику исключений

  • Поместите перехватчик журнала или перехватчик исключений на первое место в цепочке перехватчиков, и метод preHandle будет освобожден.

Приложение-перехватчик — аутентификация

перехватчик


public class LoginInterceptor implements HandlerInterceptor {

	//在执行handler之前来执行的
	//用于用户认证校验、用户权限校验
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		
		//得到请求的url
		String url = request.getRequestURI();
		
		//判断是否是公开 地址
		//实际开发中需要公开 地址配置在配置文件中
		//...
		if(url.indexOf("login.action")>=0){
			//如果是公开 地址则放行
			return true;
		}
		
		//判断用户身份在session中是否存在
		HttpSession session = request.getSession();
		String usercode = (String) session.getAttribute("usercode");
		//如果用户身份在session中存在放行
		if(usercode!=null){
			return true;
		}
		//执行到这里拦截,跳转到登陆页面,用户进行身份认证
		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
		
		//如果返回false表示拦截不继续执行handler,如果返回true表示放行
		return false;
	}
	//在执行handler返回modelAndView之前来执行
	//如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("HandlerInterceptor1...postHandle");
		
	}
	//执行handler之后执行此方法
	//作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
	//实现 系统 统一日志记录
	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("HandlerInterceptor1...afterCompletion");
	}

}

Controller


@Controller
public class LoginController {
	
	
	//用户登陆提交方法
	@RequestMapping("/login")
	public String login(HttpSession session, String usercode,String password)throws Exception{
		
		//调用service校验用户账号和密码的正确性
		//..
		
		//如果service校验通过,将用户身份记录到session
		session.setAttribute("usercode", usercode);
		//重定向到商品查询页面
		return "redirect:/items/queryItems.action";
	}
	
	//用户退出
	@RequestMapping("/logout")
	public String logout(HttpSession session)throws Exception{
		
		//session失效
		session.invalidate();
		//重定向到商品查询页面
		return "redirect:/items/queryItems.action";
		
	}
	

}

Суммировать

  • Метод проверки с использованием Spring заключается в добавлении объявлений аннотаций перед проверяемыми свойствами.
  • Добавьте аннотацию @Validation к параметрам метода в контроллере. Затем SpringMVC поможет нам обработать его внутри (создать соответствующий bean-компонент, загрузить файл конфигурации)
  • BindingResult может получить сообщение об ошибке проверки
  • Групповая проверка должна сделать нашу проверку более гибкой: метод должен проверять это свойство, а методу не нужно проверять это свойство. Мы можем использовать групповую проверку.
  • Для обработки исключений SpringMVC использует унифицированный класс обработчика исключений. Реализует интерфейс HandlerExceptionResolver.
  • Модули подразделяются на несколько классов исключений, каждый из которых обрабатывается нашим унифицированным классом обработчика исключений.
  • Для спецификации RESTful мы можем использовать то, что просто поддерживает SpringMVC.Изменение interception.action SpringMVC является произвольным. В то же время, если это статический файл ресурсов, мы должны настроить его так, чтобы он не перехватывался.
  • Для параметров URL-адресаМы можем использовать @PathVariable для переноса {} в URL-адрес с параметрами и формальными параметрами для привязки.
  • Перехватчики SpringMVC аналогичны перехватчикам Struts2. Однако перехватчики SpringMVC проще настроить, чем Struts2.
    • что касается ихПоследовательность вызова цепочки перехватчиков ничем не отличается от последовательности вызова Filter.

Если в статье есть ошибки, пожалуйста, исправьте меня и поделитесь друг с другом. Учащиеся, привыкшие читать технические статьи в WeChat и желающие получить больше ресурсов по Java, могутОбратите внимание на публичный аккаунт WeChat: Java3y