5.dispatcherServlet源码分析

DispatcherServlet:前端控制器
在这里插入图片描述

HttpServlet如果发送请求,首先一定是来到这个HttpServlet 的 doPost方法和doGet方法
HttpServletBean
FrameworkServlet重写了doPost和doGet方法。叫processRequest(request,response)这个方法调用了本类的抽象方法:doService那么子类一定要实现这个抽象方法,
DispatcherServlet实现了doService,doService这个方法调用了本类的方法:doDispatch

总结就是 一发请求,是来到DispatcherServlet的doDispatch方法

用debug的方式,
给DispatcherServlet的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.检查是否文件上传请求。checkMultipart
		processedRequest = checkMultipart(request);
		
				multipartRequestParsed = (processedRequest != request);

		 2.Determine handler for the current request.
		根据当前的请求,
		就来看哪个handlerMapping中有这个请求的映射信息,
		获取到目标处理器类。
		mappedHandler = getHandler(processedRequest);

         3.如果没有找到哪个处理器(控制器)能处理这个请求就404,或者抛异常。
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

	 4.springmvc拿到能执行这个类的所有方法的适配器:(反射工具)
	   因为最后执行这个类的所有方法要反射执行。
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

		Actually invoke the handler.
  5.适配器来执行目标方法;将目标方法执行完成后的返回值作为视图名,设置保存到modelandview。
   目标方法无论怎么写,最终适配器执行完成以后,都会将执行后的信息封装成model and view 。
  mv = ha.handle(processedRequest, response, mappedHandler.getHandler());


				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(request, mv);
				如果没有视图名,就设置一个默认的视图名
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			
6.根据目标方法最终执行完成后封装的ModelAndView;转发到对应页面,而且ModelAndView中的数据可以从请求域中获取。
	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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()细节:
getHandler()会返回目标处理器类的执行链。
在这里插入图片描述
HandlerMapping:处理器映射:他里面保存了每一个处理器能处理哪些请求的映射信息。
在这里插入图片描述
handlerMap:ioc容器启动创建Controller对象的时候,扫描每个处理器都能处理什么请求,保存在handlerMapping的handlerMap属性中。下一次请求来,就来看哪个handlerMapping中有这个请求映射信息。
在这里插入图片描述

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

如何找到这个目标处理器类的适配器。要拿适配器才去执行目标方法
(能执行这个类的所有方法)
在这里插入图片描述
AnnotationMethodHandlerAdapters:能解析注解方法的适配器
处理器类中只要有标了注解的这些方法就能用。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			//当前处理器有没有实现这个接口(handler instanceof   HttpRequestHandler);
			//当前处理器有没有实现这个接口(handler instanceof Controller);
			if (ha.supports(handler)) {
				return ha;
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

DispatcherServlet有几个引用类型的属性;SpringMVC的九大组件;
SpringMVC在工作的时候,关键位置都是由这些组件完成的。

/** 文件上传解析器*/
private MultipartResolver multipartResolver;

/** 区域化信息解析器 和国际化有关 */
private LocaleResolver localeResolver;

/** 主题解析器 */
private ThemeResolver themeResolver;

/** 处理器映射 */
private List<HandlerMapping> handlerMappings;

/**  HandlerAdapters适配器 */
private List<HandlerAdapter> handlerAdapters;

/** SpringMVC支持强大的异常功能,异常解析器 */
private List<HandlerExceptionResolver> handlerExceptionResolvers;

/**  */
private RequestToViewNameTranslator viewNameTranslator;

/** SpringMVC允许重定向携带数据*/
private FlashMapManager flashMapManager;

/** 视图解析器 */
private List<ViewResolver> viewResolvers;

spring构造器创建,有一个onRefresh()方法,留给子类的方法
DispatcherServlet重写了这个方法
相当于ioc容器一启动,onRefresh()方法里面的代码也会被调用,
初始化战略initStrategies

@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

DispatcherServlet中九大组件初始化的地方

protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		
		initHandlerMappings(context);
		
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

可以在web.xml中的修改DispatcherServlet中某些属性的默认配置

<init-param>
       <param-name>detectAllHandlerMappings</param-name>
       <param-value>false</param-value>
</init-param>

初始化HandlerMapping

private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			 他先会在Bean工厂(BeanFactoryUtils),找到这个HandlerMapping类型的所有bean
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				OrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
			ioc容器中找到这个id的bean
				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) {
		就getDefaultStrategies获取默认配置
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

组件的初始化:
有些组件在容器中是使用类型找到,有些组件是使用id找到;
去容器中找这个组件,如果没有找到就用默认的配置;

	args:如何确定目标方法每一个参数的值,最难?
	method.invoke(this,args)
	
	@RequestMapping("/update")  
	public String updateBook(
	
	@RequestParam(value="author")String author,
	   Map<String,Object> model,
	   HttpServletRequest request,
	   @ModelAttribute("book")Book book
	   )

mv=ha.handle(processedRequest,response,mappedHandler.getHandler());
适配器执行目标方法的细节:
| |
\ /

return invokeHandlerMethod(request, response, handler);

| |
\ /

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
    拿到方法解析器
		ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
	 方法解析器根据当前请求地址找到真正的目标方法
		Method handlerMethod = methodResolver.resolveHandlerMethod(request);
		创建了一个方法执行器
		ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
		包装原生的request, response
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		创建了一个BindingAwareModelMap隐含模型
		ExtendedModelMap implicitModel = new BindingAwareModelMap();

        真正执行目标方法;
        目标方法利用反射执行期间确定参数值,
        提前执行modelattribute方法 等所有操作都在这个方法中。       
		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler 
		,webRequest, implicitModel);
		
		ModelAndView mav =
				methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
		methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
		return mav;
	}

