昵称:bennyrhys
公众号:让我遇见相似的灵魂

0%

简述

SpringBoot之web进阶。

SpringBoot两小时快速入门,基因芯片个人信息程序的进阶内容。

信息时代最重视的是什么?是效率!企业最重视的人才是什么?是高效能人士!所谓“天下武功唯快不破 ” ,为此专门推出此教程,让你快速Get到SpringBoot实际应用技能。本教程所有代码、笔记由bennyrhys免费提供,遵循”学习其实可以很快乐“的原则,以极客浪漫情怀的”红包表白程序“为场景,贯穿微服务必学框架-SpringBoot相关内容,将初学SpringBoot的踩坑点进行指点,为同学打造麻雀虽小五脏俱全的教程。标题风趣幽默,必然让你本次的学习之旅尽兴而归。载着干货的老司机,在召唤你~

链接

GitHub:点击预览——SpringBoot进阶基因芯片个人信息程序

详细文档:点击搜索——SpringBoot进阶基因芯片个人信息程序

目录

  • @Valid表单验证
    • 背景假设
    • 整理代码
    • git提交
    • 新增少女:参数换成对象
    • 对象:字段进行限制
    • git提交
  • AOP统一处理请求日志
    • AOP,IOC防误区简述
    • 背景假设
    • pom依赖
    • 新建aspect拦截
    • 验证拦截
    • 拦截顺序
    • 简化拦截代码
    • 日志替换print输出
    • 日志获取请求信息
    • 日志获取响应信息
    • git提交
  • 统一异常处理
    • 封装返回值json
    • 验证返回json
    • 代码优化-减少重复
    • 背景假设
    • 业务分析
    • 判断信息-外抛异常获取
    • git提交-判断信息-外抛异常获取
    • 捕获异常封装返回json
    • 重写Exception-多状态码
    • 未知异常排除-打日志
    • 管理异常code和msg-枚举
    • git提交-异常处理-枚举
  • 单元测试
    • 测试service
    • 测试AP
    • I测试用例每个都要手动?
    • git提交-单元测试

[TOC]

作者:bennyrhys@163.com

@Valid表单验证

背景假设

拦截所有未满18周岁的少女,禁止添加。

整理代码

建包规整代码

  • domain

    • Girl
  • controller

    • GirlController
    • HelloController
  • service

    • GirlService
  • repository

    • GirlRepository
  • properties

    • GirlConfig
  • GirlApplication

git提交

git commit -m “代码包整理”

新增少女:参数换成对象

GirlController

//原本参数获取太过繁琐

1
2
@RequestParam("cupSize") String cupSize,
@RequestParam("age") Integer age

//修改参数,传入对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 新增一个女生
* http://localhost:8081/girl/girls
* 注意:post请求
*
* 参数
* cupSize f
* age 16
*
* 插入因为自增注意id冲突
*/
@PostMapping(value = "/girls")
public Girl girlAdd(Girl girl){
girl.setCupSize(girl.getCupSize());
girl.setAge(girl.getAge());
//save返回添加对对象
return repository.save(girl);
}

//测试

http://localhost:8081/girl/girls

{

​ “id”: 8,

​ “cupSize”: “C”,

​ “age”: 22

}

对象:字段进行限制

Girl

1
2
@Min(value = 18, message = "未成年少女禁止入内")
private Integer age;

GirlController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 新增一个女生
* http://localhost:8081/girl/girls
* 注意:post请求
*
* 参数
* cupSize f
* age 16
*
* 插入因为自增注意id冲突
*/
@PostMapping(value = "/girls")
//@Valid表示要验证的是这个对象 BindingResult返回验证信息
public Girl girlAdd(@Valid Girl girl, BindingResult bindingResult){
//判断是否发生错误
if (bindingResult.hasErrors()){
//打印错误信息
System.out.println(bindingResult.getFieldError().getDefaultMessage());
return null;
}
girl.setCupSize(girl.getCupSize());
girl.setAge(girl.getAge());
//save返回添加对对象
return repository.save(girl);
}

请求postman

http://localhost:8081/girl/girls

参数age小于18时提示禁止信息

控制台输出

2019-12-31 09:15:01.151 INFO 1327 — [nio-8081-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 9 ms
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into girl (age, cup_size, id) values (?, ?, ?)
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into girl (age, cup_size, id) values (?, ?, ?)
未成年少女禁止入内

git提交

git commit -m “@Valid表单验证”

AOP统一处理请求日志

AOP,IOC防误区简述

  • AOP编程范式
    • 编程范式,与语言无关,是一种设计思想
    • 面向切面(AOP)、面向对象(OOP)【java、c++、c#】、面向过程(POP)【c】
    • 如果说面向对象:垂直切割成各自独立对象,面向切面:就是抽取通用业务逻辑

背景假设

  • 登陆后获得权限调用方法
    • 传统直接想到每个方法if判断(不可行,不能每个都加太麻烦)
    • controller加构造方法if(不可行,spring启动时调用构造方法,后期http请求不会调用构造方法)
    • AOP(推荐,统一进行验证)

pom依赖

1
2
3
4
5
<!--        添加aop依赖,无需在启动类加注解-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

新建aspect拦截

  • aspect包
    • HttpAspect.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.bennyrhys.girl.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect //aop标识
@Component //添加到容器
public class HttpAspect {
//拦截方法:加两个..表示任何参数都会被拦截
@Before("execution(public * com.bennyrhys.girl.controller.GirlController.girlList(..))")
//先定方法不然没上方提示
public void log(){
System.out.println("llllllll");
}
}

检测类下所有方法/*

1
@Before("execution(public * com.bennyrhys.girl.controller.GirlController.*(..))")

验证拦截

查询列表

http://localhost:8081/girl/girls

1
[{"id":3,"cupSize":"G","age":20},{"id":4,"cupSize":"G","age":20},{"id":5,"cupSize":"F","age":22},{"id":6,"cupSize":"F","age":16},{"id":7,"cupSize":"F","age":16},{"id":8,"cupSize":"C","age":22},{"id":9,"cupSize":"C","age":20},{"id":10,"cupSize":"C","age":18}]

控制台

llllllll //被过滤
Hibernate: select girl0_.id as id1_0_, girl0_.age as age2_0_, girl0_.cup_size as cup_size3_0_ from girl girl0_

增加少女

http://localhost:8081/girl/girls

//未被过滤检测

Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into girl (age, cup_size, id) values (?, ?, ?)

拦截顺序

HttpAspect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.bennyrhys.girl.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect //aop标识
@Component //添加到容器
public class HttpAspect {
//@Before方法执行之前就执行了
//拦截方法:加两个..表示任何参数都会被拦截
@Before("execution(public * com.bennyrhys.girl.controller.GirlController.*(..))")
//先定方法不然没上方提示
public void log(){
System.out.println("llllllll");
}


@After("execution(public * com.bennyrhys.girl.controller.GirlController.*(..))")
//先定方法不然没上方提示
public void doAfter(){
System.out.println("22222222");
}
}

GirlController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 查询所有女生列表
* @return
* http://localhost:8081/girl/girls
* [
* {
* "id": 1,
* "cupSize": "B",
* "age": 18
* }
* ]
*/
@GetMapping(value = "/girls")
public List<Girl> girlList(){
System.out.println("girlList测试检测顺序");
return repository.findAll();
}

控制台打印

llllllll
girlList测试检测顺序
Hibernate: select girl0_.id as id1_0_, girl0_.age as age2_0_, girl0_.cup_size as cup_size3_0_ from girl girl0_
22222222

简化拦截代码

HttpAspect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.bennyrhys.girl.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect //aop标识
@Component //添加到容器
public class HttpAspect {
//提取公共方法
//拦截方法:加两个..表示任何参数都会被拦截
@Pointcut("execution(public * com.bennyrhys.girl.controller.GirlController.*(..))")
//先定方法不然没上方提示
public void log(){
System.out.println("1111");
}

//@Before方法执行之前就执行了
@Before("log()")
public void doBefore(){
System.out.println("11111111");
}


@After("log()")
//先定方法不然没上方提示
public void doAfter(){
System.out.println("22222222");
}
}

控制台

http://localhost:8081/girl/girls

11111111
girlList测试检测顺序
Hibernate: select girl0_.id as id1_0_, girl0_.age as age2_0_, girl0_.cup_size as cup_size3_0_ from girl girl0_
22222222

日志替换print输出

HttpAspect

//日志输出slf4j 注意类名切换
private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);

//打印

logger.info(“222222222”);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.bennyrhys.girl.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect //aop标识
@Component //添加到容器
public class HttpAspect {

//日志输出slf4j
private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);


//提取公共方法
//拦截方法:加两个..表示任何参数都会被拦截
@Pointcut("execution(public * com.bennyrhys.girl.controller.GirlController.*(..))")
//先定方法不然没上方提示
public void log(){
}

//@Before方法执行之前就执行了
@Before("log()")
public void doBefore(){
logger.info("111111111");
}


@After("log()")
//先定方法不然没上方提示
public void doAfter(){
logger.info("222222222");
}
}

GirlController

1
2
3
//日志输出slf4j
private final static Logger logger = LoggerFactory.getLogger(GirlController.class);
logger.info("girlList测试检测顺序");

