项目源码地址 https://github.com/nieandsun/security
1. 代码重构前后对比
上篇文章结束后生成验证码的代码结构
代码重构之后生成验证码的代码结构
前后对比
- 重构后可以消除ValidateCodeController中的重复代码,使代码开看起来更简洁
- 重构后
便于扩展
,如再有相似功能,只需继承ValidateCodeGenerator实现验证码生成功能+继承AbstractValidateCodeProcessor实现send方法就可以了
一点小感悟
- spring源码中用到了很多的模板方法,在我前面的一篇文章spring-security入门6—表单登陆认证原理源码解析中就解析过spring-security所用到的模板方法,有兴趣的可以看一下。我自认为在读spring源码时,以这些封装了模版方法的类作为切入点,往往可以让你更快地理清某块内容的整体逻辑。
- 模板模式相对来说并不是很难,但在工作中如果遇到一些相似的逻辑,将其抽离出来,组成模板方法是真的可以让你的代码变得非常
易于扩展
和易于维护
—>有过亲身体会(☺)
2. 一些注意事项
本篇文章重构前后的代码(https://github.com/nieandsun/security)有兴趣的可以clone下来进行对比,这里主要对几个大家可能有疑问的点做一些特别说明。
2.1 ServletWebRequest对象
ServletWebRequest
对象其实比较简单,它是一个关于HttpServletRequest和HttpServletResponse的包装类,假如我们方法的参数为new ServletWebRequest(request, response),可以直接用ServletWebRequest request 去接,从而省去了写response对象的麻烦,如果想要再获取response对象,只需要用request.getResponse()方法就可以了.
比如说我们在Controller类中create方法传的参数为new ServletWebRequest(request, response)
@RestController
public class ValidateCodeController {
@Autowired
private Map<String, ValidateCodeProcessor> validateCodeProcessors;
/**
* 创建验证码,根据验证码类型不同,调用不同的 {@link ValidateCodeProcessor}接口实现
*
* @param request
* @param response
* @param type
* @throws Exception
*/
@GetMapping("/code/{type}")
public void createCode(HttpServletRequest request, HttpServletResponse response, @PathVariable String type) throws Exception {
/**
* ServletWebRequest是一个包装类,
* 如果参数为new ServletWebRequest(request, response)
* 可以直接用ServletWebRequest request 去接,
* 省去了写response对象的麻烦,如果想要再获取response对象,只需要用request.getResponse()方法就可以
*/
validateCodeProcessors.get(type + "CodeProcessor").create(new ServletWebRequest(request, response));
}
}
而在ValidateCodeProcessor接口中我们的参数只要为ServletWebRequest request就可以
package com.nrsc.security.core.validate.code;
import org.springframework.web.context.request.ServletWebRequest;
/**
* @author: Sun Chuan
* @date: 2019/7/10 20:09
* Description:校验码处理器,封装不同校验码的处理逻辑
*/
public interface ValidateCodeProcessor {
/**
* 验证码放入session时的前缀
*/
String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_";
/**
* 创建校验码
*
* @param request
* @throws Exception
*/
void create(ServletWebRequest request) throws Exception;
}
如果真要用到response对象时,直接使用request.getResponse()方法就可以了
package com.nrsc.security.core.validate.code.image;
import com.nrsc.security.core.validate.code.impl.AbstractValidateCodeProcessor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletWebRequest;
import javax.imageio.ImageIO;
/**
* @author: Sun Chuan
* @date: 2019/7/10 20:22
* Description: 图片验证码处理器
*/
@Component("imageCodeProcessor")
public class ImageCodeProcessor extends AbstractValidateCodeProcessor<ImageCode> {
/**
* 发送图形验证码,将其写到响应中
*
* @param request
* @param imageCode
* @throws Exception
*/
@Override
protected void send(ServletWebRequest request, ImageCode imageCode) throws Exception {
ImageIO.write(imageCode.getImage(), "JPEG", request.getResponse().getOutputStream());
}
}
2.2 @Autowired一个Map
代码如下:
/**
* 收集系统中所有的 {@link ValidateCodeProcessor} 接口的实现,并将其存到Map中。
*/
@Autowired
private Map<String, ValidateCodeProcessor> validateCodeProcessors;
如注释中所言,spring在启动时会自动收集指定接口的实现类,并将其按照<beanName,bean>的形式存到Map里,直接可以通过map.get(beanName)获取到相应的实现类。