慕课网 Spring Boot 入门和进阶笔记

慕课网 Spring Boot 入门和进阶

课程链接地址:


目录

  • 入门
    • 1.第一个SpringBoot程序
    • 2.自定义属性配置
    • 3.Controller的使用
    • 4.Spring-data-jpa
  • 进阶
    • 5.表单验证
    • 6.AOP处理请求
    • 7.统一异常处理
    • 8.单元测试

1.第一个SpringBoot程序

1.1创建新的工程步骤:

创建新工程

1.2修改maven的默认选项,不修改的话,会报错误找不到spring*的包。

maven

1.3目录结构

目录结构

1.4pom.xml文件

  • 默认生成,无需修改

1.5程序启动入口

  • GirlApplication类上有标注@SpringBootApplication,可以右键单击此类进行启动项目。

1.6添加新的访问

  • 给新的Controller类加上注解@RestController
  • 给say()方法加上注解@RequestMapping(),value是路径,method是请求方式。

这里写图片描述

2.自定义属性配置

2.1用properties文件

这里写图片描述

2.2用yml文件

  • 格式相对于properties文件更简便。
  • 关键词:空格+值

这里写图片描述

2.3用注入方式配置变量

  • 在注入时定义变量类型,配置时不用定义。例如图中的cupSize,并不是在配置文件中定义的类型,而是在Controller类引入时定义的,private String cupSize
  • 也可以在配置文件中,再使用配置。

这里写图片描述

2.4配置文件的分组配置使用

  • 配置文件的属性分组
  • 创建属性类,加入注解@Component,@ConfigurationProperties(prefix = "girl")
  • Controller类,@Autowired注解引用属性类对象,注意给引用的类加上@Component注解,这里是GirlProperties类

这里写图片描述

2.5不同环境下不同配置的用法。

这里写图片描述

3.Controller的使用

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

3.1Controller

  • 使用模板,类似于jsp页面,pom文件中加入模板引擎thymeleaf依赖。
<!--spring官方的模板,因为用模板会影响性能,所以不建议使用,改用前后端分离Restful-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  • resources目录下,加入文件夹templates,加入index.html页面。
 <h1>hello Spring Boot!</h1>
  • Controller类中。方法返回值为return "index";
@Controller
public class HelloController {
    //使用模板 返回index.html
    @RequestMapping(value = "{"/hi","/hello"}",method = RequestMethod.GET)
    public String say() {
        return "index";
    }
}

3.2RestController=Controller+ResponseBody

3.3RequestMapping

  • @PathVariable获取url中的数据,请求地址:/http/say/10
  • @RequestParam获取请求参数的值,请求地址:/http/say?id=10
  • @GetMapping组合注解,@RequestMapping(value = "{"/say"}",method = RequestMethod.GET)简写为@GetMapping(value = "/say")
@GetMapping(value = "/say/{id}")
//@GetMapping(value = "/{id}/say")
//请求地址:/http/say/10
public String say(@PathVariable("id") Integer myId) {
	return "id: " + myId;
}

//@RequestMapping(value = "/say2",method = RequestMethod.GET)
//请求地址:/http/say2?id=10,required是否必传,defaultValue默认值,不能是int,需要是字符"0"
public String say2(@RequestParam(value="id",required=false,defaultValue="0") Integer myId) {
	return "id: " + myId;
}

4.数据库操作Spring-data-jpa

  • jpa定义了一系列对象持久化的标准,可以看做是spring对hibernate的整合。

4.1 RESTful API设计

这里写图片描述

4.2 添加依赖和配置文件

  • pom.xml文件,加入jpa依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
  • application.yml文件,加入datasource和jpa配置
    • ddl-auto的参数,常用的有create,update
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/dbgirl
    username: root
    password: root
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

4.3 创建实体类

  • 新建Girl类,并加上@Entity,类中的属性值对应数据库表中的字段。
  • @GeneratedValue注释为自增长,
  • @Id表示id字段,
  • 必须要有空参数的构造方法。
@Entity
public class Girl {

    @Id
    @GeneratedValue//自增长注解
    private Integer id;
    //@NotBlank(message = "这个字段必须传")
    private String cupSize;
    @Min(value = 18, message = "未成年少女禁止入门")
    private Integer age;

    public Girl() {}
    
	public Integer getId() {return id;}
    public void setId(Integer id) {this.id = id;}
    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;}
}

