首页 > 编程学习 > spring-security入门11---短信验证码(二)----重构---模板模式

文章目录

  • 1. 代码重构前后对比
  • 2. 一些注意事项
    • 2.1 ServletWebRequest对象
    • 2.2 @Autowired一个Map

项目源码地址 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)获取到相应的实现类。

Copyright © 2010-2022 ngui.cc 版权所有 |关于我们| 联系方式| 豫B2-20100000