2019-12-31 10:07:19.744 INFO 1684 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect :

111111111
2019-12-31 10:07:19.749 INFO 1684 — [nio-8081-exec-1] c.b.girl.controller.GirlController : girlList测试检测顺序
Hibernate: select girl0_.id as id1_0_, girl0_.age as age2_0_, girl0_.cup_size as cup_size3_0_ from girl girl0_
2019-12-31 10:07:19.851 INFO 1684 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : 222222222

日志获取请求信息

HttpAspect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.bennyrhys.girl.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Aspect //aop标识
@Component //添加到容器
public class HttpAspect {

//日志输出slf4j 注意类名切换
private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);


//提取公共方法
//拦截方法:加两个..表示任何参数都会被拦截
@Pointcut("execution(public * com.bennyrhys.girl.controller.GirlController.*(..))")
//先定方法不然没上方提示
public void log(){
}

//@Before方法执行之前就执行了
@Before("log()")
public void doBefore(JoinPoint joinPoint){
//此处强转
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//import javax.servlet.http.HttpServletRequest;
HttpServletRequest request = attributes.getRequest();
//url
logger.info("url={}", request.getRequestURL());
//method
logger.info("method={}",request.getMethod());
//ip
logger.info("ip={}",request.getRemoteAddr());
//类方法 参数传入doBefore(JoinPoint joinPoint)
//类名:getDeclaringTypeName类方法:getName
logger.info("class_method={}", joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());
//参数
logger.info("args={}", joinPoint.getArgs());
}


@After("log()")
//先定方法不然没上方提示
public void doAfter(){
logger.info("222222222");
}
}

控制台

2019-12-31 10:27:11.554 INFO 1816 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : url=http://localhost:8081/girl/girls/6
2019-12-31 10:27:11.554 INFO 1816 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : method=GET
2019-12-31 10:27:11.554 INFO 1816 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : ip=0:0:0:0:0:0:0:1
2019-12-31 10:27:11.555 INFO 1816 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : class_method=com.bennyrhys.girl.controller.GirlController.girlFindById
2019-12-31 10:27:11.555 INFO 1816 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : args=6
Hibernate: select girl0_.id as id1_0_0_, girl0_.age as age2_0_0_, girl0_.cup_size as cup_size3_0_0_ from girl girl0_ where girl0_.id=?
2019-12-31 10:27:11.603 INFO 1816 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : 222222222

日志获取响应信息

HttpAspect

1
2
3
4
5
6
//日志获取响应信息
//入参:returning 注意:toString方法,否则输出是对象
@AfterReturning(returning = "object", pointcut = "log()")
public void doAfterReturning(Object object){
logger.info("response={}", object.toString());
}

控制台

2019-12-31 10:36:42.905 INFO 1885 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : url=http://localhost:8081/girl/girls/6
2019-12-31 10:36:42.905 INFO 1885 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : method=GET
2019-12-31 10:36:42.905 INFO 1885 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : ip=0:0:0:0:0:0:0:1
2019-12-31 10:36:42.907 INFO 1885 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : class_method=com.bennyrhys.girl.controller.GirlController.girlFindById
2019-12-31 10:36:42.907 INFO 1885 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : args=6
Hibernate: select girl0_.id as id1_0_0_, girl0_.age as age2_0_0_, girl0_.cup_size as cup_size3_0_0_ from girl girl0_ where girl0_.id=?
2019-12-31 10:36:42.952 INFO 1885 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : 222222222
2019-12-31 10:36:42.952 INFO 1885 — [nio-8081-exec-1] com.bennyrhys.girl.aspect.HttpAspect : response=Girl{id=6, cupSize=’F’, age=16}

git提交

“AOP日志”

统一异常处理

封装返回值json

新建Result

  • domain包
    • Result.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.bennyrhys.girl.domain;

/**
* http请求返回对最外层对象
*/
//get/set
public class Result<T> {//具体内容用范型表示
//错误码
private Integer code;
//提示信息
private String msg;
//具体内容
private T data;
}

GirlController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 新增一个女生
* http://localhost:8081/girl/girls
* 注意:post请求
*
* 参数
* cupSize f
* age 16
*
* 插入因为自增注意id冲突
*/
@PostMapping(value = "/girls")
//@Valid表示要验证的是这个对象 BindingResult返回验证信息
public Result<Girl> girlAdd(@Valid Girl girl, BindingResult bindingResult){
//判断是否发生错误
if (bindingResult.hasErrors()){
//打印错误信息
Result result = new Result();
result.setCode(1);
result.setMsg(bindingResult.getFieldError().getDefaultMessage());
result.setData(null);
return result;
}
girl.setCupSize(girl.getCupSize());
girl.setAge(girl.getAge());
//save返回添加对对象

Result result = new Result();
result.setCode(0);
result.setMsg("成功");
result.setData(repository.save(girl));
return result;
}

验证返回json

访问:失败

http://localhost:8081/girl/girls

参数

cupSize D

age 17

响应信息

{

​ “code”: 1,

​ “msg”: “未成年少女禁止入内”,

​ “data”: null

}

参数

访问:成功

http://localhost:8081/girl/girls

参数

cupSize D

age 18

响应信息

{

​ “code”: 0,

​ “msg”: “成功”,

​ “data”: {

​ “id”: 14,

​ “cupSize”: “D”,

​ “age”: 18

​ }

}

代码优化-减少重复

新建

  • utils包
    • ResultUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.bennyrhys.girl.utils;

import com.bennyrhys.girl.domain.Result;

public class ResultUtil {
//成功-返回结果
public static Result success(Object object){
Result result = new Result();
result.setCode(0);
result.setMsg("成功");
result.setData(object);
return result;
}
//成功-没有返回结果
public static Result success(){
return success(null);
}
//失败
public static Result error(Integer code,String msg){
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
}

GirlController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 新增一个女生
* http://localhost:8081/girl/girls
* 注意:post请求
*
* 参数
* cupSize f
* age 16
*
* 插入因为自增注意id冲突
*/
@PostMapping(value = "/girls")
//@Valid表示要验证的是这个对象 BindingResult返回验证信息
public Result<Girl> girlAdd(@Valid Girl girl, BindingResult bindingResult){
//判断是否发生错误
if (bindingResult.hasErrors()){
//打印错误信息
return ResultUtil.error(1,bindingResult.getFieldError().getDefaultMessage());
}
girl.setCupSize(girl.getCupSize());
girl.setAge(girl.getAge());
//save返回添加对对象

return ResultUtil.success(repository.save(girl));
}

背景假设

获取女生年龄做判断

【 ,10),返回“应该在上小学”

【10,16),返回“可能在上初中”

业务分析

要如何把信息返回界面?

一般不是简单的一个方法就直接返回页面,业务逻辑复杂,因此一次判断类似一次表单验证,后续还有其他逻辑跟随

  • controller判断打标记返回,service继续根据不同标记进行(可以,繁琐,标记乱)
  • 统一处理,从service-》if抛异常,controller继续抛异常(推荐)

判断信息-外抛异常获取

GirlService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 获取女生年龄做判断
* 【 ,10),返回“应该在上小学”
*
* 【10,16),返回“可能在上初中”
*/
public void getAge(Integer id)throws Exception{
Optional<Girl> optional = repository.findById(id);
if (optional.isPresent()){
Girl girl = optional.get();
Integer age = girl.getAge();
if(age < 10){
//【 ,10),返回“应该在上小学”
throw new Exception("应该在上小学");

}else if (age >= 10 && age <16){
//【10,16),返回“可能在上初中”
throw new Exception("可能在上初中");

}
}
}

GirlController

1
2
3
4
5
6
7
8
9
10
11
/**
* 获取女生年龄做判断
* 【 ,10),返回“应该在上小学”
*
* 【10,16),返回“可能在上初中”
*/
@GetMapping(value = "/girls/getAge/{id}")
public void getAge(@PathVariable("id") Integer id)throws Exception{
//逻辑在service中判断
service.getAge(id);
}

访问

http://localhost:8081/girl/girls/getAge/3

响应异常信息

{

​ “timestamp”: “2019-12-31T08:41:32.890+0000”,

​ “status”: 500,

​ “error”: “Internal Server Error”,

​ “message”: “应该在上小学”,

​ “path”: “/girl/girls/getAge/3”

}

控制台

java.lang.Exception: 应该在上小学

git提交-判断信息-外抛异常获取

捕获异常封装返回json

新建

  • handle包
    • ExceptionHandle.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.bennyrhys.girl.handle;

import com.bennyrhys.girl.domain.Result;
import com.bennyrhys.girl.utils.ResultUtil;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* 异常捕获
*/
@ControllerAdvice
public class ExceptionHandle {
//声明捕获哪个类
@ExceptionHandler(value = Exception.class)
@ResponseBody //因为要返回给浏览器,上面又没写@restcontroller
public Result handle(Exception e){
return ResultUtil.error(100,e.getMessage());
}
}

响应捕获后的封装信息

//不足之处返回code都是100,在exception中只能传一个msg

http://localhost:8081/girl/girls/getAge/3

{

​ “code”: 100,

​ “msg”: “应该在上小学”,

​ “data”: null

}

http://localhost:8081/girl/girls/getAge/4

