Application of strategy design pattern in spring source code
Use of policy design patterns
Combined with my own learning, I have some understanding of the strategy mode. First of all, the use background, in short, is used in scenarios with multiple strategies. This seems to be nonsense. Take a simple example
Let's simulate the parsing logic of request parameters in the spring MVC source code
Parameter resolution interface
public interface ParamResolve { /** * Judge whether the current parsing class can parse the param parameter. Return true to support it and false to not support it * @param param * @return */ boolean isSupportResolve(Object param); /** * Analytical parameters * @param param */ void resolveParam(Object param); }
Parameter parsing implementation class
public class JsonResolve implements ParamResolve{ @Override public boolean isSupportResolve(Object param) { return "Json".equals(param); } @Override public void resolveParam(Object param) { System.out.println("analysis json parameter"); } }
public class ListResolve implements ParamResolve { @Override public boolean isSupportResolve(Object param) { return "List".equals(param); } @Override public void resolveParam(Object param) { System.out.println("analysis list parameter"); } }
public class StringResolve implements ParamResolve{ @Override public boolean isSupportResolve(Object param) { return "String".equals(param); } @Override public void resolveParam(Object param) { System.out.println("yes String Type"); } }
Here, I simulated three parameter parsers, namely, the parsing of String parameters, json parameters and list type parameters. It's just a simple demo
We need to consider that although I provide three parameter parsers, how can I know which parameter is to be parsed by which parser? Then you need an extra class to manage all the parameter parsers
public class Context { public static void main(String[] args) { List<ParamResolve> paramResolves = new ArrayList<>(); paramResolves.add(new ListResolve()); paramResolves.add(new StringResolve()); paramResolves.add(new JsonResolve()); test(paramResolves,"String"); test(paramResolves,"Json"); test(paramResolves,"List"); } public static void test(List<ParamResolve> paramResolves,String str){ for (ParamResolve paramResolve: paramResolves) { if(paramResolve.isSupportResolve(str)){ paramResolve.resolveParam(str); } } } }
This is the class of parameter management parser. That is to say, if the input parameter is a String type parameter at this time, I will call the isSupportResolve method first. If this method returns true, it means that the current parser can resolve this parameter, and then I will call its resolution method to resolve this parameter
What if I need to extend the fourth parameter parser at this time? Because I may also provide the resolution of Map type parameters and Integer type parameters? You only need to implement the ParamResolve interface, and then put the implementation class into the list collection paramResolves
I think this is the application of strategy design pattern
Application in spring source code
In fact, in the spring MVC source code, the parameter parser is the application of the policy pattern. If you have seen the spring MVC parameter parsing source code, you will find that the demo I wrote above is similar to the logic of spring MVC parameter parsing,
Parameter parser interface You can see that in this interface, only two methods are defined, namely, whether to support the resolution of the parameter, and the second is the method of parameter resolution org.springframework.web.method.support.HandlerMethodArgumentResolver public interface HandlerMethodArgumentResolver { boolean supportsParameter(MethodParameter var1); Object resolveArgument(MethodParameter var1, ModelAndViewContainer var2, NativeWebRequest var3, WebDataBinderFactory var4) throws Exception; }
The spring parser can provide N parameters by itself
At this time, the question arises. When were these spring built-in parameter parsers added to the list collection in the spring source code? Let's finish with this question
For the spring source code, how can we add the custom parameter parser to the spring container?
In the spring source code, we also use a list set to store all parameter parsers. Therefore, we only need to put the parameter parser defined by ourselves into this list set
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#argumentResolvers private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<HandlerMethodArgumentResolver>();
How do you put it in this list? spring also provides extension points. You only need to implement the WebMvcConfigurer interface, and then implement its addArgumentResolvers() method. For details, please refer to my previous note Spring MVC application - Custom parameter parser
After we put it into the collection, when we initiate the call, spring will traverse the list collection, and then call the parameters in the request in turn
Pseudo code is roughly like this:
for(HandlerMethodArgumentResolver Parameter parser: parameter parser list aggregate){ for(Param param:params){ if(Parameter parser.isSupport(param)){ Parameter parser.Analytical parameters } } }
In the spring source code, of course, it is not stupid enough to deal with it like this. Internally, it will use a map set and save the parser corresponding to the parameters, so that there is no need to parse again when calling repeatedly. This is just the implementation of pseudo code
Therefore, this is the use of strategy design pattern and its application in spring source code
I think the strength of spring is that it is powerful enough. What if some of the functions it provides still can't meet the business needs of programmers? It doesn't matter. Spring provides a series of extension points, which are enough for programmers to extend. It's a one-stop service
How can spring's own parameter parser be put into the list collection
The simplest way is to set a breakpoint in debug at all addResolve locations, and then see where it is called. In fact, when the RequestMappingHandlerAdapter bean is initialized, we add all the default parameter parsers to the list collection,
As you can see, it implements the interface to initialize the callback. Therefore, in the afterpropertieset () method, addResolve logic is provided
public void afterPropertiesSet() { this.initControllerAdviceCache(); List handlers; if (this.argumentResolvers == null) { handlers = this.getDefaultArgumentResolvers(); this.argumentResolvers = (new HandlerMethodArgumentResolverComposite()).addResolvers(handlers); } if (this.initBinderArgumentResolvers == null) { handlers = this.getDefaultInitBinderArgumentResolvers(); this.initBinderArgumentResolvers = (new HandlerMethodArgumentResolverComposite()).addResolvers(handlers); } if (this.returnValueHandlers == null) { handlers = this.getDefaultReturnValueHandlers(); this.returnValueHandlers = (new HandlerMethodReturnValueHandlerComposite()).addHandlers(handlers); } }
Therefore, this is the answer to how the default parameter parser is put into the list