spring boot学习笔记

[TOC]

spring boot

  • 特点:
    • 1、化繁为简,简化配置
    • 2、备受关注,是下一代框架
    • 3、微服务的入门级微框架

RequestMapping若要能使用多个路径访问同一个方法,可以在value={"/hello1", "/hello2"}

Spring-Data-Jpa:JPA定义了一系列对象持久化的标准,目前实现这一规范的产品有hibernate、TopLink等

NOTE:
1、前置知识:利用maven构建项目,Spring注解、RESTful API
2.不需要去学SpringMVC
3.Java、Maven等版本保持一致

Springboot的创建

在idea中用Spring Initializr进行创建

项目配置

方法一:
在application.properties中添加

<!--port代表端口号,context-path代表url前面的前缀-->
server.port=8081
server.context-path=/girl

方法二:
创建一个application.yml文件添加

server:
  context-path: /girl
  port: 8082

如何将配置文件中的属性注入进变量中去

方法一:使用Value注解

配置文件:application.yml

server:
  port: 8080
cupSize: B

NOTE:这里cupSize属性要和server并列,不然会报错

java文件:HelloController.java

  @Value("${cupSize}")
    private String cupSize;

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

@value:将配置文件中的属性值注入到属性中

方法二:使用类对象

配置文件: applivation.yml

girl:
  cupSize: B
  age: 18

对象类:GirlProperties

@Component
@ConfigurationProperties(prefix = "girl")
public class GirlProperties {
    private  String cupSize;
    private  Integer age;

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

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

    public String getCupSize() {
        return cupSize;
    }

    public Integer getAge() {
        return age;
    }
  }

NOTE:@ConfigurationProperties(prefix = "girl"):获取前缀是girl的配置
@Component:表示与配置文件中的信息进行匹配,把普通pojo实例化到spring容器中

controller:

@RestController
public class HelloController {
@Autowired
 private GirlProperties girlProperties;

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

多环境配置

环境一:application-dev.yml

server:
  port: 8080
girl:
  cupSize: B
  age: 18

环境二:application-prod.yml

server:
  port: 8080
girl:
  cupSize: F
  age: 18

在 applicatin.yml中配置

Spring:
    profiles:
        active:dev

可以根据active的不同使用不同的环境

Controller的使用

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

如何处理url中的参数

@PathVariable 获取url中的数据 (请求中为restful类型请求:/hello/${id})
@RequestParam 获取请求参数的值 (正常请求:/hello?id=23)
@GetMapping 组合注解 (@GetMapping(value="/say")相当于@RequestMapping(value = "/say",method = RequestMethod.GET))

数据库操作

Spring-Data-Jpa
JPA(Java Persistence API):定义了一系列对象持久化的标准,目前实现这一规范的产品有Hibernate、TopLink等

创建表

pom.xml文件引入依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.24</version>
        </dependency>
Note:mysql的版本控制,如果太高会报错

application.yml文件配置数据源和jpa

  datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/dbgirl
        username: root
        password:
  jpa:
      hibernate:
           ddl-auto: create
      show-sql: true
Note:datasource:连接数据库
ddl-auto:
creat表示不管数据库是不是有这张表都会重新创建
update:第一次运行会创建表,以后创建如果表中有数据会保留数据
create-drop:应用在停下来的时候会将表删除
none:什么都不做
validate:表中字段与实体类中对应,如果有不一样的会报错
show-sql:显示sql语句

Girl实体类:

@Entity
public class Girl {
    @Id
    @GeneratedValue
    private Integer id;

    private String cupSize;

    private  Integer age;
    //下面是get set方法以及空构造
}

Note:@Entity:jpa特性,表示实体类与数据库对应的表
@Id:主键
@GeneratedValue:自动生成

API接口开发

请求类型 请求路径 功能
GET /girls 获取女生列表
POST /girls 创建一个女生
GET /girls/id 通过id查询一个女生
PUT /girls/id 通过id更新一个女生
DELETE /girls/id 通过id删除一个女生

创建一个接口继承Jpa

public interface GirlRepository extends JpaRepository<Girl,Integer>{

    //通过年龄来查询
    public List<Girl> findByAge(Integer age);
}

创建出controller

@RestController
public class GirlController{

    @Autowired
    private GirlRepository girlRepository;