{

​ “code”: 100,

​ “msg”: “可能在上初中”,

​ “data”: null

}

重写Exception-多状态码

创建

  • exception包
    • GirlException.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.bennyrhys.girl.exception;

/**
* 重写Exception-多状态码
*
* 注意:
* 继承RuntimeException
* 因为spring事务回滚针对RuntimeException,而RuntimeException继承Exception。
* Exception不回滚
*/
public class GirlException extends RuntimeException{
private Integer code;
//get/set/构造

public GirlException(Integer code,String message) {
//添加字段message,父类本身就会传一个message进去
super(message);
this.code = code;
}


public Integer getCode() {
return code;
}

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

GirlService:抛出GirlException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 获取女生年龄做判断
* 【 ,10),返回“应该在上小学”
*
* 【10,16),返回“可能在上初中”
*/
public void getAge(Integer id)throws Exception{
Optional<Girl> optional = repository.findById(id);
if (optional.isPresent()){
Girl girl = optional.get();
Integer age = girl.getAge();
if(age < 10){
//【 ,10),返回“应该在上小学”
throw new GirlException(100,"应该在上小学");

}else if (age >= 10 && age <16){
//【10,16),返回“可能在上初中”
throw new GirlException(101,"可能在上初中");

}
}
}

ExceptionHandle:捕获时判断是否是自己定义的GirlException抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.bennyrhys.girl.handle;

import com.bennyrhys.girl.domain.Result;
import com.bennyrhys.girl.exception.GirlException;
import com.bennyrhys.girl.utils.ResultUtil;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* 异常捕获
*/
@ControllerAdvice
public class ExceptionHandle {
//声明捕获哪个类
@ExceptionHandler(value = Exception.class)
@ResponseBody //因为要返回给浏览器,上面又没写@restcontroller
public Result handle(Exception e){
//捕获时,判断一下异常是否是自己定义的GirlException
if (e instanceof GirlException){
GirlException girlException = (GirlException) e;
return ResultUtil.error(girlException.getCode(),girlException.getMessage());
}else {
return ResultUtil.error(-1,"未知错误");
}
}
}

验证

http://localhost:8081/girl/girls/getAge/3

{

​ “code”: 100,

​ “msg”: “应该在上小学”,

​ “data”: null

}

http://localhost:8081/girl/girls/getAge/4

{

​ “code”: 101,

​ “msg”: “可能在上初中”,

​ “data”: null

}

http://localhost:8081/girl/girls/getAge/5

{

​ “code”: -1,

​ “msg”: “未知错误”,

​ “data”: null

}

未知异常排除-打日志

1
2
3
4
5
//日志
private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);

//将未知异常通过日志排除
logger.error("[系统异常]={}",e);

ExceptionHandle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.bennyrhys.girl.handle;

import com.bennyrhys.girl.domain.Result;
import com.bennyrhys.girl.exception.GirlException;
import com.bennyrhys.girl.utils.ResultUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* 异常捕获
*/
@ControllerAdvice
public class ExceptionHandle {
//日志
private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);

//声明捕获哪个类
@ExceptionHandler(value = Exception.class)
@ResponseBody //因为要返回给浏览器,上面又没写@restcontroller
public Result handle(Exception e){
//捕获时,判断一下异常是否是自己定义的GirlException
if (e instanceof GirlException){
GirlException girlException = (GirlException) e;
return ResultUtil.error(girlException.getCode(),girlException.getMessage());
}else {
//将未知异常通过日志排除
logger.error("[系统异常]={}",e);
return ResultUtil.error(-1,"未知错误");
}
}
}

管理异常code和msg-枚举

创建枚举

  • enums包
    • ResultEnum.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.bennyrhys.girl.enums;

/**
* 管理异常code和msg-枚举
* 注意:枚举类型文件
*/
public enum ResultEnum {
UNKNOW_ERROR(-1,"未知错误"),
SUCCESS(0,"成功"),
PRIMARY_SCHOOL(100,"可能小学"),
MIDDLE_SCHOOL(101,"可能中学"),
;
private Integer code;
private String msg;
// 构造方法(两个参数),只get方法(因为枚举都是直接用构造方法创建,不用再set了)

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

public Integer getCode() {
return code;
}

public String getMsg() {
return msg;
}
}

GirlService: 返回枚举的类型,GirlException返回值处理避免爆红

1
2
3
4
5
6
7
8
9
if(age < 10){
//【 ,10),返回“应该在上小学”
throw new GirlException(ResultEnum.PRIMARY_SCHOOL);

}else if (age >= 10 && age <16){
//【10,16),返回“可能在上初中”
throw new GirlException(ResultEnum.MIDDLE_SCHOOL);

}

GirlException

1
2
3
4
5
public GirlException(ResultEnum resultEnum) {
//添加字段message,父类本身就会传一个message进去
super(resultEnum.getMsg());
this.code = resultEnum.getCode();
}

验证

http://localhost:8081/girl/girls/getAge/3

{

​ “code”: 100,

​ “msg”: “可能小学”,

​ “data”: null

}

http://localhost:8081/girl/girls/getAge/4

{

​ “code”: 101,

​ “msg”: “可能中学”,

​ “data”: null

}

http://localhost:8081/girl/girls/getAge/5

{

​ “code”: -1,

​ “msg”: “未知错误”,

​ “data”: null

}

git提交-异常处理-枚举

单元测试

测试service

GirlService

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 测试service
* 通过id查询一个女生信息并返回
*/
public Girl findOne(Integer id){
Optional<Girl> optional = repository.findById(id);
if (optional.isPresent()){
Girl girl = optional.get();
return girl;
}
return null;
}

法一:GirlServiceTest //手动test包下创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.bennyrhys.girl;

import com.bennyrhys.girl.domain.Girl;
import com.bennyrhys.girl.service.GirlService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.Assert;

@SpringBootTest //启动整个spring工程
public class GirlServiceTest {
@Autowired
private GirlService girlService;

@Test
public void findOneTest(){
Girl girl = girlService.findOne(3);
//断言比较,测试通过 新版本适用Assertions
Assertions.assertEquals(9,girl.getAge());
}
}

法二:service方法上goto-》test 自动创建

test包下自动创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.bennyrhys.girl.service;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

/**
* 自动创建的测试unit5
*/
class GirlServiceTest {

@Test
void findOne() {
}
}

测试API

contoller-》getlist-〉goto自动创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.bennyrhys.girl.controller;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@AutoConfigureMockMvc //检测api
class GirlControllerTest {

@Autowired
private GirlController girlController;
@Autowired
private MockMvc mvc;

@Test
void girlList() throws Exception {
// 不是这样测试,这样测试没有报错,但没检测到api及请求类型
// girlController.girlList();
mvc.perform(MockMvcRequestBuilders.get("/girls"))
.andExpect(MockMvcResultMatchers.status().isOk()) //期望返回状态码200,测试通过
.andExpect(MockMvcResultMatchers.content().string("abc")); //期望返回string=abc,测试肯定不通过
/**
* 检测abc返回,控制台输出
* Expected :abc
* Actual :[{"id":3,"cupSize":"G","age":9},{"id":4,"cupSize":"G","age":15},{"id":5,"cupSize":"F","age":22},{"id":6,"cupSize":"F","age":16},{"id":7,"cupSize":"F","age":16},{"id":8,"cupSize":"C","age":22},{"id":9,"cupSize":"C","age":20},{"id":10,"cupSize":"C","age":18},{"id":11,"cupSize":"D","age":19},{"id":12,"cupSize":"D","age":18},{"id":13,"cupSize":"D","age":18},{"id":14,"cupSize":"D","age":18},{"id":15,"cupSize":"D","age":18}]
*/
}
}

测试用例每个都要手动?

不需要,打包时自动测试统计成功失败

//项目根目录命令打包自动检测

mvn clean package

//之前故意放了一个错误测试

[ERROR] Failures:

[ERROR] GirlControllerTest.girlList:27 Response content expected: but was:<[{“id”:3,”cupSize”:”G”,”age”:9},{“id”:4,”cupSize”:”G”,”age”:15},{“id”:5,”cupSize”:”F”,”age”:22},{“id”:6,”cupSize”:”F”,”age”:16},{“id”:7,”cupSize”:”F”,”age”:16},{“id”:8,”cupSize”:”C”,”age”:22},{“id”:9,”cupSize”:”C”,”age”:20},{“id”:10,”cupSize”:”C”,”age”:18},{“id”:11,”cupSize”:”D”,”age”:19},{“id”:12,”cupSize”:”D”,”age”:18},{“id”:13,”cupSize”:”D”,”age”:18},{“id”:14,”cupSize”:”D”,”age”:18},{“id”:15,”cupSize”:”D”,”age”:18}]>

[INFO]

[ERROR] Tests run: 4, Failures: 1, Errors: 0, Skipped: 0

//改正错误test