4.4 Controller类中写处理方法

  • 创建Controller类,根据RESTful API,创建增删改查方法。
  • 创建Repository接口,继承JpaRepository<Girl, Integer>。括号里是我们的类类型和id类型。
  • 如果JpaRepository中的方法不够用,就在自己的Repository接口中扩展新的方法。public List<Girl> findByAge(Integer age)
@RestController
public class GirlController {
    @Autowired
    private GirlRepository girlRepository;
    
    //查询所有女生,get方式,/girls
    @GetMapping(value="/girls")
    public List<Girl> girlList(){
        return girlRepository.findAll();
    }
    //添加一个女生,post方式,/girls
    @PostMapping(value = "/girls")
    public Girl girlAdd(@RequestParam("cupSize") String cupSize,
					    @RequestParam("age") Integer age){
        Girl girl = new Girl();
        girl.setCupSize(cupSize);
        girl.setAge(age);
        return girlRepository.save(girl);
    }
    //查询一个
    @GetMapping(value="/girls/{id}")
    public Girl girlFindOne(@PathVariable("id") Integer id){
		return girlRepository.findOne(id);
	}
    //更新
    @PutMapping(value="/girls/{id}")
    public Girl girlUpdate(@PathVariable("id") Integer id,
						   @RequestParam("cupSize") String cupSize,
						   @RequestParam("age") Integer age){
		Girl girl = new Girl();
		girl.setid(id);
        girl.setCupSize(cupSize);
        girl.setAge(age);
        return girlRepository.save(girl);
	}
    //删除
    @DeleteMapping(value="/girls/{id}")
    public String girlDelete(@PathVariable("id") Integer id){
	    girlRepository.delete();
    }
    //通过年龄查询出列表
    @GetMapping(value="/girls/age/{age}")
    public List<Girl> girlListByAge(@PathVariable("age") Integer age){
	    return girlRepository.findByAge(age);
    }
}
//自己写子类来扩展方法。
public interface GirlRepository extends JpaRepository<Girl, Integer> {
	//扩展JpaRepository方法,通过年龄来查询
	public List<Girl> findByAge(Integer age);
}
  • put方式,需要选择x-www-form-urlencoded,不能选择form-data,multipart/form-data与x-www-form-urlencoded区别:
    • multipart/form-data:既可以上传文件等二进制数据,也可以上传表单键值对,只是最后会转化为一条信息;
    • x-www-form-urlencoded:只能上传键值对,并且键值对都是间隔分开的。

4.5 事务管理

  • 给自己的业务方法加上@Transactional,一般只有查询的时候不用加事务。
  • 数据库中cupSize字段,改成1个字符长度。插入girlB数据就不成功了。
@Service
public class GirlService {

    @Autowired
    private GirlRepository girlRepository;

    @Transactional
    public void insertTwo() {
        Girl girlA = new Girl();
        girlA.setCupSize("A");
        girlA.setAge(18);
        girlRepository.save(girlA);
        
        Girl girlB = new Girl();
        girlB.setCupSize("BBBB");
        girlB.setAge(19);
        girlRepository.save(girlB);
    }
}
@RestController
public class GirlController {
	……
    @Autowired
    private GirlService girlService;
    ……
    //事务管理业务方法
    @PostMapping(value = "/girls/two")
    public void girlTwo() {
        girlService.insertTwo();
    }
}

PS:项目进行分层整理,然后进入下个阶段

项目进行分层整理

5.表单验证

5.1 修改添加方法,参数改为实体对象

	/*修改添加的方法
	@PostMapping(value = "/girls")
    public Girl girlAdd2(@RequestParam("cupSize") String cupSize,@RequestParam("age") Integer age){
        Girl girl= new Girl();
        girl.setCupSize(cupSize);
        girl.setAge(age);
        return girlRepository.save(girl);
    }*/
	//修改后的方法,参数是一个实体
	@PostMapping(value = "/girls")
    public Girl girlAdd(Girl gril) {
        girl.setCupSize(girl.getCupSize());
        girl.setAge(girl.getAge());
        return girlRepository.save(girl);
    }

5.2 给添加的对象加验证

* Girl类中给age变量加18岁限制条件。 ` @Min(value = 18,message = "未成年少女禁止入门")`
* GirlController类中添加BindingResult参数及方法,验证的结果会放到这个对象中。
	//Girl类中给age变量加18岁限制条件。
	private String cupSize;
    @Min(value = 18,message = "未成年少女禁止入门")
    private Integer age;

	//GirlController类中添加BindingResult参数及方法。
	@PostMapping(value = "/girls")
    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());
        return girlRepository.save(girl);
    }

