Java CommunityAndroid开发经验谈程序员

SpringBoot之路(二)之Web进阶

2017-10-29  本文已影响157人  cmazxiaoma

前言

昨天 -> 带女朋友和小表弟去了动物园,看了《全球风暴》电影。
今天 -> 学习了慕课网的Spring Boot进阶之Web进阶的视频和该项目 项目源码,看了一个基于Spring Boot的API、RESTful API项目种子(骨架)的博客。
明天 -> 准备看纯洁的微笑的博客,更深入的学习Spring Boot相关知识。

该学习笔记Demo项目地址是Spring Boot 简单实例Demo


表单验证

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Girl {

    @Id
    @GeneratedValue
    private Integer id;

    private String name;

    @Min(value = 18, message = "Minor girls are prohibited from entering")
    private Integer age;

    private String cupSize;

}

另外我们可以用lombok来简化消除model类中的setter,getter,constructor方法。常用的注解:@Data@NoArgsConstructor@AllArgsConstructor
@Data提供所有属性的settergetter方法,此外还可以提供了equalshashCode, toString()方法。
@NoArgsConstructor注解在类上,提供了一个无参的构造方法。
@AllArgsConstructor注解在类上,提供了一个有参的构造方法。

pom.xml文件配置如下。

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
private final List<ObjectError> errors = new LinkedList();
public boolean hasErrors() {
        return !this.errors.isEmpty();
    }

数据验证失败后,我们加上需要处理的业务逻辑就行了。

    @PostMapping(value = "/girls")
    public Girl girlAdd(@Valid  Girl girl, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            System.out.println(bindingResult.getFieldError().getDefaultMessage());
            return null;
        }
        return girlRepository.save(girl);
    }
image.png

AOP处理请求

什么是AOP?
什么是JoinPoint?
什么是PointCut?
什么是Advice?
什么是Advisor?
模拟出网络请求中输出相关日志的功能
@Aspect
@Component
public class HttpAspect {

    private static final Logger _log = LoggerFactory.getLogger(HttpAspect.class);

    @Pointcut("execution(* girl.controller..*.*(..))")
    public void pointCut() {

    }

    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        _log.info("url = {}", request.getRequestURL());
        _log.info("method = {}", request.getMethod());
        _log.info("ip = {}", request.getRemoteAddr());
        _log.info("class_method = {}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        _log.info("args = {}", joinPoint.getArgs());
    }

    @After("pointCut()")
    public void  doAfter() {
        _log.info("after");
    }

    @AfterReturning(returning = "object", pointcut = "pointCut()")
    public void doAtferReturning(Object object) {
        _log.info("response = {}", object);
    }
}
image.png

全局异常处理

image.png
所以这个类有code,msg,data这三个属性。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
    private Integer code;
    private String msg;
    private Object data;
}
public enum  ResultCode {
    UNKNOW_ERROR(-1, "unknow error"),
    SUCCESS(0, "success"),
    UNAUTHORIZED(401, "no authorized"),
    NOT_FOUND(404, "not found"),
    INTERNAL_SERVER_ERROR(500, "internal server error"),
    LOLITA(1, "lolita"),
    ROYAL_SISTER(2, "royal sister"),
    YOUNG_WOMAN(3, "young woman"),
    AUNT(4, "aunt");


    private Integer code;
    private String msg;

    ResultCode(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}
public class ResultGenerator {

    public static Result success() {
        return success(null);
    }

    public static Result success(Object object) {
        Result result = new Result();
        result.setCode(ResultCode.SUCCESS.getCode());
        result.setMsg(ResultCode.SUCCESS.getMsg());
        result.setData(object);
        return result;
    }

    public static Result error(ResultCode resultCode) {
        Result result = new Result();
        result.setCode(resultCode.getCode());
        result.setMsg(resultCode.getMsg());
        result.setData(null);
        return  result;
    }
}

静态工厂的优点:

@ControllerAdvice
public class ExceptionHandler {
    private static final Logger _log = LoggerFactory.getLogger(ExceptionHandler.class);


    @org.springframework.web.bind.annotation.ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result handlerException(Exception e) {
        if (e instanceof GirlException) {
            return ResultGenerator.error(((GirlException) e).getResultCode());
        } else {
            _log.error("unknow exception = {}", e.getMessage());
            return ResultGenerator.error(ResultCode.UNKNOW_ERROR);
        }
    }
}
public class GirlException extends RuntimeException {
    private Integer code;
    private ResultCode resultCode;

    public GirlException(ResultCode resultCode) {
        super(resultCode.getMsg());
        this.resultCode = resultCode;
        this.code = resultCode.getCode();
    }

    public Integer getCode() {
        return code;
    }

    public ResultCode getResultCode() {
        return resultCode;
    }
}
 @GetMapping(value = "/girls/getAge/{id}")
    public void getAge(@PathVariable("id") Integer id) throws GirlException {
        girlService.getAge(id);
    }

GirlController调用getAge()方法,不处理GirlException异常,继续向上抛出异常。那么ExceptionHandler就能处理这个异常了,通过判断异常的类型,返回相应的Result响应结果。

public void getAge(Integer id) throws GirlException {
        Girl girl = girlRepository.findOne(id);
        int age = girl.getAge();

        if (age > 0 && age < 18) {
            throw new GirlException(ResultCode.LOLITA);
        } else if (age >= 18 && age < 30) {
            throw new GirlException(ResultCode.ROYAL_SISTER);
        } else if (age >= 30 && age < 35) {
            throw new GirlException(ResultCode.YOUNG_WOMAN);
        } else {
            throw new GirlException(ResultCode.AUNT);
        }
    }
image.png image.png

单元测试

每一个合格的程序员在写完相应的业务逻辑后,不可避免的需要去写Unit Test CaseDao层,Service层,Controller层要100%覆盖到,不要遗漏任何一个方法。

image.png image.png
@SpringBootTest
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
public class GirlControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void girlFindOne() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/girls/1"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("abc"));
    }

}

Controller层我们还需要加上额外的@AutoConfigureMockMvc注解,因为我们要模拟请求去测试我们的Controller类中的方法,所以需要配置MockMvc,使用@AutoConfigureMockMvc

@SpringBootTest
@RunWith(SpringRunner.class)
public class GirlServiceTest {

    @Autowired
    private GirlService girlService;

    @Test
    public void findOne() throws Exception {
        Girl girl = girlService.findOne(1);
        Assert.assertEquals(new Integer(15), girl.getAge());
    }

}
image.png image.png
如果TestCase很多,我们需要像这样一个一个的跑吗?

很显然,答案是不需要的。我们在girl项目的根目录打开命令窗口,输入 mvn clean package,我们在打包的时候,会自动跑完Test Case,输出测试结果。因为我刚才又加上了andExpect(MockMvcResultMatchers.content().string("abc")),会导致一个Test Case失败。

image.png image.png

如果我们在打包的时候,不想去跑Test Case,因为跑Case会影响打包的速度或者有时候跑Case会发生一些未知的错误。那么我们只需要在打包的时候输入mvn clean package -Dmaven.test.skip=true即可,观看结果。完美跳过Test Case,美滋滋Build Success

image.png

注意事项

image.png

尾言

S1末到S7了,从高一到明年大学毕业,时光荏苒。昨天RNG输了,今天WE输了。可是我的青春已经如烟了。加油LPL,百炼成钢,心之所向,风雨兼程。

上一篇 下一篇

猜你喜欢

热点阅读