[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

[INFO]

[INFO]

[INFO] — maven-jar-plugin:3.1.2:jar (default-jar) @ girl —

[INFO] Building jar: /Users/bennyrhys/Documents/Idea_Demo/GirlPlus-SpringBootProject/girl/target/girl-0.0.1-SNAPSHOT.jar

[INFO]

[INFO] — spring-boot-maven-plugin:2.2.2.RELEASE:repackage (repackage) @ girl —

[INFO] Replacing main artifact with repackaged archive

[INFO] ————————————————————————

[INFO] BUILD SUCCESS

跳过单元测试

//根目录下meaven命令

mvn clean package -Dmaven.test.skip=true

//跳过测试,直接打包成功

[INFO] BUILD SUCCESS

[INFO] ————————————————————————

[INFO] Total time: 3.761 s

[INFO] Finished at: 2019-12-31T18:43:07+08:00

[INFO] ————————————————————————

bennyrhysdeMacBook-Pro:girl bennyrhys$

git提交-单元测试

Girl-SpringBootProject

简介

SpringBoot两小时快速入门,基因芯片个人信息程序

你知道基因芯片吗?当今人类文明在生物及科技的进步,极大程度推动大数据发展,人类基因芯片计划也在不断推

进完成。Girl-SpringBootProject模拟人体特征,人类基因组计划,处理基因芯片信息。

信息时代最重视的是什么?是效率!企业最重视的人才是什么?是高效能人士!所谓“天下武功唯快不破 ” ,为此专门推出此教程,让你快速Get到SpringBoot实际应用技能。本教程所有代码、笔记由bennyrhys免费提供,遵循”学习其实可以很快乐“的原则,以极客浪漫情怀的”红包表白程序“为场景,贯穿微服务必学框架-SpringBoot相关内容,将初学SpringBoot的踩坑点进行指点,为同学打造麻雀虽小五脏俱全的教程。标题风趣幽默,必然让你本次的学习之旅尽兴而归。载着干货的老司机,在召唤你~

链接

GitHub:点击预览——基因芯片个人信息程序

详细文档:点击搜索——基因芯片个人信息程序

目录

  • 【别让宝宝输在起跑线上】版本配置
  • 【初识】第一个程序hello world
  • 【一万个哈姆雷特】花式启动程序
  • 【君子之交-淡如水】解放双手,配置先行配置
    • 选型场景模拟-个人信息
    • 单个配置
    • git提交-单配置-master
    • 多个配置
    • git提交-多配置-master
  • 【我的命,我自己说的才算】生产/开发环境切换
    • 场景模拟
    • 配置
    • 验证
    • git提交-生产环境切换
  • 【人生若只如初见】从“程序”入口Controller开始
    • @controller+thymeleaf
    • git提交-@controller+thymeleaf
    • @RestController=@ResponseBody+@Controller
    • girl/hello ==girl/hi
    • url访问一层girl/say/hi
    • Post访问
    • get/post都可访问(不推荐)
    • url参数获取
    • @PathVariable:url中数据
    • @RequestParam:请求参数
    • 空id,设置默认值
    • 简化请求注解
    • git提交-controller
  • 【从删库到跑路】撸程序-操作数据库
    • spring-data-jpa+mysql
    • RESTful API设计
    • pom依赖添加 jpa+mysqlyml
    • 配置mysql驱动+jpa创建表
    • Girl //表字段
    • Dao层:操作数据库
    • controller实现增删改查
  • 【搞定并发就在一起】事务要么都完成要么都不做
    • 使用场景
    • GirlService
    • GirlController

[TOC]

【别让宝宝输在起跑线上】版本配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
bennyrhysdeMacBook-Pro:bennyrhys bennyrhys$ java -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
bennyrhysdeMacBook-Pro:bennyrhys bennyrhys$ mvn -v
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)

IDEA旗舰版
温馨提醒:学生邮箱注册旗舰版免费使用

创建springboot2.2.2-》web模块

推荐:meaven镜像仓库
vim ~/.m2/settings.xml
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>

<!--配置profiles节点-->
<profiles>
<profile>
<id>jdk-1.8</id>
<activation>
<jdk>1.8</jdk>
</activation>
<repositories>
<repository>
<id>nexus</id>
<name>local private nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

</profile>

【初识】第一个程序hello world

HelloController//新建controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.bennyrhys.girl;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String say(){
return "hello world";
}
}

访问

http://localhost:8080/hello

输出

hello world

【一万个哈姆雷特】花式启动程序

第二种启动方式:命令行meaven

mvn spring-boot:run

第三种启动方式:

//编译程序

mvn install

//进入到target目录下 girl-0.0.1-SNAPSHOT.jar启动

java -jar girl-0.0.1-SNAPSHOT.jar

//启动时配置切换生产环境

java -jar target/girl-0.0.1-SNAPSHOT.jar –spring.profiles.active=prod

【君子之交-淡如水】解放双手,配置先行

配置选型

application.properties

1
2
server.port=8081
server.servlet.context-path=/girl

application.yml

推荐,但注意衔接留空格

1
2
3
4
server:
port: 8081
servlet:
context-path: /girl

http://localhost:8081/girl/hello

场景模拟-个人信息

girl程序,模拟人体特征,处理个人信息。

当今人类文明在生物及科技的进步,极大程度推动大数据发展,人类基因芯片计划也在不断推进完成。

随着人类基因组(测序)计划( Human genome project )的逐步实施以及分子生物学相关学科的迅猛发展,越来越多的动植物、微生物基因组序列得以测定,基因序列数据正在以前所未有的速度迅速增长。然而 , 怎样去研究如此众多基因在生命过程中所担负的功能就成了全世界生命科学工作者共同的课题。为此,建立新型杂交和测序方法以对大量的遗传信息进行高效、快速的检测、分析就显得格外重要了。

单个配置

application.yml

1
2
3
4
5
6
7
8
server:
port: 8080
servlet:
context-path: /girl

cupSize: B #罩杯
age: 18 #年龄
content: "cupSize:${cupSize},age:${age}" #引用配置中的配置

HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.bennyrhys.girl;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
//通过注解获取配置中的属性
@Value("${cupSize}")
private String cupSize;
@Value("${age}")
private Integer age;
@Value("${content}")
private String content;


@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String say(){
return cupSize+" "+age+"content:"+content;
}
}

访问

http://localhost:8080/girl/hello

输出

B 18contentcupSize:B,age:18

git提交-单配置-master

bennyrhysdeMacBook-Pro:Idea_Demo bennyrhys$ git clone https://github.com/bennyrhys/Girl-SpringBootProject.git girl

Cloning into ‘girl’…

remote: Enumerating objects: 6, done.

remote: Counting objects: 100% (6/6), done.

remote: Compressing objects: 100% (4/4), done.

remote: Total 6 (delta 1), reused 0 (delta 0), pack-reused 0

Unpacking objects: 100% (6/6), done.

bennyrhysdeMacBook-Pro:Idea_Demo bennyrhys$ cd Girl-SpringBootProject/

bennyrhysdeMacBook-Pro:Girl-SpringBootProject bennyrhys$ git status

On branch master

Your branch is up to date with ‘origin/master’.

Untracked files:

(use “git add …” to include in what will be committed)

​ girl/

​ girl_note.md

nothing added to commit but untracked files present (use “git add” to track)

bennyrhysdeMacBook-Pro:Girl-SpringBootProject bennyrhys$ git remote

origin

bennyrhysdeMacBook-Pro:Girl-SpringBootProject bennyrhys$ git add .

bennyrhysdeMacBook-Pro:Girl-SpringBootProject bennyrhys$ git commit -m “单配置”

[master 8545dc9] 单配置

7 files changed, 337 insertions(+)

create mode 100644 girl/.gitignore

create mode 100644 girl/pom.xml

create mode 100644 girl/src/main/java/com/bennyrhys/girl/GirlApplication.java

create mode 100644 girl/src/main/java/com/bennyrhys/girl/HelloController.java

create mode 100644 girl/src/main/resources/application.yml

create mode 100644 girl/src/test/java/com/bennyrhys/girl/GirlApplicationTests.java

create mode 100644 girl_note.md

bennyrhysdeMacBook-Pro:Girl-SpringBootProject bennyrhys$ git branch

* master

bennyrhysdeMacBook-Pro:Girl-SpringBootProject bennyrhys$ git push origin master

Enumerating objects: 23, done.

Counting objects: 100% (23/23), done.

Delta compression using up to 4 threads

Compressing objects: 100% (14/14), done.

Writing objects: 100% (22/22), 4.80 KiB | 2.40 MiB/s, done.

Total 22 (delta 0), reused 0 (delta 0)

To https://github.com/bennyrhys/Girl-SpringBootProject.git

d4be8b7..8545dc9 master -> master

多个配置

application.yml

1
2
3
4
5
6
7
server:
port: 8080
servlet:
context-path: /girl
girl:
cupSize: B
age: 18

GirlConfig //新建配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.bennyrhys.girl;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

//对应配置文件的girl
@ConfigurationProperties(prefix = "girl")
//注入配置
@Component
public class GirlConfig {
private String cupSize;
private Integer age;

public String getCupSize() {
return cupSize;
}

public void setCupSize(String cupSize) {
this.cupSize = cupSize;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}

HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.bennyrhys.girl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
//通过注解获取配置:类中的属性
@Autowired
private GirlConfig girlConfig;



@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String say(){
return girlConfig.getCupSize()+girlConfig.getAge();
}
}

访问

http://localhost:8080/girl/hello

输出

B18

git提交-多配置-master

bennyrhysdeMacBook-Pro:Girl-SpringBootProject bennyrhys$ git add .