6.AOP处理请求

  • 用来进行统一的操作处理

6.1 一、pom.xml文件添加依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

6.2 二、启动类GirlApplication.class上加注解,但是aop不用加

6.3 三、建立处理文件HttpAspect.class

  • @Before和@After注解,可以增加对Controller类中方法访问时需要进行的操作。
  • 增加@Pointcut后,可以在切面上操作。
  • logger.info("这个方法可以打印日志");,这个方法可以打印日志
package com.imooc.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//1.加入此注解
@Component//2.将HttpAspect 加入到spring容器
public class HttpAspect {

    private final  static Logger logger= LoggerFactory.getLogger(HttpAspect.class);
    
    @Pointcut("execution(public * com.imooc.controller.GirlController.*(..))")
    public void log(){

    }
	//@Before("execution(public * com.imooc.controller.GirlController.girlList(..))")//拦截getList方法的任何参数都拦截
	//@Before("execution(public * com.imooc.controller.GirlController.*(..))")//拦截所有方法
    @Before("log()")
    public void doBefore(){
		//System.out.println("Before1111");
        logger.info("doBefore1111");
    }
    //@After("execution(public * com.imooc.controller.GirlController.*(..))")
    @After("log()")
    public  void doAfter(){
		//System.out.println("After22222");
        logger.info("doAfter2222");
    }

}

6.4 四、测试

  • 给Controller.class中的getList()方法,添加查看顺序的语句
    private final static Logger logger = LoggerFactory.getLogger(GirlController.class);

    /**
     * 查询所有女生列表
     * 和girlAdd()访问地址相同,注意用get方式提交是查询。post是添加
     * @return
     */
    @GetMapping(value = "/girls")
    public List<Girl> girlList() {
		//System.out.println("getList 查看执行顺序");
        logger.info("getList 执行");
        return girlRepository.findAll();
    }

6.5 扩展

  • 在HttpAspect文件中,处理http请求头中的路径url,method,ip,类方法,参数,获取返回的对象等。
	//@Before("execution(public * com.imooc.controller.GirlController.girlList(..))")//拦截getList方法的任何参数都拦截
    //@Before("execution(public * com.imooc.controller.GirlController.*(..))")//拦截所有方法
    @Before("log()")
    public void doBefore(JoinPoint joinPoint){
        //System.out.println("Before1111");
        //logger.info("doBefore1111");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        //url
        logger.info("url={}",request.getRequestURL());
        //method
        logger.info("method={}",request.getMethod());
        //ip
        logger.info("ip={}",request.getRemoteAddr());
        //类方法
        logger.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());
        //参数
        logger.info("args={}",joinPoint.getArgs());
    }
    //@After("execution(public * com.imooc.controller.GirlController.*(..))")
    @After("log()")
    public  void doAfter(){
        //System.out.println("After22222");
        logger.info("doAfter2222");
    }
    //获取返回的内容,比如此实例中返回的json对象
    @AfterReturning(pointcut = "log()",returning="object")
    public void doAfterReturning(Object object){
        logger.info("response={}",object);//Girl实体类中没有toString()方法时,打印对象内存地址
        logger.info("response={}",object.toString());//Girl实体类中添加toString方法,
    }

log日志

7.统一异常处理

7.1 异常情况模拟

  • 给Girl类添加金额属性,加上必传验证,生成get/set方法。
@Id
@GeneratedValue
private Integer id;
@NotBlank(message = "这个字段必须传")
private String cupSize;
@Min(value = 18, message = "未成年少女禁止入门")
private Integer age;
@NotNull(message = "金额必传")
private Double money;
  • 直接测试新增girlAdd()方法,不传金额,前台返回错误页面500。
  • 控制台是空指针异常,因为不能通过表单验证,girlAdd()方法返回的是null对象,HttpAspect类中的doAfterReturning()方法接收的是null。(注释logger.info()语句可以解决,此为忽略的方法。)

7.2定义发生错误或异常时返回的数据封装,格式

  • 控制台打印getDefaultMessage()结果,改为返回给前台。
  • 方法返回值改Girl为Object,getDefaultMessage()方法返回的是String类型,save()方法返回Girl类型,所有就定义为Object。
/**
* 添加一个女生
* @return
*/
@PostMapping(value = "/girls")//注意用get方式提交是查询。post是添加
public Object girlAdd(@Valid Girl girl, BindingResult bindingResult) {
	if (bindingResult.hasErrors()) {
		return bindingResult.getFieldError().getDefaultMessage();
	}
	return girlRepository.save(girl);
}
  • 返回的格式整理如下,返回3个字段的数据。