    /*
    查询所有的女生列表
     */
    @GetMapping(value = "/girls")
    public List<Girl> girlList(){

        List<Girl> girlList = girlRepository.findAll();

        return  girlList;
    }
    /*
    添加一个女生
     */
    @PostMapping(value = "/girls")
    public Girl girlAdd(@RequestParam("cupSize") String cupSize,@RequestParam("age") Integer age){
        Girl girl = new Girl();
        girl.setAge(age);
        girl.setCupSize(cupSize);
        return girlRepository.save(girl);
    }

    /**
     * 查询一个女生
     * @param id
     * @return
     */
    @GetMapping(value = "/girls/{id}")
    public Girl girlFindOne(@PathVariable("id") Integer id){
        return girlRepository.findOne(id);
    }

    /**
     * 更新女生信息
     * @param id
     * @param cupSize
     * @param age
     * @return
     */
    @PutMapping("/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);
    }

    /**
     * 删除女生信息
     * @param id
     */
    @DeleteMapping(value = "/girls/{id}")
    public void girlDelete(@PathVariable("id") Integer id){
        girlRepository.delete(id);
    }

    //通过年龄查询女生列表
    @GetMapping("/girls/age/{age}")
    public List<Girl>girlListByAge(@PathVariable("age") Integer age){
        return girlRepository.findByAge(age);
    }

}

Note:注入时注意idea的spring注解管理是否为warning

save在有id时候为更新,无主键id时候为添加

事务管理

  @Transactional
    public void  insertTwo(){
        Girl girlA = new Girl();
        girlA.setCupSize("B");
        girlA.setAge(18);
        girlRepository.save(girlA);

        Girl girlB = new Girl();
        girlB.setCupSize("Cccc");
        girlB.setAge(19);
        girlRepository.save(girlB);
    }

在方法上面添加一个transational注解表示启动事务管理

spring boot进阶之web进阶

表单验证

Controller方法中使用对象接收前台数据,在对象对应的类中需要验证的属性上添加比如:@Min 注解

@Min(value = 18, message = "未成年人止步")
private Integer age;

这里value表示年龄最小值应为18,小于18的验证不通过,message为验证不通过时提示的信息

Controller方法中形参前面添加@Valid注解,使用BindingResult接收提示信息

@PostMapping("/boys")
public Boy addBoy(@Valid Boy boy, BindingResult bindingResult) {
    // 如果有错误信息,即验证不通过时
    if (bindingResult.hasErrors()) {
        // 后台输出错误提示信息
        System.out.println(bindingResult.getFieldError().getDefaultMessage());
        // 返回null到前台
        return null;
    }
    return boyRepository.save(boy);
}

使用AOP处理请求

概念

AOP(面向切面编程)是一种编程范式,与语言无关,是一种程序设计思想

OOP(面向对象编程) POP(面向过程编程)

面向过程强调流程和规划,面向对象将需求功能垂直地划分为不同的,并且相对独立的,会将其封装成不同的类,让它们有自己的行为。

AOP的关键思想是将通用逻辑从业务逻辑中分离出来

运用

在maven的pom.xml中添加aop依赖

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

第一种方法:

使用sop在控制台输出记录的日志信息

@Aspect
@Component
public class BoyAspect {
    // 公用空方法,使用注解配置切入点,提高重用性
    @Pointcut("execution(public * com.lfy.BoyController.*(..))")
    public void log() {}


    // 前置增强,也可以直接在里面写execution
    @Before("log()")
    public void doBefore() {
        // 使用sop在控制台输出记录的日志信息
        System.out.println("doBefore");
    }