bennyrhysdeMacBook-Pro:Girl-SpringBootProject bennyrhys$ git commit -m “多配置”

[master 167fc1a] 多配置

4 files changed, 195 insertions(+), 14 deletions(-)

create mode 100644 girl/src/main/java/com/bennyrhys/girl/GirlConfig.java

bennyrhysdeMacBook-Pro:Girl-SpringBootProject bennyrhys$ git push origin master

Enumerating objects: 26, done.

Counting objects: 100% (26/26), done.

Delta compression using up to 4 threads

Compressing objects: 100% (10/10), done.

Writing objects: 100% (14/14), 2.37 KiB | 1.18 MiB/s, done.

Total 14 (delta 3), reused 0 (delta 0)

remote: Resolving deltas: 100% (3/3), completed with 3 local objects.

To https://github.com/bennyrhys/Girl-SpringBootProject.git

8545dc9..167fc1a master -> master

【我的命,我自己说的才算】生产/开发环境切换

场景模拟

dev:生产环境,先天cupSzie:B

Prod:开发环境,后天想达到cupSize:F

配置

application-dev.yml

1
2
3
4
5
6
7
server:
port: 8081
servlet:
context-path: /girl
girl:
cupSize: B
age: 18

application-prod.yml

1
2
3
4
5
6
7
server:
port: 8081
servlet:
context-path: /girl
girl:
cupSize: F
age: 18

application.yml

1
2
3
spring:
profiles:
active: prod

验证

访问

http://localhost:8082/girl/hello

输出

F18

git提交-生产环境切换

bennyrhysdeMacBook-Pro:girl bennyrhys$ git add .

bennyrhysdeMacBook-Pro:girl bennyrhys$ git commit -m “生产环境切换”

[master 3c7eca6] 生产环境切换

3 files changed, 17 insertions(+), 7 deletions(-)

create mode 100644 girl/src/main/resources/application-dev.yml

create mode 100644 girl/src/main/resources/application-prod.yml

bennyrhysdeMacBook-Pro:girl bennyrhys$ git push origin master

Enumerating objects: 15, done.

Counting objects: 100% (15/15), done.

Delta compression using up to 4 threads

Compressing objects: 100% (8/8), done.

Writing objects: 100% (9/9), 830 bytes | 830.00 KiB/s, done.

Total 9 (delta 1), reused 0 (delta 0)

remote: Resolving deltas: 100% (1/1), completed with 1 local object.

To https://github.com/bennyrhys/Girl-SpringBootProject.git

167fc1a..3c7eca6 master -> master

#【人生若只如初见】从“程序”入口Controller开始

@Controller 处理http请求
@RestController Spring4之后新加的注解
原来返回json需要@ResponseBody配合@Controller
@RequestMapping 配置url映射

@controller+thymeleaf

@controller单独写,访问时会找不到模版,必须在pom中添加thymeleaf模版依赖

pom.xml

1
2
3
4
5
<!--        添加thymeleaf依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.bennyrhys.girl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@Controller
public class HelloController {
//通过注解获取配置:类中的属性
@Autowired
private GirlConfig girlConfig;



@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String say(){
// return girlConfig.getCupSize()+girlConfig.getAge();
return "index";
}
}

访问:

http://localhost:8081/girl/hello

输出:

hello thymeleaf

git提交-@controller+thymeleaf

bennyrhysdeMacBook-Pro:girl bennyrhys$ git add .

bennyrhysdeMacBook-Pro:girl bennyrhys$ git commit -m “@controller+thymeleaf”

[master 3d23c58] @controller+thymeleaf

5 files changed, 22 insertions(+), 4 deletions(-)

create mode 100644 girl/src/main/resources/templates/index.html

bennyrhysdeMacBook-Pro:girl bennyrhys$ git push origin master

Enumerating objects: 29, done.

Counting objects: 100% (29/29), done.

Delta compression using up to 4 threads

Compressing objects: 100% (12/12), done.

Writing objects: 100% (16/16), 1.35 KiB | 692.00 KiB/s, done.

Total 16 (delta 4), reused 0 (delta 0)

remote: Resolving deltas: 100% (4/4), completed with 4 local objects.

To https://github.com/bennyrhys/Girl-SpringBootProject.git

3c7eca6..3d23c58 master -> master

@RestController=@ResponseBody+@Controller

主要作用见标题

其次可以用在类上、方法体上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.bennyrhys.girl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
//@ResponseBody
@Controller
public class HelloController {
//通过注解获取配置:类中的属性
@Autowired
private GirlConfig girlConfig;


@ResponseBody
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String say(){
return girlConfig.getCupSize()+girlConfig.getAge();
// return "index";
}
}

访问

http://localhost:8081/girl/hello

输出

F18

girl/hello ==girl/hi

快捷键:查看列表可用参数command+p

1
@RequestMapping(value = {"/hello","/hi"}, method = RequestMethod.GET)

http://localhost:8081/girl/hi

F18

http://localhost:8081/girl/hello

F18

url访问一层girl/say/hi

注解@RequestMapping(“/say”)

HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.bennyrhys.girl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/say")
public class HelloController {
//通过注解获取配置:类中的属性
@Autowired
private GirlConfig girlConfig;


@RequestMapping(value = {"/hello","/hi"}, method = RequestMethod.GET)
public String say(){
return girlConfig.getCupSize()+girlConfig.getAge();
// return "index";
}
}

http://localhost:8081/girl/say/hi

F18

Post访问

1
@RequestMapping(value = {"/hello","/hi"}, method = RequestMethod.POST)

页面无法直接访问,用postman

get/post都可访问(不推荐)

@RequestMapping(value = {“/hello”,”/hi”})

具体业务场景,要严谨

url参数获取

@PathVariable 获取url中数据
@RequestParam 获取请求参数的值
@GetMapping 组合注解

@PathVariable:url中数据

HelloController