//错误时										//正确时
{											{
    "code": 1,								    "code": 0,		
    "msg": "金额必传",							"msg": "成功",		
    "data": null								"data": {		
}											 		"id": 11,
											        "cupSize": "A",
											        "age": 29,
											        "money": 300
											    }
											}

  • domain包下,新建Result类,用来定义结果对象。
public class Result<T> {
    private Integer code;//错误码
    private String msg;//提示信息
    private T data;//具体内容
    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 T getData() {return data;}
    public void setData(T data) {this.data = data;}
}
  • 再次改造Controller
@PostMapping(value = "/girls")//注意用get方式提交是查询。post是添加
public Result<Girl> girlAdd(@Valid Girl girl, BindingResult bindingResult) {
	if (bindingResult.hasErrors()) {
		Result result = new Result();
        result.setCode(1);
        result.setMsg(bindingResult.getFieldError().getDefaultMessage());
        return result;
    }
    Result result = new Result();
    result.setCode(0);
    result.setMsg("成功");
    result.setData(girlRepository.save(girl));
    return result;
}
  • 因为重复代码,写一个ResultUtil工具类
public class ResultUtil {
    public static Result succss(Object object){
        Result result = new Result();
        result.setCode(0);
        result.setMsg("成功");
        result.setData(object);
        return result;
    }
    //成功时也可能不含Object
    public static Result success(){
        return  succss(null);
    }
    public static Result error(Integer code,String msg){
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
}
  • 用ResultUtil工具类,简化Controller代码
public Result<Girl> girlAdd(@Valid Girl girl, BindingResult bindingResult) {
	if (bindingResult.hasErrors()) {
		return ResultUtil.error(1,bindingResult.getFieldError().getDefaultMessage());
	}
	return ResultUtil.succss(girlRepository.save(girl));
}

7.3在Service类中写自己的业务逻辑

  • 业务需求,获取某女生的年龄并判断:
    • 小于10,返回“应该在上小学”。
    • 大于10且小于16,返回“可能在上初中”。
@GetMapping(value = "/girls/getAge/{id}")
public void getAge(@PathVariable("id") Integer id) throws Exception {
	girlService.getAge(id);
}
  • 在Service中写业务逻辑
public void getAge(Integer id) throws Exception {
	Girl girl = girlRepository.findOne(id);
	Integer age = girl.getAge();
	if (age < 10) {
		//返回,你还在上小学吧
		throw new Exception("你还在上小学吧");
	} else if (age > 10 && age < 16) {
		//返回,你可能在上初中
		throw new Exception("你可能上初中");
	}
}
  • 请求id号为12号的girl信息,前台控制台返回的结果:

这里写图片描述

7.4异常捕获

  • 通过上面的异常,里面的格式并不是我们想要的,所以需要自己写一个类捕获异常类:ExceptionHandle。
  • 因为返回浏览器的数据是json,而这个类又没有RestController注释,需要给ExceptionHandle.handle方法加入ResponseBody注解。
@ControllerAdvice
public class ExceptionHandle {
    @ExceptionHandler(value = Exception.class)//声明捕获哪个异常类
    @ResponseBody//返回浏览器是json,而这个类又没有RestController注释,就得加入ResponseBody注释
    public Result handle(Exception e) {
        return ResultUtil.error(100,e.getMessage());
    }
}
  • 执行以后,前台和控制台的返回结果就是我们想要的格式了。前台是3个字段数据的json,控制台不报异常信息。

这里写图片描述

  • 业务处理逻辑保留在一个环节:Service.getAge(),验证<10,就直接往外抛异常,Controller.getAge()方法调用Service.getAge()方法,不用处理,也是抛出异常,最终由handle捕获处理。

7.5自定义异常

