SpringMvc框架

一.什么是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)的一种实现,底层通过动态代理模式完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值