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