# 设计模式实战 **Repository Path**: bossDuy/design-pattern ## Basic Information - **Project Name**: 设计模式实战 - **Description**: 设计模式实战,写代码来学习设计模式 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-06-01 - **Last Updated**: 2025-10-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 装饰器模式 对原本对象进行装饰、增强,而不修改它原本的功能,来实现我们的目的 ## HistorySet 我们需要一个HistorySet,元素remove的时候记录一下,并且其他的和普通set是一样的 ```java package com.yb0os1.insign; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Set; public class HistorySet implements Set { private List historyList = new ArrayList<>(); private Set delegate; public HistorySet(Set delegate) { this.delegate = delegate; } @Override public int size() { return delegate.size(); } @Override public boolean isEmpty() { return delegate.isEmpty(); } @Override public boolean contains(Object o) { return delegate.contains(o); } @Override public Iterator iterator() { return delegate.iterator(); } @Override public Object[] toArray() { return delegate.toArray(); } @Override public T[] toArray(T[] a) { return delegate.toArray(a); } @Override public boolean add(E e) { return delegate.add(e); } @Override public boolean remove(Object o) { boolean remove = delegate.remove(o); if (remove) { historyList.add((E) o); } return remove; } @Override public boolean containsAll(Collection c) { return delegate.containsAll(c); } @Override public boolean addAll(Collection c) { return delegate.addAll(c); } @Override public boolean retainAll(Collection c) { return delegate.retainAll(c); } @Override public boolean removeAll(Collection c) { Object[] array = c.toArray(); for (Object o : array) { historyList.add((E) o); } return delegate.removeAll(c); } @Override public void clear() { delegate.clear(); } @Override public String toString() { return delegate.toString() + " remove list " + historyList.toString(); } } ``` ## Collections.synchronizedCollection(new ArrayList()) ![输入图片说明](https://foruda.gitee.com/images/1750409661631285373/17d992c3_9120271.png "屏幕截图") 可以看到它底层就是使用了装饰器模式,对ArrayList进行增强(加锁) ## IO体系 IO的体系就是装饰器模式,减少IO操作 比如读取文件的时候,我们不需要一个字节一个字节的读取文件,而且读取文件到一个buffer里面 ```java package com.yb0os1.insign; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class BufferInputStream extends InputStream { private final FileInputStream delegate; private final byte[] buffer = new byte[8192]; private int bufferIndex = -1; private int bufferLength = -1; public BufferInputStream(FileInputStream delegate) { this.delegate = delegate; } @Override public int read() throws IOException { if (bufferCanRead()) { return readFromBuffer(); } refreshBuffer(); if (!bufferCanRead()) { return -1; } return readFromBuffer(); } private void refreshBuffer() throws IOException { bufferIndex = 0; bufferLength = delegate.read(buffer); } private int readFromBuffer() { return buffer[bufferIndex++] & 0xff; } private boolean bufferCanRead() { if (bufferLength == -1) return false; if (bufferLength == bufferIndex) return false; return true; } @Override public void close() throws IOException { super.close(); } } ``` ## 案例:自定义参数解析器 目标: ```java @RestController @RequestMapping("/api") public class MyController { //需求,一个自定义注解 让map自动增加一个时间戳 // 当然实现的方法可能有很多 AOP等等 // 这里我们考虑springboot帮助我们加载的过程 @PostMapping public Map test(@RequestBody Map json){ return json; } } ``` ### 源码分析 我们想要给创建的map自动添加一个时间戳,我们需要知道map什么时候创建的? 我们打断点测试一下,我们可以看到args就是我们的map ![输入图片说明](img/image0.png) 找args是什么时候创建生成的?也就是找到args为null的时候 依次往前找 args是由这个函数创建的 ![输入图片说明](img/image1.png) ```java protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = this.getMethodParameters();//获取调用方法的参数列表 if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } else { 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) { if (!this.resolvers.supportsParameter(parameter)) {//判断是否支持这个参数? throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);//这里就是对args[i]进行赋值的 创建了map } catch (Exception var10) { if (logger.isDebugEnabled()) { String exMsg = var10.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw var10; } } } return args; } } ``` 我们看看this.resolvers ```java private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); ``` 方法参数解析器的组合 实现了 方法参数解析器 HandlerMethodArgumentResolver > HandlerMethodArgumentResolverComposite是一个解析器,而且里面还有一个解析器的集合 组合 **经典的装饰器模式** ![输入图片说明](img/image2.png) 方法参数解析器有两个函数 判断是否有支持这个参数的解析器、解析的方法 ![输入图片说明](img/image3.png) 我们看我们这个组合是怎么实现这两个函数的 supportsParameter 可以看到 根据传入的参数拿对应的解析器,先从缓存拿 缓存不存在的话,遍历我们这个解析器集合 ![输入图片说明](img/image4.png) resolveArgument 我们看看拿到具体哪一个参数解析器? ![输入图片说明](img/image5.png) RequestResponseBodyMethodProcessor也实现了HandlerMethodArgumentResolver,因此必然有上面说的两个方法 ![输入图片说明](img/image6.png) 真正帮助我们创建map对象 ```java public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { parameter = parameter.nestedIfOptional(); Object arg = this.readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()); if (binderFactory != null) { String name = Conventions.getVariableNameForParameter(parameter); ResolvableType type = ResolvableType.forMethodParameter(parameter); WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name, type); if (arg != null) { this.validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) { throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); } } if (mavContainer != null) { mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); } } return this.adaptArgumentIfNecessary(arg, parameter); } ``` 到这里我们知道了谁帮助我们创建了map,为什么加上注解可以创建 根据加上的注解获取对应的解析器,然后进行解析创建对象 ### 实现功能 我们想要实现功能的话,自己创建一个参数解析器实现HandlerMethodArgumentResolver 那么我们还需要考虑两个事情: 1. 我们自己创建的参数解析器springboot怎么读取? 2. 我们不需要知道怎么创建对象,里面有一个RequestRespondBodyMethodProcessor帮助我们创建,那么怎么获取这个对象? 我们先解决问题1,想将我们自定义的参数解析器放到HandlerMethodArgumentResolverComposite里面,那么我们需要知道springboot是怎么将其他的解析器放到里面的 有27个参数解析器 ![输入图片说明](img/image7.png) 我们看看这些参数解析器是什么时候被添加的 以及怎么样添加的 可以看到有三个添加函数 我们看看具体走的哪一个 打断点 ![输入图片说明](img/image8.png) 走的39行的这个函数,传入的27个参数解析器 ![输入图片说明](img/image9.png) 找参数解析器是怎么来的 ![输入图片说明](img/image10.png) shi山代码 一个一个new的 ![输入图片说明](img/image11.png) 看看用户自定义的参数解析器 ![输入图片说明](img/12.png) 自定义的只有在这里赋值了 ![输入图片说明](img/13.png) 通过getArgumentResolvers函数获取的自动解析器,getArgumentResolvers所在的类是WebMvcConfigurationSupport ![输入图片说明](img/14.png) ![输入图片说明](img/15.png) 我们只要将我们的解析器放到`private final List delegates = new ArrayList();`里面就好 也就是通过下面的方法 ![输入图片说明](img/16.png) 这个方法怎么被调用的呢?这下面是一个所在的类是被spring代理的配置类,通过自动注入方法,把所有被spring管理的实现了WebMvcConfigurer接口的对象拿到包装起来 ![输入图片说明](img/17.png) 只要我们实现WebMvcConfigurer并放到spring容器中,重写函数就可以实现将自定义参数解析器放到里面 解决问题2,这个RequestRespondBodyMethodProcessor直接被new出来的,没有被spring管理 所以不可以自动注入,但是可以拿到包含这个对象的对象(被spring管理)