@RequestMapping(value = {“/hi/{id}”},id在前后都行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.bennyrhys.girl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/say")
public class HelloController {
//通过注解获取配置:类中的属性
@Autowired
private GirlConfig girlConfig;

@RequestMapping(value = {"/hi/{id}"}, method = RequestMethod.GET)
public String say(@PathVariable("id") Integer id){
// return girlConfig.getCupSize()+girlConfig.getAge();
return "id:"+id;
}
}

http://localhost:8081/girl/say/hi/99

id:99

@RequestParam:请求参数

1
@RequestParam("id") Integer id  //这参数的两个id不用对应,前者等同于url的id

HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.bennyrhys.girl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/say")
public class HelloController {
//通过注解获取配置:类中的属性
@Autowired
private GirlConfig girlConfig;

@RequestMapping(value = {"/hi"}, method = RequestMethod.GET)
public String say(@RequestParam("id") Integer myid){
// return girlConfig.getCupSize()+girlConfig.getAge();
return "id:"+myid;
}
}

http://localhost:8081/girl/say/hi?id=99

id:99

空id,设置默认值

http://localhost:8081/girl/say/hi?id=

id:null

http://localhost:8081/girl/say/hi

(type=Bad Request, status=400).

设置默认值

1
2
//required = false 是否必传
public String say(@RequestParam(value = "id", required = false, defaultValue = "0") Integer myid){

http://localhost:8081/girl/say/hi

id:0

简化请求注解

http://localhost:8081/girl/say/hi?id=2

id:2

有默认值

http://localhost:8081/girl/say/hi

id:0

mapping是一个系列get、post、put

1
2
//    @RequestMapping(value = {"/hi"}, method = RequestMethod.GET)
@GetMapping(value = "/hi")

HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.bennyrhys.girl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/say")
public class HelloController {
//通过注解获取配置:类中的属性
@Autowired
private GirlConfig girlConfig;

// @RequestMapping(value = {"/hi"}, method = RequestMethod.GET)
@GetMapping(value = "/hi")

public String say(@RequestParam(value = "id", required = false, defaultValue = "0") Integer myid){
// return girlConfig.getCupSize()+girlConfig.getAge();
return "id:"+myid;
}
}

git提交-controller

bennyrhysdeMacBook-Pro:girl bennyrhys$ git add .

bennyrhysdeMacBook-Pro:girl bennyrhys$ git commit -m “controller”

[master b0517fe] controller

1 file changed, 7 insertions(+), 9 deletions(-)

bennyrhysdeMacBook-Pro:girl bennyrhys$ git push origin master

Enumerating objects: 19, done.

Counting objects: 100% (19/19), done.

Delta compression using up to 4 threads

Compressing objects: 100% (7/7), done.

Writing objects: 100% (10/10), 885 bytes | 885.00 KiB/s, done.

Total 10 (delta 3), reused 0 (delta 0)

remote: Resolving deltas: 100% (3/3), completed with 3 local objects.

To https://github.com/bennyrhys/Girl-SpringBootProject.git

3d23c58..b0517fe master -> master

【从删库到跑路】撸程序-操作数据库

spring-data-jpa+mysql

java客户端:spring-data-jpa

jpa是对象持久化标准

实现这标准hibernate、toplink等

spring-data-jpa,是Spring对hibernate整合

数据库:mysql

RESTful API设计

请求类型 请求路径 功能
get /girls 获取女生列表
post /girls 创建一个女生
get /girls/id 通过id查询一个女生
put /girls/id 通过id更新一个女生
delete /girls/id 通过id删除一个女生

pom依赖添加 jpa+mysql

1
2
3
4
5
6
7
8
9
<!--        添加jpa+mysql-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

Error:(1, 1) java: 无法访问com.bennyrhys
Error reading file /Users/bennyrhys/.m2/repository/org/springframework/boot/spring-boot-starter-data-jpa/2.2.2.RELEASE/spring-boot-starter-data-jpa-2.2.2.RELEASE.jar: error in opening zip file

yml配置mysql驱动+jpa创建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 8081
servlet:
context-path: /girl

girl:
cupSize: B
age: 18
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/girl?characterEncoding=utf-8
username: root
password: rootroot
jpa:
hibernate:
ddl-auto: create #update第一次创建,后来如果有数据会保留
show-sql: true

Girl //表字段

构造方法/get/set,千万不要导包错误

1
2
3
4
5
6
7
8
9
10
11
12
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

//表示对应数据库对表
@Entity
public class Girl {
@Id //指定唯一id
@GeneratedValue //自增
private Integer id;
private String cupSize;
private Integer age;

Dao层:操作数据库

GirlRepository

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.bennyrhys.girl;

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/**
* dao
* 注意命名,继承jpa,参数<字段类,唯一id类型>
* extends JpaRepository<Girl,Integer>
*/
public interface GirlRepository extends JpaRepository<Girl,Integer> {
//通过年龄来查询,方法名有讲究
public List<Girl> findByAge(Integer age);
}

controller实现增删改查

GirlController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package com.bennyrhys.girl;

import com.sun.org.apache.regexp.internal.RE;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
public class GirlController {

@Autowired
private GirlRepository repository;

/**
* 查询所有女生列表
* @return
* http://localhost:8081/girl/girls
* [
* {
* "id": 1,
* "cupSize": "B",
* "age": 18
* }
* ]
*/
@GetMapping(value = "/girls")
public List<Girl> girlList(){
return repository.findAll();
}
/**
* 新增一个女生
* http://localhost:8081/girl/girls
* 注意:post请求
*
* 参数
* cupSize f
* age 16
*
* 插入因为自增注意id冲突
*/
@PostMapping(value = "/girls")
public Girl girlAdd(@RequestParam("cupSize") String cupSize,
@RequestParam("age") Integer age){
Girl girl = new Girl();
girl.setCupSize("F");
girl.setAge(16);
//save返回添加对对象
return repository.save(girl);
}
/**
* 通过id查询一个女生
* @return
* http://localhost:8081/girl/girl/2
* {
* "id": 2,
* "cupSize": "F",
* "age": 16
* }
*/
@GetMapping(value = "/girls/{id}")
public Girl girlFindById(@PathVariable("id") Integer id){
return repository.findById(id).orElse(null);

}

/**
* 更新某个id女生基因信息
* http://localhost:8081/girl/girls/2
* 参数
* cupSize G
* age 22
*
* {
* "id": 2,
* "cupSize": "G",
* "age": 22
* }
*/
@PutMapping(value = "/girls/{id}")
public Girl updateGirl(@PathVariable("id") Integer id,
@RequestParam("cupSize") String cupSize,
@RequestParam("age") Integer age){
Optional<Girl> optional = repository.findById(id);
if (optional.isPresent()){
Girl girl = optional.get();
girl.setAge(age);
girl.setCupSize(cupSize);
return girl;
}
return null;
}
/**
*删除某个id女生的信息
* http://localhost:8081/girl/girls/2
* 2
*/
@DeleteMapping(value = "/girls/{id}")
public Integer deleteGirlById(@PathVariable("id") Integer id){
Optional<Girl> optional = repository.findById(id);
if (optional.isPresent()){
Girl girl = optional.get();
repository.delete(girl);
return id;
}
return null;
}
/**
* 通过年龄查询女生列表
* 先自定义dao方法
* http://localhost:8081/girl/girls/age/16
* [
* {
* "id": 3,
* "cupSize": "F",
* "age": 16
* },
* {
* "id": 4,
* "cupSize": "g",
* "age": 16
* },
*/
@GetMapping("/girls/age/{age}")
public List<Girl> girlListByAge(@PathVariable("age") Integer age){
return repository.findByAge(age);
}


}

【搞定并发就在一起】事务要么都完成要么都不做

使用场景

当某个名声显赫的家族要新增整个家族的女性优质基因时,为了事情不败露,必须保证同时实现基因的改变。

事务:要么都完成要么都不做

@Transactional //保证同时发生的注解,spring版

GirlService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.bennyrhys.girl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class GirlService {
@Autowired
private GirlRepository repository;

@Transactional //保证同时发生的注解,spring版
public void changgeTwo(){
Girl girl = new Girl();
girl.setId(3);
girl.setAge(20);
girl.setCupSize("G");
repository.save(girl);

Girl girl2 = new Girl();
girl2.setId(4);
girl2.setAge(20);
girl2.setCupSize("G");
repository.save(girl2);
}
}

GirlController

1
2
3
4
5
6
7
8
9
10
/**
* 触发事务
* 同时改变基因cupSize
* http://localhost:8081/girl/girls/two
* 数据发生改变
*/
@PostMapping(value = "/girls/two")
public void girlsTwo(){
service.changgeTwo();
}

[TOC]

【让宝宝不要输在起跑线】文档说明

作者:bennyrhys@163.com

版本配置

Jdk1.8.111

meaven3.5.4

springboot2.1.3

【初识】SpringBoot-hello world

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.bennyrhys.luckymoney;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
// @GetMapping("/hello")
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello(){
return "hello world";
}
}

命令行启动

bennyrhys$ mvn spring-boot:run```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

终止

ctrl+c

## 解决打包失败

```xml
<!-- 为了打包后加的-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>

查看jar包

ls target/

运行jar

java -jar target/luckymoney-0.0.1-SNAPSHOT.jar

访问:http://localhost:8080/hello

配置文件

application.properties

1
2
server.servlet.context-path=/luckymoney
server.port=8081

application.yml(推荐)

1
2
3
4
server:
port: 8081
servlet:
context-path: /luckymoney

访问:http://localhost:8081/luckymoney/hello

git提交 :helloworld+yml启动及简单配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//初始化
springboot2小时浪漫红包 bennyrhys$ git init
//配置提交用户
git config user.name bennyrhys
git config user.email bennyrhys@163.com
//验证用户信息
cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[user]
name = bennyrhys
email = bennyrhys@163.com
//查看文件状态
git status
On branch master

No commits yet

Untracked files:
(use "git add <file>..." to include in what will be committed)

.DS_Store
luckymoney/
//添加修改文件到暂存区
git add luckymoney/
//添加文件到数据库
git commit -m "hello world+yml 简单配置启动"
//建立远程连接
//列出已经存在的远程分支 -v详情
git remote -v
//创建远程分支
git remote add origin_lm https://github.com/bennyrhys/LuckyMoney.git
//检测分支
git remote -v
origin_lm https://github.com/bennyrhys/LuckyMoney.git (fetch)
origin_lm https://github.com/bennyrhys/LuckyMoney.git (push)
//此处切记更改上传人密钥信息
//push文件
git push origin_lm master
//远程仓库名冲突,修改远程分支
git remote remove origin_lm
//新建远程分支
git remote add origin_lm https://github.com/bennyrhys/LuckyMoney-SpringBootProject.git
//因为github原有README.md文档需要解决冲突
git pull origin_lm master
//git push origin_lm master

【君子之交-淡如水】解放双手,配置红包

手动单个配置

1 .application.yml

1
2
3
mixMoney: 1
maxMoney: 99
description: 最少要发${mixMoney}元

2 .HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
public class HelloController {
//法1:手动引入配置信息
@Value("${mixMoney}")
private BigDecimal mixMoney;
@Value("${description}")
private String description;


@GetMapping("/hello")
// @RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello(){
return "mixMoney"+mixMoney+"说明"+description;
}
}
  1. 访问http://localhost:8081/luckymoney/hello
  2. 输出:mixMoney1说明最少要发1元
  3. Git上传到分支confmoney
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git branch
* master
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git checkout -b confmoney
M luckymoney/src/main/java/com/bennyrhys/luckymoney/HelloController.java
M luckymoney/src/main/resources/application.yml
M luckymoney_note.md
Switched to a new branch 'confmoney'
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git branch
* confmoney
master
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git status
On branch confmoney
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: luckymoney/src/main/java/com/bennyrhys/luckymoney/HelloController.java
modified: luckymoney/src/main/resources/application.yml
modified: luckymoney_note.md

Untracked files:
(use "git add <file>..." to include in what will be committed)

.DS_Store
luckymoney/.DS_Store

no changes added to commit (use "git add" and/or "git commit -a")
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git add .
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git commit -m "红包金额-手动配置"
[confmoney bde8c36] 红包金额-手动配置
5 files changed, 59 insertions(+), 5 deletions(-)
create mode 100644 .DS_Store
create mode 100644 luckymoney/.DS_Store
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git remote -v
origin https://github.com/bennyrhys/LuckyMoney-SpringBootProject.git (fetch)
origin https://github.com/bennyrhys/LuckyMoney-SpringBootProject.git (push)
origin_lm https://github.com/bennyrhys/LuckyMoney-SpringBootProject.git (fetch)
origin_lm https://github.com/bennyrhys/LuckyMoney-SpringBootProject.git (push)
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git push origin_lm confmoney
Enumerating objects: 27, done.
Counting objects: 100% (27/27), done.
Delta compression using up to 4 threads
Compressing objects: 100% (11/11), done.
Writing objects: 100% (15/15), 2.37 KiB | 2.37 MiB/s, done.
Total 15 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 2 local objects.
remote:
remote: Create a pull request for 'confmoney' on GitHub by visiting:
remote: https://github.com/bennyrhys/LuckyMoney-SpringBootProject/pull/new/confmoney
remote:
To https://github.com/bennyrhys/LuckyMoney-SpringBootProject.git
* [new branch] confmoney -> confmoney

自动多个配置红包金额【limit类限制金额范围】

  1. application.yml
1
2
3
4
limit:
minMoney: 1
maxMoney: 99
description: 最少要发${limit.minMoney}元,最多发${limit.maxMoney}元
  1. LimitConfig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;

@RestController
public class HelloController {

//法2;自动注入
@Autowired
LimitConfig limitConfig;


@GetMapping("/hello")
public String hello(){
return "说明"+limitConfig.getDescription();
}
}
  1. HelloController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;

@RestController
public class HelloController {

//法2;自动注入
@Autowired
LimitConfig limitConfig;


@GetMapping("/hello")
public String hello(){
return "说明"+limitConfig.getDescription();
}
}
  1. 输出

http://localhost:8081/luckymoney/hello

说明最少要发1元,最多发99元

  1. git提交
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git branch
* confmoney
master
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git status
On branch confmoney
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: luckymoney/src/main/java/com/bennyrhys/luckymoney/LimitConfig.java

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: README.md
modified: luckymoney/src/main/java/com/bennyrhys/luckymoney/HelloController.java
modified: luckymoney/src/main/java/com/bennyrhys/luckymoney/LimitConfig.java
modified: luckymoney/src/main/resources/application.yml
modified: luckymoney_note.md

Untracked files:
(use "git add <file>..." to include in what will be committed)

README.assets/

bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git add .
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git commit -m "limit类限制金额自动配置"

生成环境开发环境划分

  1. 思路

dev开发模式

prod生产模式

由yml控制选择模式

场景

dev:测试0.1 元

prod:正式1元

  1. application-dev.yml
1
2
3
spring:
profiles:
active: dev #设定部署的测试配置开发、生产(如果正式上线,可以不修改此处,打包后命令切换环境)
  1. application-dev.yml
1
2
3
4
5
6
7
8
server:
port: 8081
servlet:
context-path: /luckymoney
limit:
minMoney: 0.1
maxMoney: 99
description: 最少要发${limit.minMoney}元,最多发${limit.maxMoney}元
  1. application-prod.yml
1
2
3
4
5
6
7
8
server:
port: 8081
servlet:
context-path: /luckymoney
limit:
minMoney: 1
maxMoney: 99
description: 最少要发${limit.minMoney}元,最多发${limit.maxMoney}元
  1. 输出

Dev:说明最少要发0.1元,最多发99元

Prod:说明最少要发1元,最多发99元

  1. 打包命令切换开发环境
1
2
3
4
5
6
7
meaven使用
//项目根目录打包
mvn clean package
//普通启动-开发环境
java -jar target/luckymoney-0.0.1-SNAPSHOT.jar
//启动时修改-生产环境
java -jar -Dspring.profiles.active=prod target/luckymoney-0.0.1-SNAPSHOT.jar
  1. git上传
1
2
3
4
5
6
7
8
9
10
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git branch
* confmoney
master
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git add .
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git commit -m "多环境配置-生产、开发"
[confmoney b3f8623] 多环境配置-生产、开发
4 files changed, 137 insertions(+), 10 deletions(-)
create mode 100644 luckymoney/src/main/resources/application-dev.yml
create mode 100644 luckymoney/src/main/resources/application-prod.yml
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git push origin_lm confmoney

小结

@Value //单个配置

@Component @ConfigurationProperties//多个配置

多环境配置

【人生若只如初见】从“程序”入口Controller开始

image-20191229164313530

@requestmapping旧版使用,新版@getmapping,但有个别场景还是会使用到它的

controller+thymeleaf(不推荐,前后端不分离)

将restcontrller改成contrllre(报错),增添thymeleaf模版,返回指定templates-index页面

  1. 改成旧版controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;

@Controller
public class HelloController {

//法2;自动注入
@Autowired
LimitConfig limitConfig;


@GetMapping("/hello")
public String hello(){
// return "说明"+limitConfig.getDescription();
return "index";
}
}
  1. pom.xml,新增thymeleaf模版
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  1. 新建thymeleaf模版index页面

4.访问

http://localhost:8081/luckymoney/hello

hello world

  1. git上传
1
2
3
4
5
6
7
8
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git add .
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git commit -m "controller+thymeleaf"
[confmoney 0c07f43] controller+thymeleaf
6 files changed, 94 insertions(+), 2 deletions(-)
create mode 100644 luckymoney/src/main/resources/templates/index.html
create mode 100644 luckymoney_note.assets/image-20191229164307062.png
create mode 100644 luckymoney_note.assets/image-20191229164313530.png
bennyrhysdeMacBook-Pro:LuckyMoney-SpringBootProject bennyrhys$ git push origin_lm confmoney

恢复访问@Controller+@ResponseBody

注销thymeleaf,新增@RestponseBody恢复页面访问,消除报错

@RestponseBody使用,当同时两个方法使用,@RestponseBody添加返回字符串,不加返回h5页面

http://localhost:8081/luckymoney/hello

http://localhost:8081/luckymoney/hello2

  1. pom.xml
1
2
3
4
5
6
7
8
  <!--      <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
-->
//注销模版使用,记得每次修改meaven要重新import一下
//@Controller+@ResponseBody=@RestController
//@ResponseBody可以放在方法上用
  1. HelloController//单独合并两个注解功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;

@Controller
//@ResponseBody
public class HelloController {

//法2;自动注入
@Autowired
LimitConfig limitConfig;

@ResponseBody
@GetMapping("/hello")
public String hello(){
return "说明"+limitConfig.getDescription();
// return "index";
}
}
  1. HelloController同时返回 字符串+thymeleaf h5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;

@Controller
//@ResponseBody
public class HelloController {

//法2;自动注入
@Autowired
LimitConfig limitConfig;

@ResponseBody
@GetMapping("/hello")
public String hello(){
return "说明"+limitConfig.getDescription();
// return "index";
}
@GetMapping("/hello2")
public String hello2(){
return "index";
}
}
  1. git上传
1
2
git commit -m "@controller+@ResponseBody"
git push origin_lm confmoney

多访问url同返回(名称数组)

快捷键:command+p//查看@GetMapping(“/hello”),括号内参数类型

1
@GetMapping({"/hello","/hi"})

url深层访问:@RequestMapping(“hello”)

低级做法:不方便维护,每次写深路径

1
@GetMapping("/hello/say")

高级做法:@RequestMapping(“hello”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
@RequestMapping("hello")
@RestController
public class HelloController {

@Autowired
LimitConfig limitConfig;

@GetMapping("/say")
public String hello(){
return "说明"+limitConfig.getDescription();
}

}

post请求访问

​ @PostMapping(“/say”)

get无法接受请求,使用工具postman发送post请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
@RequestMapping("hello")
@RestController
public class HelloController {

@Autowired
LimitConfig limitConfig;

// @GetMapping("/say")
@PostMapping("/say")
public String hello(){
return "说明"+limitConfig.getDescription();
}

}

Get/post都行的访问

不推荐,请求要明确,都行刷流氓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
@RequestMapping("hello")
@RestController
public class HelloController {

@Autowired
LimitConfig limitConfig;

// @GetMapping("/say")
// @PostMapping("/say")
@RequestMapping("/say")
public String hello(){
return "说明"+limitConfig.getDescription();
}

}

获取请求携带的参数

image-20191229173946637
  • url-简洁hello/say/100

@PathVariable(“id”) +@GetMapping(“/say/{id}”)

HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
@RequestMapping("hello")
@RestController
public class HelloController {

@Autowired
LimitConfig limitConfig;

@GetMapping("/say/{id}")
public String hello(@PathVariable("id") Integer id){
return "id:"+id;
}

}

http://localhost:8081/luckymoney/hello/say/100

id:100

  • url-hello/say?id=19

@RequestParam(“id”),撤销@GetMapping(“/say”)的{id}

注意:@RequestParam中id和访问say?id=19名称一致,访问的id不能为空

HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
@RequestMapping("hello")
@RestController
public class HelloController {

@Autowired
LimitConfig limitConfig;

@GetMapping("/say")
public String hello(@RequestParam("id") Integer id){
return "id:"+id;
}

}

http://localhost:8081/luckymoney/hello/say?id=19

id:19

  • url-hello/say?id=为空时

设置属性非必须属性设置

1
public  String hello(@RequestParam(value = "id" , required = false, defaultValue = "0") Integer id){

不传id时默认为0

http://localhost:8081/luckymoney/hello/say

id:0

  • 访问切换成post请求

请求方式多样,可以跟在url-hello/say?id=后面,也可以在请求体中

推荐参数 放在body的urlencode里面

1
@PostMapping("/say")

git提交

confmoney分支 commit “controller-end”

【收获人生”红颜”知己】撸程序-红包收发操作数据库

搭配方案:

java端:spring-data-jpa:持久层配置标准,定义标准好比接口,实现此规范的产品hibernate,toplink,对spring整合,不用写一行sql语句操作

数据库:mysql

restful-api设计

请求类型 请求路径 请求功能
get /luckmoneys 获取红包列表
post /luckmoneys 创建一个红包
get /luckmoneys/id 通过id查询红包
put /luckmoneys/id 通过id更新红包

引入pom jpa+mysql

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

注意:不要加版本号,Springbot已经选择好了版本

yml配置mysql

先建立数据库luckmoney

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 8081
servlet:
context-path: /luckymoney
limit:
minMoney: 0.1
maxMoney: 99
description: 最少要发${limit.minMoney}元,最多发${limit.maxMoney}元
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/luckmoney
username: root
password: rootroot
jpa:
hibernate:
ddl-auto: create
show-sql: true #控制台显示sql命令

数据库-创建表

不需要执行sql

创建java文件命名时首字母大写,中间不要驼峰命名,默认识别为一个单词

Luckmoney.java 【空构造方法、get/set、@id导包不要错Java.presistence、id自增】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.bennyrhys.luckymoney;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.math.BigDecimal;
@Entity
public class Luckmoney {
@Id
@GeneratedValue //自增
private Integer id;
private BigDecimal money;
private String producer;//发送方
private String consumer;//接收方

运行,自动创建表

控制台输出sql

Hibernate: drop table if exists hibernate_sequence
Hibernate: drop table if exists luckmoney
Hibernate: create table hibernate_sequence (next_val bigint) engine=MyISAM
Hibernate: insert into hibernate_sequence values ( 1 )
Hibernate: create table luckmoney (id integer not null, consumer varchar(255), money decimal(19,2), producer varchar(255), primary key (id)) engine=MyISAM

填充表中数据,并关闭创建表yml配置create->update

1
2
3
4
jpa:
hibernate:
ddl-auto: update
show-sql: true #控制台显示sql命令

LuckymoneyRepository接口

Repository就是和数据连接的Dao,这样写和jpa命名一致

继承JpaRepository<Luckmoney,Integer>数据库的实体类,和id的类型

1
2
3
4
5
6
package com.bennyrhys.luckymoney;

import org.springframework.data.jpa.repository.JpaRepository;

public interface LuckymoneyRepository extends JpaRepository<Luckmoney,Integer> {
}

LuckmoneyController

先写好dao接口再调cotroller

1
2
3
4
5
6
7
8
9
10
11
12
package com.bennyrhys.luckymoney;

import java.util.List;

public class LuckmoneyController {
/**
* 获取红包列表
*/
public List<Luckmoney> list(){

}
}

列表输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
get:http://localhost:8081/luckymoney/luckymoneys
[{"id":1,"money":0.10,"producer":"李四","consumer":"zhangsan"},{"id":2,"money":0.20,"producer":"王五","consumer":"李四"}]

post:http://localhost:8081/luckymoney/luckymoneys
producer 啊本
money 10
{
"id": 3,
"money": 10,
"producer": "啊本",
"consumer": null
}

get:http://localhost:8081/luckymoney/luckymoneys/2
{
"id": 2,
"money": 100.00,
"producer": "瑞新",
"consumer": null
}

put:http://localhost:8081/luckymoney/luckymoneys/2
consumer 师姐
{
"id": 2,
"money": 1.00,
"producer": "阿本",
"consumer": "师姐"
}

【搞定并发就在一起】事务就像乱点鸳鸯的丈母娘

数据库事务,要么都完成要么都不做

LuckymoneyService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.bennyrhys.luckymoney;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
@Service
public class LuckymoneyService {
@Autowired
private LuckymoneyRepository luckymoneyRepository;
/**
* 创建两个红包
*/
public void createTwo(){
Luckmoney luckmoney = new Luckmoney();
luckmoney.setProducer("瑞新");
luckmoney.setMoney(new BigDecimal("520"));

luckymoneyRepository.save(luckmoney);
Luckmoney luckmoney2 = new Luckmoney();
luckmoney2.setProducer("瑞新");
luckmoney2.setMoney(new BigDecimal("1314"));

luckymoneyRepository.save(luckmoney2);
}
}

LuckmoneyController

1
2
3
4
5
6
7
/**
* 发两个红包-测试
*/
@PostMapping("luckymoneys/two")
public void creatTwo(){
service.createTwo();
}

请求发送红包

http://localhost:8081/luckymoney/luckymoneys/two 状态码返回200

Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into luckmoney (consumer, money, producer, id) values (?, ?, ?, ?)
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into luckmoney (consumer, money, producer, id) values (?, ?, ?, ?)

配置测试环境

decimal类型的金额,设置长度为五位(则整数为3小数为2)

1314的红包创建报错,数值越界Out of range value for column ‘money’ at row 1

写入一条520,另一条1314失败,不符合同时发生的事务要求

搞定并发

添加注解@Transactional,选择导入spring那个,先清空数据库,结果520依旧存入,1314失败

  • 事务理解:
    • @注解控制java事务
    • 有些数据库不支持事务,mysql有自己的事务机制(MYISAM)->(mysql支持多类型修改成innoDB实现事务)

[TOC]

简介

SpringBoot两小时快速入门,极客表白浪漫红包

信息时代最重视的是什么?是效率!企业最重视的人才是什么?是高效能人士!所谓“天下武功唯快不破 ” ,为此专门推出此教程,让你快速Get到SpringBoot实际应用技能。本教程所有代码、笔记由bennyrhys免费提供,遵循”学习其实可以很快乐“的原则,以极客浪漫情怀的”红包表白程序“为场景,贯穿微服务必学框架-SpringBoot相关内容,将初学SpringBoot的踩坑点进行指点,为同学打造麻雀虽小五脏俱全的教程。标题风趣幽默,必然让你本次的学习之旅尽兴而归。载着干货的老司机,在召唤你~

image-20191229211932494

相关链接

GitHub:点击预览——极客浪漫红包表白程序

详细文档:点击搜索——极客浪漫红包表白详解

目录

  • 【让宝宝不要输在起跑线】

    • 文档说明
    • 版本配置
  • 【初识】SpringBoot-hello world

    • 解决打包失败
    • 配置文件
      • application.properties
      • application.yml(推荐)
    • git提交 :helloworld+yml启动及简单配置
  • 【君子之交-淡如水】解放双手,配置红包

    • 手动单个配置
    • 自动多个配置红包金额【limit类限制金额范围】
  • 生成环境开发环境划分

  • 小结

  • 【人生若只如初见】从“程序”入口Controller开始

    • controller+thymeleaf(不推荐,前后端不分离)
    • 恢复访问@Controller+@ResponseBody
    • 多访问url同返回(名称数组)
    • url深层访问:@RequestMapping(“hello”)
    • post请求访问
    • Get/post都行的访问
    • 获取请求携带的参数
    • git提交
  • 【收获人生”红颜”知己】撸程序-红包收发操作数据库

    • 搭配方案:
    • restful-api设计
    • 引入pom jpa+mysql
    • yml配置mysql
    • 数据库-创建
    • LuckymoneyRepository接口
    • LuckmoneyController
    • 列表输出
  • 【搞定并发就在一起】事务就像乱点鸳鸯的丈母娘

    • LuckymoneyService
    • LuckmoneyController
    • 请求发送红包
    • 配置测试环境
    • 搞定并发

[TOC]

Hexo提升篇-切换一个主题

第一轮测试-页面局部显示

疑npm install 导致显示不全,测试二不安装依赖

1
2
3
4
5
6
7
8
9
//挑选主题
https://hexo.io/themes/
//克隆主题到本地,放置thmemes
bennyrhys>git clone https://github.com/theme-next/hexo-theme-next.git
//更改配置文件
theme: hexo-theme-next
//安装依赖插件
$ cd hexo-theme-next
$ npm install

第二轮测试-全部显示(未安装依赖,切换主题风格)

阅读全文 »

[TOC]

开篇博客-说明

专注分享JAVA全栈技术,JAVA零基础到高级,数据结构,框架,设计模式,微服务,中间件,大数据,网络爬虫,开源项目,阿里技术,面试经个人简介见,站点左侧关于。

guihub:点此访问
csdn:点此访问
公众号:让我遇见相似的灵魂
邮箱:bennyrhys@163.com

第一次创建命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//创建文件
hexo new "启示录"
//生产静态文件
hexo g
//启动server
hexo server
//页面预览
http://localhost:4000
//启动监控
hexo g -w
//一键部署(先修改配置_config.yml的type: git 参数)
Git
1.配置
deploy:
type: git
repo: <repository url> #https://github.com/bennyrhys/bennyrhys.github.io.git
branch: [branch] #master
message: [message] #先不写
2.安装 hexo-deployer-git。
$ npm install hexo-deployer-git --save
3.部署
hexo deploy //如果重新生成了文件在部署末尾加 -g

致谢

感谢您的阅读,博客持续更新中……