invokeHandlerMethod方法执行的细节

public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
			NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {

		Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
		try {
			boolean debug = logger.isDebugEnabled();
			for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
				Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
				if (attrValue != null) {
					implicitModel.addAttribute(attrName, attrValue);
				}
			}
	1.找到所有@Modelattribute注解标注的方法;
			for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
			
				Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
		
	2.确定modelattribute方法执行时要用的每一个参数的值		
				Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, 
				implicitModel);
				
				if (debug) {
					logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
				}
找到这个@ModelAttribute的value值,我么们没写value值,attrName=null				
String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();

				if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
					continue;
				}
2.方法可访问的				ReflectionUtils.makeAccessible(attributeMethodToInvoke);

3.执行方法,返回值attrValue=null				
Object attrValue = attributeMethodToInvoke.invoke(handler, args);

4.无value值			if ("".equals(attrName)) {

5.解析返回值类型					
					Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
6.为返回值类型取变量名,void 他变成attrName					
					attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
				}
	7.把提前运行的ModelAttribute标注方法的返回值,放到隐含模型中			
				if (!implicitModel.containsAttribute(attrName)) {
					implicitModel.addAttribute(attrName, attrValue);
				}
			}
	8.	确定目标方法执行时要用的每一个参数的值		
			Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
			if (debug) {
				logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
			}
			ReflectionUtils.makeAccessible(handlerMethodToInvoke);
			9.目标方法反射执行
			return handlerMethodToInvoke.invoke(handler, args);
		}
		catch (IllegalStateException ex) {
			// Internal assertion failed (e.g. invalid signature):
			// throw exception with full handler method context...
			throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
		}
		catch (InvocationTargetException ex) {
			// User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
			ReflectionUtils.rethrowException(ex.getTargetException());
			return null;
		}
	}

public java.lang.String com.shuchong.controller.ModelAttributeController.updateBook
(java.lang.String,
java.util.Map,
javax.servlet.http.HttpServletRequest,
com.shuchong.controller.Book)

