springboot 源码

目录

2.2 原生参数&注解注入

2.3 请求处理-【源码分析】-Model、Map原理

2.4 自定义类型参数处理


2.2 原生参数&注解注入


   在请求处理的过程中,完成了请求映射找到对应Controller方法后,就要完成参数注入了,参数注入包括注解类型的参数、Servlet原生API参数,以及我们自定义类型的POJO参数。本节我们讲解一下注解参数注入和原生参数注入的原理。我们先回顾一下原生参数:

原生参数
实现方式:
1.HttpServletRequest :原生Servlet请求
2. HttpServletResponse :原生Servlet响应
3. HttpSession:原生Session域
4. java.security.Principal :可表示任何实体,通常用来做安全认证和授权
5. Locale :表示地区信息
6. InputStream:字节输入流
7. OutputStream:字节输出流
8. Reader:字符输入流
9. Writer:字符输出流

注解参数
实现方式:
1.@PathVariable:路径传参,如someUrl/{paramId}绑定@Pathvariable paramId
2.@RequestHeader:请求头信息,可获取Map类型的全部请求头信息或String类型的单一信息,如 @RequestHeader(“User-Agent”) String userAgent
3.@CookieValue:Cookie的值信息,可以获取Cookie或String类型的值信息,如@CookieValue(“_ck”) Cookie cookie
4.@RequestParam:请求参数,可以灵活选取参数类型如@RequestParam(“age”) Integer age
5.@RequestBody:请求体参数,通常用来获取前端传递给后端的json字符串,如@RequestBody String jsonValue

原理解析:
   前文中说过,所有请求处理的源码都点就是DispatcherServlet类的doDispatch()方法,参数注入的原理我们也从这里开始~如下图所示,首先经过请求映射过程获取到mappedHandler(如蓝框所示),之后为该Handler寻找一个适配器HandlerAdapter(如红框所示):

在这里插入图片描述

 HandlerAdapter是处理器适配器(接口),它是请求处理部分的关键,其 设计模式是适配器模式(上文已有介绍),在处理器这里应用适配器模式的原因我们可以大胆猜测一下,就是因为处理器为了处理请求,需要调用各类的接口方法,而部分类和接口方法不兼容,因此通过适配器模式协调。
    为请求映射获取到的Handler寻找一个HandlerAdapter,在本项目中HandlerAdapter有四个,如下图所示:
在这里插入图片描述

  这四个适配器见名知意即可知道大概的意思,如第一个适配器是@RequestMapping 方法所使用的,第二个适配器是支持函数式编程的。寻找处理器适配器时依然是按顺序遍历每一个适配器,我们来看一下遍历过程:

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();

            while(var2.hasNext()) {
                HandlerAdapter adapter = (HandlerAdapter)var2.next();
                //判断adapter适配器是否支持当前handler
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }

        throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }

如何判断一个adapter适配器是否支持handler方法?我们查看supports方法源码:

    public final boolean supports(Object handler) {
        return handler instanceof HandlerMethod && this.supportsInternal((HandlerMethod)handler);
    }

判断是否支持的逻辑为当前handler是HandlerMethod类型,如请求映射后我们获得了RequestMappingHandlerMapping,该处理器映射最终得到的handler就会封装成RequestMappingHandler,也就是RequestMappingHandlerAdapter支持的类型。
    找到xxxHandlerAdapter后,就会调用适配器的.handle()方法,其内又是多级的方法调用,我们引用雷神的笔记(加一些我的注解)描述这一过程:

适配器处理方法逐级调用:
//DispatcherServlet——doDispatch()——handle():
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//RequestMappingHandlerAdapter——handleInternal()
//handleInternal()方法内调用——invokeHandlerMethod()
mav = invokeHandlerMethod(request, response, handlerMethod); //其内封装了参数解析器和返回值处理器
//ServletInvocableHandlerMethod ——>invokeForRequest() 执行目标方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//invokeForRequest中首先要获取方法的参数值——>getMethodArgumentValues()
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