  • 创建自定义异常类,定义一个code变量,记录错误代码。
  • Spring框架中,自定义异常只有继承RuntimeException才能支持事务回滚。
public class GirlException extends RuntimeException {
    //spring框架中,自定义异常只有继承RuntimeException才能支持事务回滚
    private Integer code;//定义一个code变量,错误代码
    public GirlException(Integer code, String message) {
        super(message);
        this.code = code;
    }
    public Integer getCode() {return code;}
	public void setCode(Integer code) {this.code = code;}
}
  • 使用自定义异常后的Service
public void getAge(Integer id) throws Exception {
	Girl girl = girlRepository.findOne(id);
	Integer age = girl.getAge();
	if (age < 10) {
		//返回,你还在上小学吧
		throw new GirlException(100, "你还在上小学吧");
	} else if (age > 10 && age < 16) {
		//返回,你可能在上初中
		throw new GirlException(101, "你可能上初中");
	}
}
  • 捕获异常类,加入代码if (e instanceof GirlException){},作用是判断异常是不是自定义的异常。
  • 加入日志记录,可以让控制台打印出异常信息,不加控制台是看不到的。
@ControllerAdvice
public class ExceptionHandle {
    private final static Logger logger = LoggerFactory.getLogger(ResponseBody.class);
    @ExceptionHandler(value = Exception.class)//声明捕获哪个异常类
    @ResponseBody//返回浏览器是json,而这个类又没有RestController注释,就得加入ResponseBody注释
    public Result handle(Exception e) {
        if (e instanceof GirlException) {
            GirlException girlException = (GirlException) e;
            return ResultUtil.error(girlException.getCode(), girlException.getMessage());
        } else {
            logger.error("【系统异常】{}", e);
            return ResultUtil.error(-1, "未知错误");
        }
    }
}

7.6异常错误代码代码管理

  • 创建枚举类型的类来管理错误代码。
public enum ResultEnum {
    UNKONW_ERROR(-1, "未知错误"),
    SUCCESS(0, "成功"),
    PRIMARY_SCHOOL(100, "你可能还在上小学"),
    MIDDLE_SCHOOL(101, "你可能在上初中"),;
    private Integer code;
    private String msg;

    ResultEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    public Integer getCode() {return code;}
    public String getMsg() {return msg;}
}
  • 修改GirlException的构造方法。
public GirlException(ResultEnum resultEnum) {
	super(resultEnum.getMsg());
	this.code = resultEnum.getCode();
}
  • 修改Service代码,参数改为枚举类型的常量
if (age < 10) {
	//throw new GirlException(100, "你还在上小学吧");
	throw new GirlException(ResultEnum.PRIMARY_SCHOOL);
} else if (age > 10 && age < 16) {
	//throw new GirlException(101, "你可能上初中");
	throw new GirlException(ResultEnum.MIDDLE_SCHOOL);
}

8.单元测试

  • 使用场景,测试Service中的方法,通过id查询一个女生信息并返回。
  • 使用场景,测试Controller方法,浏览器返回的状态码,返回的内容。

8.1 测试方法1,通过test目录下的测试类service:

  • 创建Test类,加上注解@RunWith(SpringRunner.class),表示使用测试类(底层使用junit),注解@SpringBootTest,能够启动整个工程。其他测试如junit测试,@Test,Assert断言。
@RunWith(SpringRunner.class)//使用测试
@SpringBootTest//启动整个spring工程
public class GirlServiceTest {
    @Autowired
    private GirlService girlService;
    @Test
    public void findOneTest(){
        Girl girl = girlService.findOne(12);
        Assert.assertEquals(new Integer(9),girl.getAge());
    }
}

8.2 测试方法2,AutoConfigureMockMvc测试controller:

  • 通过IDEA自带测试方法,右键要测试的方法,选择Go To--Test--选择要测试的方法,然后会生成相应的测试类。
  • 给类加上@AutoConfigureMockMvc注解,结合mvc.perform()
  • MockMvcResultMatchers.status().isOk()测试返回状态码,
  • MockMvcResultMatchers.content().string("abc")测试返回内容。
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class GirlControllerTest {
    /*
    //传统测试方法
    @Autowired
    private GirlController girlController;
    @Test
    public void girlList() throws Exception {
        girlController.girlList();
    }*/
    @Autowired
    private MockMvc mvc;
    @Test
    public void girlList() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/girls"))
                .andExpect(MockMvcResultMatchers.status().isOk())//测试返回状态码
                .andExpect(MockMvcResultMatchers.content().string("abc"));//测试返回内容
    }
}

8.2 测试方法3,通过maven命令:

  • maven clean package,打包命令。
  • 中途如果出现测试用例失败,会增加打包时间,查看日志会看到失败的原因,注释掉测试中的错误语句,比如此例中的andExpect(MockMvcResultMatchers.content().string("abc")),可以缩短这个时间。
  • 还可以跳过单元测试,使用命令maven clean package -Dmaven.test.skip=true

git地址

代码

转载于:https://my.oschina.net/u/2408149/blog/1570203

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值