先确定方法执行时要用的每一个参数的值
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {

		Class<?>[] paramTypes = handlerMethod.getParameterTypes();
		
创建了一个Object类型的数组,长度和参数个数一样,用来保存参数的值。
Object[] args = new Object[paramTypes.length];

		for (int i = 0; i < args.length; i++) {
			MethodParameter methodParam = new MethodParameter(handlerMethod, i);
			methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
			GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
			String paramName = null;
			String headerName = null;
			boolean requestBodyFound = false;
			String cookieName = null;
			String pathVarName = null;
			String attrName = null;
			boolean required = false;
			String defaultValue = null;
			boolean validate = false;
			Object[] validationHints = null;
			1.初始化参数注解的个数
			int annotationsFound = 0;
			Annotation[] paramAnns = methodParam.getParameterAnnotations();
        
    2.如果参数有注解,就解析并保存参数注解的信息
			for (Annotation paramAnn : paramAnns) {
	 		
				if (RequestParam.class.isInstance(paramAnn)) {
					RequestParam requestParam = (RequestParam) paramAnn;
					paramName = requestParam.value();
					required = requestParam.required();
					defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
					annotationsFound++;
				}
				
				else if (RequestHeader.class.isInstance(paramAnn)) {
					RequestHeader requestHeader = (RequestHeader) paramAnn;
					headerName = requestHeader.value();
					required = requestHeader.required();
					defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
					annotationsFound++;
				}
				
				else if (RequestBody.class.isInstance(paramAnn)) {
					requestBodyFound = true;
					annotationsFound++;
				}
				
				else if (CookieValue.class.isInstance(paramAnn)) {
					CookieValue cookieValue = (CookieValue) paramAnn;
					cookieName = cookieValue.value();
					required = cookieValue.required();
					defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
					annotationsFound++;
				}
				
				else if (PathVariable.class.isInstance(paramAnn)) {
					PathVariable pathVar = (PathVariable) paramAnn;
					pathVarName = pathVar.value();
					annotationsFound++;
				}
				
				else if (ModelAttribute.class.isInstance(paramAnn)) {
					ModelAttribute attr = (ModelAttribute) paramAnn;
					attrName = attr.value();
					annotationsFound++;
				}
				
				else if (Value.class.isInstance(paramAnn)) {
					defaultValue = ((Value) paramAnn).value();
				}
				else if (paramAnn.annotationType().getSimpleName().startsWith("Valid")) {
					validate = true;
					Object value = AnnotationUtils.getValue(paramAnn);
					validationHints = (value instanceof Object[] ? (Object[]) value : new Object[] {value});
				}
			}

			if (annotationsFound > 1) {
				throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
						"do not specify more than one such annotation on the same parameter: " + handlerMethod);
			}
         3.没有找到参数的注解的情况
			if (annotationsFound == 0) {
		 4.就解析普通参数
				Object argValue = 
				resolveCommonArgument(methodParam, webRequest);
		  会进入resolveStandardArgument(解析标准参数)



				if (argValue != WebArgumentResolver.UNRESOLVED) {
					args[i] = argValue;
				}
				else if (defaultValue != null) {
					args[i] = resolveDefaultValue(defaultValue);
				}
				else {
				
					Class<?> paramType = methodParam.getParameterType();
					
					if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
				 如果不是普通参数,再判断是否是Model或者Map旗下的,
				 如果是,将之前创建的隐含模型直接赋值给这个参数
	
						if (!paramType.isAssignableFrom(implicitModel.getClass())) {
							throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " +
									"Model or Map but is not assignable from the actual model. You may need to switch " +
									"newer MVC infrastructure classes to use this argument.");
						}
						args[i] = implicitModel;
					}
					else if (SessionStatus.class.isAssignableFrom(paramType)) {
						args[i] = this.sessionStatus;
					}
					else if (HttpEntity.class.isAssignableFrom(paramType)) {
						args[i] = resolveHttpEntityRequest(methodParam, webRequest);
					}
					else if (Errors.class.isAssignableFrom(paramType)) {
						throw new IllegalStateException("Errors/BindingResult argument declared " +
								"without preceding model attribute. Check your handler method signature!");
					}
					else if (BeanUtils.isSimpleProperty(paramType)) {
						paramName = "";
					}
					else {
						attrName = "";
					}
				}
			}
       确定值的环节
			if (paramName != null) {
				args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
			}
			else if (headerName != null) {
				args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
			}
			else if (requestBodyFound) {
				args[i] = resolveRequestBody(methodParam, webRequest, handler);
			}
			else if (cookieName != null) {
				args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
			}
			else if (pathVarName != null) {
				args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
			}
			
			springmvc确定POJO值,
			还要将请求中的每一个参数赋值给这个对象
			else if (attrName != null) {
				WebDataBinder binder =
						resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
				boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
				if (binder.getTarget() != null) {
					doBind(binder, webRequest, validate, validationHints, !assignBindingResult);
				}
				args[i] = binder.getTarget();
				if (assignBindingResult) {
					args[i + 1] = binder.getBindingResult();
					i++;
				}
				implicitModel.putAll(binder.getBindingResult().getModel());
			}
		}

		return args;