我们看一下本项目中的参数解析器argumentResolvers,即参数注入的核心:

在这里插入图片描述

  每一个argumentResolvers对应一个注解,看到这里大家应该明白了,其实注解参数和Servlet原生API参数的解析原理是一样的,都是通过对应不同的参数解析器来调用其resolve()方法解析的。这里我们以表格形式列出一些常用的参数解析器功能:

 参数解析器的实质是一个接口HandlerMethodArgumentResolver,里面包含两个接口方法首先通过supportsParameter判断是否支持这种参数,如果支持的话调用resolveArgument来解析。

在这里插入图片描述

 回到源码中适配器方法的逐级调用,最终是通过invokeForRequest()真正调用目标方法:

    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        //通过该方法封装参数
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Arguments: " + Arrays.toString(args));
        }

        return this.doInvoke(args);
    }

其中,通过调用getMethodArugumentValues()封装方法的参数,之后就将参数传入目标方法,通过反射doInvoke调用了。我们继续进入方法源码:

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}

		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			//遍历所有参数解析器判断是否支持解析该方法参数
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
			//若支持则调用对应参数解析器的解析方法,解析参数值
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				// Leave stack trace for later, exception may actually be resolved and handled...
				if (logger.isDebugEnabled()) {
					String exMsg = ex.getMessage();
					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, exMsg));
					}
				}
				throw ex;
			}
		}
		return args;
	}

上述方法中首先通过==getMethodParameters()==获取到Controller方法的方法参数数组parameters,其内包含每一个参数的索引位置、类型、标注的注解等;然后创建了一个Object数组,遍历parameters进行初始化、使用名称发现器确定参数名,关键步骤是 通过supportsParameter()遍历所有参数解析器,挨个调用supportsParameter()方法,判断是否存在支持解析该方法参数的参数解析器。若存在则找到对应参数解析器并调用其resolveArgument()方法解析参数(并放入缓存)。 supportsParameter()遍历参数解析器的源码:
 

    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
        if (result == null) {
            Iterator var3 = this.argumentResolvers.iterator();

            while(var3.hasNext()) {
                HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
                //确认支持该参数后,会把参数和对应参数解析器放入缓存
                if (resolver.supportsParameter(parameter)) {
                    result = resolver;
                    this.argumentResolverCache.put(parameter, resolver);
                    break;
                }
            }
        }

        return result;
    }

  确认支持该方法参数的参数解析器后,调用其解析方法,解析过程比较繁琐,依次解析参数名和参数的值,解析参数值时又用到底层的BeanExpressionResolver等,我们不做深究。只需知道,最终解析是调用确定的HandlerMethodArgumentResolver的resolveArgument方法即可。
在这里插入图片描述

需要补充一点的是,当我们给方法中注入复杂参数时(如Model和Map),map、model里面的数据会被放在request的请求域,相当于request.setAttribute。我们再讲解一下SpringBoot中是如何将他们放入到请求域中的呢?其具体实现原理在后文响应处理中讲解,这里做一下简单说明。其实也是通过参数解析器实现的,只不过Model和Map使用的是Model/MapMethodProcessor解析器,他们的resolveArgument()方法比较特殊,我们来看一下:

在这里插入图片描述

 处理时会返回mavContainer.geModel(),即ModelAndMapContainer,查看该类的getModel()发现最终返回的是BindingAwareModelMap(),该类既是Model也是Map

private final ModelMap defaultModel = new BindingAwareModelMap();

 将携带数据的BindingAwareModelMap封装到目标方法中,并doInvoke()执行目标方法。特殊的地方在于,执行完目标方法后,BindingAwareModelMap的值会保存在ModelAndMapContainer(mavContainer)中,并在处理返回结果时将mavContainer传入,具体方法是通过processDispatchResult()
   简单总结:复杂参数使用xxxMethodProcessor解析,其resolveArgument()较为特殊,会返回一个mavContainer.getModel()方法,Map/Model方法会封装在Model中,ModelAndViewContainer封装成ModelAndView又层层包装为mergedModel(本质是Map<String,Object>),在响应处理中有一步渲染视图,渲染视图时会将mergedModel中的每一个(k,v)数据放在Request请求域中。