    // 最终增强
    @After("log()")
    public void doAfter() {
        System.out.println("doAfter");
    }
}

第二种方法:

使用LoggerFactory的getLogger方法获取Logger对象,调用其info方法在控制台输出记录的日志信息

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class BoyAspect {
    private final static Logger logger = LoggerFactory.getLogger(BoyAspect.class);

    @Pointcut("execution(public * com.lfy.BoyController.*(..))")
    public void log() {}

    @Before("log()")
    public void doBefore(JoinPoint joinPoint) {、
        // 获取HttpServletRequest对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 输出相应信息
        // url
        logger.info("url={}", request.getRequestURI());
        // 请求方式
        logger.info("method={}", request.getMethod());
        // 请求的ip地址
        logger.info("ip={}", request.getRemoteAddr());
        // 请求调用的方法(调用JoinPoint的方法)
        logger.info("class.method={}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        // 请求调用的方法的参数(调用JoinPoint的方法)
        logger.info("args={}", joinPoint.getArgs());
    }

    // 后置增强
    @AfterReturning(pointcut="log()", returning = "object")
    public void doAfterReturning(Object object) {
        // 输出响应信息
        logger.info("response={}", object);
    }

    @After("log()")
    public void doAfter() {
        logger.info("doAfter");
    }
} 

统一异常处理

传给前台的数据格式的统一

自定义Result类,因为返回的data数据的类型未知,故使用泛型

public class Result<T> {
    private Integer code;
    private String message;
    private T data;
    // ...
}

Result工具类

public class ResultUtil {
    // 成功并返回data数据
    public static Result success(Object object) {
        Result result = new Result();
        result.setCode(0);
        result.setMessage("成功");
        result.setData(object);
        return result;
    }
    // 成功不返回data数据
    public static Result success() {
        return success(null);
    }

    // 失败
    public static Result error(Integer code, String message) {
        Result result = new Result();
        result.setCode(code);
        result.setMessage(message);
        return result;
    }
}

统一异常处理示例

枚举类的使用,没有set方法,因为枚举类型通过构造方法赋值

public enum ResultEnums {
    UNKNOWN_ERROR(-1, "未知错误"),
    SUCCESS(0, "成功"),
    PRIMARY_SCHOOL(101, "小学"),
    MIDDLE_SCHOOL(102, "中学");

    private Integer code;
    private String message;

    ResultEnums(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
}

自定义异常,这里继承RuntimeException而不继承Exception是因为spring框架针对RuntimeException才会回滚

public class BoyException extends RuntimeException {
    private Integer code;

    public Integer getCode() {
        return code;
    }

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

    // 直接通过枚举类获取信息
    public BoyException(ResultEnums resultEnums) {
        super(resultEnums.getMessage());
        this.code = resultEnums.getCode();
    }
}

异常处理类

通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置。

注解了@ControllerAdvice的类的方法可以使用@ExceptionHandler等等注解到方法上。

@ControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。

@ExceptionHandler:用于全局处理控制器里的异常。

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 static final Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);

    @ExceptionHandler(value=Exception.class)
    @ResponseBody
    public Result handle(Exception e) {
        if (e instanceof BoyException) {
            BoyException boyException = (BoyException) e;
            return ResultUtil.error(boyException.getCode(), boyException.getMessage());
        }
        logger.info("系统异常={}", e);
        return ResultUtil.error(-1, "未知错误");
    }
}

service使用自定义异常和枚举类

public void getAge(Integer id) throws Exception {
        Boy boy = boyRepository.findOne(id);
        Integer age = boy.getAge();
        if (age <= 10) {
            throw new BoyException(ResultEnums.PRIMARY_SCHOOL);
        } else if (age <= 16 ) {
            throw new BoyException(ResultEnums.MIDDLE_SCHOOL);
        }
    }

controller,throws Exception使得处理异常返回的结果返回给前台

@GetMapping(value="/boys/getAge/{id}")
public void getAge(@PathVariable("id") Integer id) throws Exception {
    boyService.getAge(id);
}

单元测试

测试service

@RunWith(SpringRunner.class)
@SpringBootTest
public class BoyServiceTest {
    @Autowired
    private BoyService boyService;
    
    @Test
    public void findAllBoy() throws Exception {
        List<Boy> allBoy = boyService.findAllBoy();
        // 此处使用断言进行测试,前者是预期结果,后者是测试结果
        Assert.assertEquals(new Integer(7), allBoy.get(0).getAge());
    }
}

测试结果与预期结果不一致,控制台提示:1 test failed

java.lang.AssertionError:

Expected :8

Actual :7

一致,提示:1 test passed

测试API

添加@AutoConfigureMockMvc注解,使用MockMvc对象进行测试

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class BoyControllerTest {
    @Autowired
    private MockMvc mvc;

    @Test
    public void findAllBoy() throws Exception {
        // 测试访问路径
        mvc.perform(MockMvcRequestBuilders.get("/boys"))
                // 测试响应码是否是200(成功)
                .andExpect(MockMvcResultMatchers.status().isOk())
                // 测试响应的内容
                .andExpect(MockMvcResultMatchers.content().string("123"));
    }
}

推荐阅读更多精彩内容