Spring Boot 入门和进阶
来源:互联网 发布:淘宝的违禁词 编辑:程序博客网 时间:2024/06/03 03:23
慕课网 Spring Boot 入门和进阶
课程链接地址:
- 1、2小时学会Spring Boot
- 2、Spring Boot进阶之Web进阶
目录
- 入门
- 1.第一个SpringBoot程序
- 2.自定义属性配置
- 3.Controller的使用
- 4.Spring-data-jpa
- 进阶
- 5.表单验证
- 6.AOP处理请求
- 7.统一异常处理
- 8.单元测试
1.第一个SpringBoot程序
1.1创建新的工程步骤:
1.2修改maven的默认选项,不修改的话,会报错误找不到spring*的包。
1.3目录结构
1.4pom.xml文件
- 默认生成,无需修改
1.5程序启动入口
- GirlApplication类上有标注@SpringBootApplication,可以右键单击此类进行启动项目。
1.6添加新的访问
- 给新的Controller类加上注解@RestController
- 给say()方法加上注解@RequestMapping(),value是路径,method是请求方式。
- 启动项目后,访问http://127.0.0.1:8080/hello
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不同环境下不同配置的用法。
- 不同的启动方式
- 命令行启动prod环境
- 项目目录下输入mvn install,等待maven编译完成
- 输入java -jar target/girl-0.0.1-SHAPSHOT.jar –spring.profiles.active=prod,项目启动
- IDE中启动dev环境
- 分别访问 http://127.0.0.1:8080/hello,http://127.0.0.1:8081/hello,得到2个环境下的结果。
- 命令行启动prod环境
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”;
@Controllerpublic class HelloController { //使用模板 返回index.html @RequestMapping(value = "{"/hi","/hello"}",method = RequestMethod.GET) public String say() { return "index"; }}
- 访问 http://127.0.0.1:8080/hello,得到index.html显示的结果。
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/10public 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字段,
- 必须要有空参数的构造方法。
@Entitypublic 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)
@RestControllerpublic 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数据就不成功了。
@Servicepublic 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); }}
@RestControllerpublic class GirlController { …… @Autowired private GirlService girlService; …… //事务管理业务方法 @PostMapping(value = "/girls/two") public void girlTwo() { girlService.insertTwo(); }}
- 启动项目后访问 http://127.0.0.1:8080/girls/two,结果是数据插不成功的。
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方法, }
- get方式访问http://127.0.0.1:8080/girls/7,得到如下结果:
7.统一异常处理
7.1 异常情况模拟
- 给Girl类添加金额属性,加上必传验证,生成get/set方法。
@Id@GeneratedValueprivate 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注解。
@ControllerAdvicepublic 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){}
,作用是判断异常是不是自定义的异常。 - 加入日志记录,可以让控制台打印出异常信息,不加控制台是看不到的。
@ControllerAdvicepublic 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@AutoConfigureMockMvcpublic 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
阅读全文
0 0
- Spring Boot 入门和进阶
- Spring Boot进阶1
- 【Spring Boot】Spring Boot 入门
- Spring Boot Dubbo 整合和入门理解
- Spring boot 进阶之路
- 慕课网-Spring Boot进阶之Web进阶
- Spring Boot 快速入门
- spring boot 入门
- Spring Boot 入门
- Spring Boot 入门
- spring boot简易入门
- Spring boot入门
- Spring Boot 快速入门
- Spring boot快速入门
- Spring boot 入门 实例
- Spring Boot 快速入门
- spring boot入门(一)
- spring boot入门例
- struts2下载
- 分布式架构的资料汇总
- inline
- C++字符串翻转
- MySQL主从复制与读写分离 tu
- Spring Boot 入门和进阶
- BitBlt和StretchBlt的区别
- 快速排序原理解剖
- WebUploader中,父级div为display:none,点击显示无效。
- 中企动力被评为2017全球互联网“最佳企业数字化服务商”
- Mysql主从配置,实现读写分离
- Realm For Android详细教程
- Android NDK系列(四)-AS生成jar包、导入so库并使用方法
- 9月8日国盟CISM每日一题