参数解析过程总结:
SpringMVC功能的起点都是doDispatch()方法

请求映射:获取匹配当前请求的Handler处理器(mappedHandler)
寻找匹配的适配器:遍历所有适配器HandlerAdapter(适配器设计模式),并调用其support()方法看是否支持处理当前handler()方法(support源码中判断当前handler是否为HandlerMethod类型)
调用适配器的handle方法:找到支持的适配器HandlerAdapter并调用其ha.handle()方法
方法逐级调用:handle()——>handleInternal()——>invokeHandlerMethod()*
(该方法封装了默认的参数解析器和返回值处理器)——>invokeForRequest()(获取方法参数值后通过反射调用目标方法)
——>getMethodArgumentValues()(参数解析核心方法)
获取目标方法参数数组:getMethodParameters()获取到Controller方法的方法参数数组parameters,包含每一个参数的索引位置、类型、标注的注解
遍历参数解析器判断是否支持解析当前参数:针对每个参数遍历所有参数解析器,挨个调用HandlerMethodArgumentResolver的supportsParameter()方法,判断是否存在支持解析该参数的参数解析器。
调用支持的参数解析器的解析方法:若存在则找到对应参数解析器并调用其resolveArgument()方法解析参数(并放入缓存)
图片无法显示

简言之:通过mappedHandler找出匹配的adapter,然后把参数封装成数组后,对每个元素找出匹配的resolver(根据参数加的注解例如@PathVariable找出匹配的)。通过解析器获得参数中的数值,最后再通过doinvoke把参数和参数名绑定为k:v的格式返回。

2.3 请求处理-【源码分析】-Model、Map原理

        2.2是说的参数是原生参数和注解参数,有一个细节点没有提到但是个人觉得还是挺重要的。2.2所获取的数据都是model,doDispatch 不仅仅完成对参数的解析后封装到对应参数名的k:v中。还会完成对视图view的解析(仅仅在有view的时候)

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

       handle获取model和view的数据,model数据封装为k:v形式,view直接就是值没有为null(view都没解析当然是null)。获取数据后,应该注意一点当视图view为null的时候,mv返回值是null/******************/ 和view没有关系,ha.handle里面不处理view,是通过returnvalue来判断是否为null

。。。边debug边写的,反复试错。。一句话总结:

        1.ha.handle是处理view的,根据returnvalue的值,赋值给view,如下代码所示

        2.mv的值是取决于mav的,但是mav值由多种因素相关,我只深究了mavContainer.isRequestHandled()是根据view是否为null决定mav的值,其余的今天不想看了。

上代码:

--------------------1-------------------

@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		if (returnValue instanceof CharSequence) {
			String viewName = returnValue.toString();
			mavContainer.setViewName(viewName);
			if (isRedirectViewName(viewName)) {
				mavContainer.setRedirectModelScenario(true);
			}
		}
		else if (returnValue != null) {
			// should not happen
			throw new UnsupportedOperationException("Unexpected return type: " +
					returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
		}
	}

-----------------2-------------------------

@Nullable
	private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

		modelFactory.updateModel(webRequest, mavContainer);
		if (mavContainer.isRequestHandled()) {
			return null;
		}
		ModelMap model = mavContainer.getModel();
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			if (request != null) {
				RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
			}
		}
		return mav;
	}

-------------------

protected Object doInvoke(Object... args) throws Exception {
		Method method = getBridgedMethod();
		try {
			if (KotlinDetector.isSuspendingFunction(method)) {
				return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
			}
			return method.invoke(getBean(), args);
          //这里挺有趣的,知道是反射,debug的时候直接就进入自己写的controller中了
		}
		catch (IllegalArgumentException ex) {
			....
		}
	}

为啥突然提到doInvoke?因为,returnValue的值由它返回

if (mavContainer.isRequestHandled()) {
			return null;
		}