如果参数没有注解;
解析普通参数,( resolveCommonArgument)
就是去看当前参数是不是原生API

@Override
		protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception {
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);

			if (ServletRequest.class.isAssignableFrom(parameterType) ||
					MultipartRequest.class.isAssignableFrom(parameterType)) {
				Object nativeRequest = webRequest.getNativeRequest(parameterType);
				if (nativeRequest == null) {
					throw new IllegalStateException(
							"Current request is not of type [" + parameterType.getName() + "]: " + request);
				}
				return nativeRequest;
			}
			else if (ServletResponse.class.isAssignableFrom(parameterType)) {
				this.responseArgumentUsed = true;
				Object nativeResponse = webRequest.getNativeResponse(parameterType);
				if (nativeResponse == null) {
					throw new IllegalStateException(
							"Current response is not of type [" + parameterType.getName() + "]: " + response);
				}
				return nativeResponse;
			}
			else if (HttpSession.class.isAssignableFrom(parameterType)) {
				return request.getSession();
			}
			else if (Principal.class.isAssignableFrom(parameterType)) {
				return request.getUserPrincipal();
			}
			else if (Locale.class.equals(parameterType)) {
				return RequestContextUtils.getLocale(request);
			}
			else if (InputStream.class.isAssignableFrom(parameterType)) {
				return request.getInputStream();
			}
			else if (Reader.class.isAssignableFrom(parameterType)) {
				return request.getReader();
			}
			else if (OutputStream.class.isAssignableFrom(parameterType)) {
				this.responseArgumentUsed = true;
				return response.getOutputStream();
			}
			else if (Writer.class.isAssignableFrom(parameterType)) {
				this.responseArgumentUsed = true;
				return response.getWriter();
			}
			return super.resolveStandardArgument(parameterType, webRequest);
		}

如何确定方法的每一个参数的值
public String updateBook(
@RequestParam(value=“author”)String author,
Map<String,Object> model,
HttpServletRequest request,
@ModelAttribute(“book”)Book book
)在这里插入图片描述

 标了注解:
        ---| 解析并保存这个参数注解的详细信息;
                 ---|如果参数是ModelAttribute注解
                     拿到ModelAttribute注解的value值,让attrName保存

没标注解:
1 .先看是否普通参数(是否原生API)
再看是否Model或者Map,如果是传入隐含模型;
2. 自定义类型参数没有ModelAttribute注解
1.先看是否原生API
2.再看是否Model或者Map
3.再看是否是其他类型的比如SessionStatus,HttpEntity,Errors,
4.再看是否简单类型的属性 ,比如Integer,String,基本类型参数
如果是paramName=“”
5.attrName=""

如果是自定义类型对象,最终会产生两个效果
1.如果参数是ModelAttribute注解,就给attrName赋值为这个注解的value值
2.如果参数没有ModelAttribute注解,就给attrName赋值"";

确定自定义类型参数的值

	private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
	
      String name = attrName;
		如果attrName是空串,就将参数类型的小写作为值(book)
		if ("".equals(name)) {
			name = Conventions.getVariableNameForParameter(methodParam);
		}
		Class<?> paramType = methodParam.getParameterType();
		确定目标对象的值,
		Object bindObject;
		如果隐含模型有这个key,将值赋值给目标对象bindObject
		if (implicitModel.containsKey(name)) {
			bindObject = implicitModel.get(name);
		}
		else if (this.methodResolver.isSessionAttribute(name, paramType)) {
			bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
			if (bindObject == null) {
				raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
			}
		}
		else {
			bindObject = BeanUtils.instantiateClass(paramType);
		}
		WebDataBinder binder = createBinder(webRequest, bindObject, name);
		initBinder(handler, name, binder, webRequest);
		return binder;
	}

SpringMVC确定POJO值的三步:

1.如果隐含模型中有这个key(标了ModelAttribute注解,就是注解指定的value,没标,就是参数类型的首字母小写)的值
如果有将这个值赋值给bindObject

  1. 如果是SessionAttributes标注的属性,就从Session中拿
  2. 如果都不是就利用反射创建对象
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值