欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

spring boot学习笔记

程序员文章站 2022-07-14 11:02:19
...

[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"));
    }
}

转载于:https://www.jianshu.com/p/62ab94157210