mavContainer最终会变成mv,这个不必太过纠结。当view是null,这种情况下不用跳转页面,request handled directly。

当然你要是纠结,我也满足你。mavContainer中显然由view和model的数据,当view数据为null时:(下面的方法返回值不同,也不同,这里返回值是就是参数名参数的键值对,所以走这个,有页面跳转的返回值是forward:/controller中的mapper url)

	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

最终会走到上面这个地方,当mavContainer.setRequestHandled(true);执行完成后,直接激活request handled directly!!!

你又问怎么走到这?我直接贴源码注释:

iterate over registered HandlerMethodReturnValueHandlers and invoke the one that supports it.
Throws:
IllegalStateException – if no suitable HandlerMethodReturnValueHandler is found

虽然是直接处理,但是也是由对应返回值的handler的——RequestResponseMethodProcessor,找不到support的handler直接就抛出异常了。

下一步就是这个(根据mv是否为null进行一系列操作,包含下面所说的写进域里面):

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

 为什么特地谈一下model和map的数据类型。因为,model和map的数据获得后会加入request域中,下面为关键处的源码。

public class InternalResourceView extends AbstractUrlBasedView {
    
 	@Override//该方法在AbstractView,AbstractUrlBasedView继承了AbstractView
	public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		
        ...
        
		Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
		prepareResponse(request, response);
        
        //看下一个方法实现
		renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
	}
    
    @Override
	protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		// Expose the model object as request attributes.
        // 暴露模型作为请求域属性
		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);
		
        ...
	}
    
    //该方法在AbstractView,AbstractUrlBasedView继承了AbstractView
    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);
			}
		});
	}
    
}

2.4 自定义类型参数处理

        首先我们来看一下应用场景,如果我们希望前端提交的信息直接与我们自定义的Bean对象绑定,并自动完成属性注入,SpringBoot能否自动实现呢?如下图所示:

/**
 *     姓名: <input name="userName"/> <br/>
 *     年龄: <input name="age"/> <br/>
 *     生日: <input name="birth"/> <br/>
 *     宠物姓名:<input name="pet.name"/><br/>
 *     宠物年龄:<input name="pet.age"/>
 */
@Data
public class Person {
    
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
    
}

@Data
public class Pet {

    private String name;
    private String age;

}

        在正式解析原理之前,我们要明确一点,自定义类型参数封装注入的前期流程和一般参数是一样的,只是采用的参数解析器不同。

原理解析:
   自定义类型参数所使用的参数解析器是ServletModelAttributeMethodProcessor,我们依然关注该参数解析器的两个接口方法,首先看一下它的supportsParameter()方法,查看它支持处理哪种类型的参数:

    public boolean supportsParameter(MethodParameter parameter) {
    //关注最后一个判断是否是简单属性,非简单属性则返回true代表能处理
        return parameter.hasParameterAnnotation(ModelAttribute.class) || this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType());
    }

        该方法的逻辑为,首先判断是否标注了==@ModelAttribute==注解,如果没标注并且注解不是必须的,则判断是否是简单属性,非简单属性则返回true代表能处理。因此,其supportsParameter()表示该参数解析器(实际上包括ModelAttributeMethodProcessor父类下的所有子类)可以处理自定义类型参数。具体简单类型包括哪些,我们也可以查看源码确认:

public static boolean isSimpleValueType(Class<?> type) {
		return (Void.class != type && void.class != type &&
				(ClassUtils.isPrimitiveOrWrapper(type) ||
				Enum.class.isAssignableFrom(type) ||
				CharSequence.class.isAssignableFrom(type) ||
				Number.class.isAssignableFrom(type) ||
				Date.class.isAssignableFrom(type) ||
				Temporal.class.isAssignableFrom(type) ||
				URI.class == type ||
				URL.class == type ||
				Locale.class == type ||
				Class.class == type));
	}

显然我们自定义类型不属于上述简单类型,因此判断可解析。接下来我们关注它的另一个接口方法resolveArgument()是如何进行解析的:

public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
		Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

		String name = ModelFactory.getNameForParameter(parameter);
		ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
		if (ann != null) {
			mavContainer.setBinding(name, ann.binding());
		}

		Object attribute = null;
		BindingResult bindingResult = null;

		if (mavContainer.containsAttribute(name)) {
			attribute = mavContainer.getModel().get(name);
		}
		else {
			// Create attribute instance(核心逻辑在这开始!)
			//这里创建了一个初始为空的自定义类型的实例(空Person)
			try {
				attribute = createAttribute(name, parameter, binderFactory, webRequest);
			}
			catch (BindException ex) {
				if (isBindExceptionRequired(parameter)) {
					// No BindingResult parameter -> fail with BindException
					throw ex;
				}
				// Otherwise, expose null/empty value and associated BindingResult
				if (parameter.getParameterType() == Optional.class) {
					attribute = Optional.empty();
				}
				bindingResult = ex.getBindingResult();
			}
		}

		if (bindingResult == null) {
			// Bean property binding and validation;
			// skipped in case of binding failure on construction.
			//创建数据绑定器,将请求中传入的自定义类型对象参数传入到创建的空attribute中
			WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
			if (binder.getTarget() != null) {
				if (!mavContainer.isBindingDisabled(name)) {
					bindRequestParameters(binder, webRequest);
				}
				validateIfApplicable(binder, parameter);
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new BindException(binder.getBindingResult());
				}
			}
			// Value type adaptation, also covering java.util.Optional
			if (!parameter.getParameterType().isInstance(attribute)) {
				attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
			}
			bindingResult = binder.getBindingResult();
		}

		// Add resolved attribute and BindingResult at the end of the model
		Map<String, Object> bindingResultModel = bindingResult.getModel();
		mavContainer.removeAttributes(bindingResultModel);
		mavContainer.addAllAttributes(bindingResultModel);

		return attribute;
	}

        首先尝试获取@ModelAttribute注解,若存在则将注解内容绑定到mavContainer中。若不存在,则会通过createAttribute创建一个空的自定义类型对象(如创建一个空Person对象)attribute = createAttribute(name, parameter, binderFactory, webRequest)。之后,创建了一个数据绑定器WebDataBinder,它的作用就是将请求参数的值绑定到attribute中,进而绑定到JavaBean内。
   我们Debug看一下WebDataBinder的结构如下图所示:
在这里插入图片描述

  WebDataBinder工作原理:WebDataBinder实际上是通过其转换服务conversionService中的诸多转换器converters将请求数据转换成指定类型的。为什么有这么多转换器呢?这是因为,传输中我们默认使用的是HTTP协议,传输的数据默认是字符串类型,需要通过调用ConversionService里的某一个converter方法将协议中的id之类的数据转成Integer等类型。因此GenericConversionService就是在设置每一个值的时候调用canService()方法,该方法遍历所有converter哪个可以将当前数据类型(如请求携带的字符串类型参数ID,值为“10”)转换到指定的类型(如JavaBean中的Integer类型id,值为10),转换后经过复杂的层层封装和反射工具,最终调用自定义bean对象的set方法为对应属性赋值。
设计模式:策略模式:
   我们在ArgumentsResolver、WebDataBinder以及ReturnValueHandler(下一章讲)中都见到了遍历所有底层组件(分别遍历了解析器、转换器、处理器)看谁能执行当前处理(处理参数、处理类型转换、处理返回值),就调用对应类的处理方法。这其实是典型的策略模式。此外,接口的设计也是典型的策略模式,不同应用对象实现同一行为可采用不同方式。其基本概念如下:


策略模式
● 背景:在不同场景下使用不同的方法解决同一问题。
● 概念:属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同>接口的独立的类中,从而使得它们可以相互替换。简单的说,策略模式定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换
● 实现:策略模式把对象本身和运算规则区分开来,因此我们整个模式也分为三个部分:
1.环境类(Context):用来操作策略的上下文环境。
2.抽象策略类(Strategy):一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
3.具体策略类(ConcreteStrategy):具体的策略实现。

原理流程图: 

2.5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值