一.什么是MVC模式?
① Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是该模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
② View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
③ Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。
二.Model1和Model2的区别
Model是JSP+JavaBeans开发模式;JSP既充当View视图层,又充当Controller控制层;所有的代码都写到JSP文件中,代码利用率很低;
Model2是JSP+Servlet+JavaBeans开发模式;JSP文件中几乎不写业务代码,只充当View视图层;
三.DispatcherServlet初始化:
<!--配置DispatcherServlet 核心其实就是一个servlet 重点: springmvc的前端控制器-->
<servlet>
<servlet-name>SpringMVCCore</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置springmvc的配置文件 如果不配置 默认加载/WRB_INF/servlet名称—serlvet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
<!-- 配置 servlet 的对象的创建时间点:应用加载时创建。
取值只能是非 0 正整数,表示启动顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
1.初始化时机:
一般情况下,第一次发起客户请求的时候才会调用初始化init方法,如果配置了load-on-startup>0 则项目启动时就会初始化;
2.初始化源码
初始化时会调DispatcherServlet的父类HttpServletBean中的init()方法,init()方法中就实现了SpringMVC的整个启动流程
public final void init() throws ServletException {
// 获取web.xml中DispatcherServlet 的属性contextConfigLocation并进行赋值
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
initServletBean();
}
org.springframework.web.servlet.FrameworkServlet#initServletBean下的initWebApplicationContext方法
protected WebApplicationContext initWebApplicationContext() {
//获取Spring上下文对象
//Spring上下文对象来源于:如果时spring+springMvc架构的项目,Spring上下文对象(rootContext )在此之前由ContextLoaderListener初始化并注册在ServletContext中,详见Tomcat基础九.Tomcat是如何启动Spring项目的?
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
// //创建WebApplicationContext-springMvc上下文,并将spring上下文对象设置为父容器;
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
//刷新springMvc上下文对象,DispatcherServlet初始化九大组件
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
//默认XmlWebApplication
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
//调用无参构造函数创建 ConfigurableWebApplicationContext
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//设置环境参数
wac.setEnvironment(getEnvironment());
//设置springmvc的WebApplicationContext容器的父容器为spring的ApplicationContext容器
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
//设置‘springmvc.xml’配置文件路径
wac.setConfigLocation(configLocation);
}
//配置刷新容器
configureAndRefreshWebApplicationContext(wac);
return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
//就是把它的id由org.springframework.web.context.support.XmlWebApplicationContext@c335c0c
//改成了基于Servlet名字的org.springframework.web.context.WebApplicationContext:/dispatcher,
//官方文档说是替换成一个更有意义的ID
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
//配置ServletContext,放进去的是ApplicationContextFacade对象
wac.setServletContext(getServletContext());
//配置ServletConfig,StandardWrapperFacade对象
wac.setServletConfig(getServletConfig());
//配置命名空间,默认是[Servlet的名字]-servlet
wac.setNamespace(getNamespace());
//添加监听器,监听ContextRefreshedEvent事件,该事件会在WebApplicationContext初始化完毕或者主动调用
//refresh()方法时触发,比如Spring容器加载完context配置文件后就会触发,所以会触发多次,触发后调用
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
//补全之前创建的StandardServletEnvironment对象
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
//空方法,也是供子类扩展的,目前还没有使用
postProcessWebApplicationContext(wac);
//主要是为了在调用refresh方法之前做一些准备工作
applyInitializers(wac);
//主动调用refresh方法,触发上面刚添加的监听器
wac.refresh();
}
DispatcherServlet初始化初始化九大组件
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void onRefresh(ApplicationContext context) {
//将初始化策略和onRefresh方法分开,让子类灵活扩展
initStrategies(context);
}
//这也正是SpringMVC最为核心的部分
protected void initStrategies(ApplicationContext context) {
//处理文件上传请求
initMultipartResolver(context);
//国际化处理
initLocaleResolver(context);
//解析主题配置
initThemeResolver(context);
//把request映射到具体的处理器上(负责找handler)
initHandlerMappings(context);
//调用处理器来处理(负责让handler干活)
initHandlerAdapters(context);
//解析过程出了问题就交给我
initHandlerExceptionResolvers(context);
//从request中获取viewName
initRequestToViewNameTranslator(context);
//将我们返回的视图名字符串(如hello.jsp)解析为具体的view对象
initViewResolvers(context);
//管理FlashMap,主要用于redirect
initFlashMapManager(context);
}
}
四.Controller控制器实现的3种方式
详见文章:https://blog.csdn.net/qq_38225558/article/details/83271233
五.DispatcherServlet-请求的处理流程
1.解析web.xml 得到前端控制器 DispatcherServlet(其实就是一个servlet);
2.执行DispatcherServlet的doService方法(其实就是servlet中的service方法);
3.DispatcherServlet遍历所有的处理映射器HandlerMapping,选择适配的HandlerMapping获得处理器执行链HandlerExecutionChain对象;
A.Controller控制器实现的方式不同,使用的HandlerMapping不同,我们一般用的是RequestMappingHandlerMapping;
B. HandlerExecutionChain对象,包括 Handler 对象以及 Handler 对象对应的HandlerInterceptor拦截器;
C.Handler对象 的类型是HandlerMethod,HandlerMethod 对象中包含了能够处理请求的 bean(具体的controller类) 及方法信息;
4.DispatcherServlet 根据获得的 HandlerExecutionChain中的Handler对象,遍历所有的HandlerAdapter选择一个合适的HandlerAdapter;
A.根据controller的实现方式不同,对应的适配器不同:
HttpRequestHandler适配实现了HttpRequestHandler接口的handler
SimpleServletHandlerAdapter:适配实现了Servlet接口的handler
RequestMappingHandlerAdapter:适配通过@RequestMapping注解的handler。会封装为HandleMethod对象
SimpleControllerHandlerAdapter:适配实现了Controller接口的handler
5.DispatcherServlet 执行过滤器HandlerInterceptor拦截器前置过滤方法;
6.DispatcherServlet 中执行HandlerAdapter的hadle方法,传参为Handler对象,request,response;处理参数,执行controller方法,得到返回结果,将返回结果构建成ModelAndView对象,返回给DispatcherServlet ;
A.提取 Request 中的参数值和handler对象中的方法参数类型,遍历所有的方法参数处理器,选择适配的方法参数处理器HandlerMethodArgumentResolver来解析参数;#常见的HandlerMethodArgumentResolver
B.参数值解析完成之后,通过反射调用处理请求的目标方法,获取方法的返回值;
C遍历所有的HandlerMethodReturnValueHandler,选择适配的HandlerMethodReturnValueHandler处理返回值;#常见的HandlerMethodReturnValueHandler、ModelAndViewMethodReturnValueHandler
D.如果方法返回值标注了@ResponseBody,则会使用RequestResponseBodyMethodProcessor处理返回值时,直接将结果返回给客户端,返回的ModelAndView为null;
如果方法返回值返回了ModelAndView对象,则使用ModelAndViewMethodReturnValueHandler处理返回值,返回的modelAndView不为空;
E.将返回结果构建为ModelAndView对象;
7.DispatcherServlet 执行过滤器HandlerInterceptor后置过滤方法;
8.根据返回的 ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到 Spring 容器中的 ViewResolver)返回给 DispatcherServlet ;
https://www.yii666.com/article/67626.html
SpringBoot自动装配的WebMvcAutoConfiguration类中帮我们配置了InternalResourceViewResolver类,这个类中会从配置文件中获取视图解析器的前缀和后缀,所以我们可以在application.properties文件配置此视图解析器
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
SpringBoot官方是推荐使用thymeleaf作为优选的视图解析器,所以SpringBoot对Thymeleaf的支持非常好,这里仅仅演示SpringBoot如何选用Thymeleaf作用默认视图解析器。
第一步:导入Thymeleaf的依赖
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
第二步:创建存放Thymeleaf模板文件夹,在Resources目录下创建templates目录,创建index.html
SpringBoot中使用Freemark:
第一步:导入Maven依赖
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
第二步:创建存放Freemark模板文件夹,在Resources目录下创建templates目录,创建index.ftl
9.ViewResolver 结合 Model 和 View,来渲染视图
10.将渲染结果返回给客户端
六.处理流程:源码解析
参考文章:https://blog.csdn.net/weixin_56644618/article/details/127594065
在bean初始化的时候,AbstractHandlerMethodMapping抽象类做的一些动作;
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
//获取所有的bean名字并遍历
for (String beanName : getCandidateBeanNames()) {
// 判断不是已 scopedTarget 开头
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//处理所有的bean
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
//获取所有的bean名字
protected String[] getCandidateBeanNames() {
return (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
}
//处理所有的bean
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
// 获取具体的类型
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// beanType不是null 并且 类型是存在 @Controller 或者 @RequestMapping 注解
if (beanType != null && isHandler(beanType)) {
//探测bean中所有的方法,将方法和bean和处理映射器HandlerMapping绑定,后边通过request找到合适的处理映射器;
detectHandlerMethods(beanName);
}
}
}
DispatcherServlet处理请求过程:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//请求对象
HttpServletRequest processedRequest = request;
//处理器执行链对象
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//获取异步处理管理器,servlet3.0后支持异步处理,可以在子线程中响应用户请求
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
//模型和视图
ModelAndView mv = null;
//异常对象
Exception dispatchException = null;
try {
//①:解析multipart类型的请求,上传文件用的就是multipart类型的请求方式
processedRequest = checkMultipart(request);
//用来标记是否是multipart类型的请求
multipartRequestParsed = (processedRequest != request);
//②:根据请求获取合适的处理映射器,返回HandlerExecutionChain对象(包括具体的handle(其实就是哪个controller和对应的方法)及拦截器)
mappedHandler = getHandler(processedRequest);
//如果没有找到处理器,就404了
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//③:根据处理器获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//④:拓展点:拦截器:HandlerInterceptor,调用拦截器的preHandle方法,若返回false,处理结束
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//⑤:调用handler实际处理请求,获取ModelAndView对象,这里会调用HandlerAdapter#handle方 法处理请求
//这里会去执行handler(也就是controller)的具体方法,执行方法前会通过系统中的HandlerMethodArgumentResolver处理解析方法参数
//获取方法返回值后会通过系统中的HandlerMethodReturnValueHandler来处理返回结果
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//判断异步请求不是已经开始了,开始了就返回了
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果mv对象中没有视图 & DispatcherServlet配置了默认的视图,则给mv安排一个默认的视图
applyDefaultViewName(processedRequest, mv);
//⑥:调用拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//⑦:处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//⑧:调用拦截器的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//⑧:调用拦截器的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
//对于异步处理的情况,调用异步处理的拦截器AsyncHandlerInterceptor的afterConcurrentHandlingStarted方法
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
//对于multipart的请求,清理资源,比如文件上传的请求,在上传的过程中文件会被保存到临时文件中,这里就会对这些文件继续清理
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
⑦:处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//⑦-1:如果有异常,进行全局异常处理
//拓展点:全局异常处理器:HandlerExceptionResolver
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
if (mv != null && !mv.wasCleared()) {
//⑦-2:渲染视图
render(mv, request, response);
if (errorView) {
//调用request.removeAttribute方法清理request中错误信息
WebUtils.clearErrorRequestAttributes(request);
}
}
if (mappedHandler != null) {
//⑦-3:调用拦截器的afterCompletion方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
#常见的HandlerMethodArgumentResolver
每个请求里都有一个内置的参数Model model,通过model.addAttribute(“actualUrl”, actualUrl)将底层获取的数据进行存储并传递到页面中去(只有这一个作用);这里注意和ModelAndView的区别,
ModelAndView可以设置转向地址、将底层获取的数据进行存储并传递到页面中去、最后将数据传递给View
#常见的HandlerMethodReturnValueHandler
这里找一个比较有代表性的,带大家看一下,就以RequestResponseBodyMethodProcessor来说一下,这个会处理@ResponseBody标注的方法,抽取其 2 个关键方法的代码,如下
//判断类上或者目标方法上是否有@ResponseBody注解
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
//处理返回值
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
//1:标注为请求已处理,因为当前handleReturnValue方法会直接将结果输出到客户端,所以后续就不需要再进行视图渲染了,表示请求已经被处理了
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
//2:将结果输出到客户端,拓展点HttpMessageConverter
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
#controller返回值
七.springMVC拓展点:
1.拦截器:HandlerInterceptor
2.全局异常处理器:HandlerExceptionResolver
3.执行器方法入参解析器:HandlerMethodArgumentResolver;
比如自定义一个注解@JsonParam,自定义一个JsonParamMethodArgumentResolver,用@JsonParam接收参数,并将值解析为JSONObject对象;
4.执行器方法返回值处理器:HandlerMethodReturnValueHandler
5.HttpMessageConverter返回值类型转化器;
/**
* Web MVC配置
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyInvokeJsonParamMethodArgumentResolver());
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
supportedMediaTypes.add(new MediaType("application", "*+json", StandardCharsets.UTF_8));
supportedMediaTypes.add(MediaType.TEXT_HTML);
fastJsonConverter.setSupportedMediaTypes(supportedMediaTypes);
fastJsonConverter.getFastJsonConfig().setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
converters.add(fastJsonConverter);
}
@Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new MyRequestMappingHandlerMapping();
}
}
八.springMvc注解:
@InitBinder:作用范围为当前controller的参数绑定器(如将日期格式的字符串参数转化为Date类型)
@InitBinder主要用在@Controller中标注于方法上(@RestController也算),表示初始化当前控制器的数据绑定器(或者属性绑定器),只对当前的Controller有效;
(1)定义controller并使用@InitBinder注册属性编辑器CustomDateEditor,作用是根据提供的SimpleDateFormat,将输入的字符串数据格式化为Date类型的指定格式数据。
(2)还有一种就是通过实现PropertyEditorSuppotr接口自定义的。
(3)其中StringTrimmerEditor也是PropertyEditorSuppotr的一个子类!作用是去除字符串的前后空格。
@ModelAttribute
controller中所有方法级的 标注了 @ModelAttribute 的方法,将这些方法的返回值放到模型中,这些值是当前controller中共享的;
@ModelAttribute
public void abc(){
User user = new User();
user.setSex("男");
user.setAddress("建国门");
retrun user;
}
@RequestMapping("test")
public String testModelAttribute(@ModelAttribute("abc")User user,HttpRequest request) {
System.out.println("控制器中处理请求的方法:修改用户: "+user);
HttpSession session = request.getSession();
Enumeration enume = session.getAttributeNames();
while (enume.hasMoreElements())
//@ModelAttribute中添加的属性,在session中是无法获取的
System. out .println(enume.nextElement() + "\r");
}
return "success";
}
@SessionAttributes
一般是和@ModelAttribute一块使用的, @ModelAttribute中添加的属性,在session中是无法获取的,但是如果在controller中添加@SessionAttributes (types={User. class })后,@ModelAttribute中添加的属性session中就会存在;
@SessionAttribute
用于获取session中的属性;
@RequestMapping("/session")
public String session(
@SessionAttribute("abc") User user,
HttpServletRequest request
){
return "index";
}
@InitBinder
public void initBinder(WebDataBinder binder) {
// 格式化date方式一:get请求params传参必须传yyyy-MM-dd HH:mm:ss,否则400错误
// post请求json传参只能传yyyy-MM-dd,如果传其他格式,连这个方法都进不来就400异常了
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CustomDateEditor dateEditor = new CustomDateEditor(df, true);
binder.registerCustomEditor(Date.class, dateEditor);
// // 格式化date方式二,自定义PropertyEditorSupport,然后利用hutool的格式化,DateUtil.parse支持的格式有很多种,这里支持很多种是可以传入任何格式,他都会给你格式化成yyyy-MM-dd HH:mm:ss
// 日期没有时分秒的时候格式化出来的是2022-10-11 00:00:00
// 自定义的这种方式对于json传参方式没有效果,压根连方法都不会进入
// binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
// @Override
// public void setAsText(String text) {
// System.out.println("1111");
// // DateUtil.parse是hutool当中的方法,hutool是一个Java工具包
// setValue(DateUtil.parse(text));
// }
// });
// 格式化string:如果是字符串类型,就去除字符串的前后空格
binder.registerCustomEditor(String.class,
new StringTrimmerEditor(true));
}
}
九.过滤器和拦截器的区别:
1.最简单明了的区别就是过滤器可以修改request,而拦截器不能
2.过滤器需要在servlet容器中实现,拦截器是web框架实现的(如果是springmvc则是mvc实现的)
3.因为拦截器是web框架实现的,所以他可以调用IOC容器中的各种依赖,而过滤器不能
5.过滤器只能在请求的前后使用,而拦截器可以详细到每个方法
区别很多,大家可以去查下
使用场景:
过滤器 的应用:请求编码修改,url级别的权限控制,xss控制等
拦截器 的应用:
1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
2、权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面;
3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个controller都需要的即可使用拦截器实现。
5、OpenSessionInView:如hibernate,在进入处理器打开Session,在完成后关闭Session。拦截器是AOP( Aspect-Oriented Programming)的一种实现,底层通过动态代理模式完成。