spring boot
配置文件:
格式一:
application.properties
例如:添加全局访问路径和修改 Tomcat 的端口号
server.context-path=/demo
server.port=8081
格式二:
-
系统属性
application.yml
例如:添加全局访问路径 和 修改 Tomcat 的端口号
server: context-path: /demo port: 8081
-
自定义属性:
application.yml
age: 18 # 自定义属性 name: inke # 自定义属性 content: "name:${name},age:${age}" #配置引用配置
在代码中引用
HelloController.java
@Value("${age}") private Integer age;//@value可以获取application.yml配置的值 @RestController public class HelloController { }
-
类和属性关联
application.yml
person: age: 18 # 自定义属性 name: inke # 自定义属性
Person.java
@Component @ConfigurationProperties(prefix = "person") public class Person { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
在代码中引用
HelloController.java
@RestController public class HelloController { // @Value("${age}") // private Integer age; // // @Value("${content}") // private String content; @Autowired private Person person; @RequestMapping(value = "/hello") public String sayHello() { return "hello world name:" + person.getName() + " , age:" + person.getAge(); } }
多个配置文件切换
application.yml
通过 active 指定的属性的环境:dev 还是 prod
spring:
profiles:
active: prod
application-dev.yml
person:
age: 18 # 自定义属性
name: dev001 # 自定义属性
application-prod.yml
person:
age: 18 # 自定义属性
name: prod001 # 自定义属性
启动的时候,会根据 active 指定的环境属性文件进行加载。
Controller
示例:
HelloController
package com.example;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Value("${age}")
private Integer age;//@value可以获取application.yml配置的值
@RequestMapping(value = "/hello")
public String sayHello() {
return "hello world age:" + age;
}
}
application.yml
age: 18
常用注解
@RestController
= @Controller
+ @ResponseBody
@Controller
需要返回 template 模版,类似于 Django 的模板。
建议使用:@RestController
映射多个路径
- 单个映射:
@RequestMapping(value = "/hello")
@RequestMapping(value = "/hello")
public String sayHello() {
return "hello world name:" + person.getName() + " , age:" + person.getAge();
}
- 多个映射:
@RequestMapping(value = {"hello", "hi"})
@RequestMapping(value = {"hello", "hi"})
public String sayHello() {
return "hello world name:" + person.getName() + " , age:" + person.getAge();
}
- 整个 controller 增加映射,在类上增加:
@RequestMapping(value = "/demo")
@RestController
@RequestMapping(value = "/demo")
public class HelloController {
@Autowired
private Person person;
@RequestMapping(value = {"hello", "hi"})
public String sayHello() {
return "hello world name:" + person.getName() + " , age:" + person.getAge();
}
}
访问方式:
method = RequestMethod.GET
method = RequestMethod.POST
- ...
- 简便方式:
@GetMapping(value = "hello")
@PostMapping(value = "hello")
@RestController
@RequestMapping(value = "/demo")
public class HelloController {
@Autowired
private Person person;
@RequestMapping(value = {"hello", "hi"}, method = RequestMethod.GET)
//@GetMapping(value = "hello")
public String sayHello() {
return "hello world name:" + person.getName() + " , age:" + person.getAge();
}
}
注意:什么都不写,默认是 get 和 post 都可以访问,但是不建议这样做,需要明确指定访问方式。
获取访问参数
-
获取
GET
的URL
的参数和POST
的 请求body
参数- 字段field映射
@RequestMapping(value = "hello", method = RequestMethod.GET) public String sayHello(@RequestParam(value = "id", required = false, defaultValue = "0") Integer id) { return "id:" + id; }
- 实体 model 映射
@PostMapping(value = "/addPerson") public Person addPerson(Person person) { return personRespository.save(person); }
- 获取 restfully 参数
@RequestMapping(value = "hello/{id}", method = RequestMethod.GET)
public String sayHello(@PathVariable("id") Integer id) {
return "id:" + id;
}
数据库
-
准备工作,加入数据库的配置和依赖
pom.xml
mysql mysql-connector-java org.springframework.boot spring-boot-starter-data-jpa application.yml
spring: profiles: active: prod datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot username: root password: root jpa: hibernate: ddl-auto: create show-sql: true
Person.java
@Entity public class Person { @Id @GeneratedValue private Integer id; private String name; private Integer age; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
创建接口继承JpaRepository,泛型使用的是
public interface PersonRespository extends JpaRepository
{ } -
单表简单查询
HelloController.java
@RestController public class HelloController { @Autowired private PersonRespository personRespository; @PostMapping(value = "/addPerson") public Person addPerson(@RequestParam(value = "name") String name, @RequestParam(value = "age") Integer age) { Person person = new Person(); person.setName(name); person.setAge(age); return personRespository.save(person); } @DeleteMapping(value = "/delPerson/{id}") public void delPerson(@PathVariable(value = "id") Integer id) { personRespository.delete(id); } @PutMapping(value = "/updatePerson") public Person updatePerson(@RequestParam(value = "id") Integer id, @RequestParam(value = "name") String name, @RequestParam(value = "age") Integer age) { Person person = new Person(); person.setId(id); person.setName(name); person.setAge(age); return personRespository.save(person); } @GetMapping(value = "/findPerson/{id}") public Person findPerson(@PathVariable(value = "id") Integer id) { return personRespository.findOne(id); } @GetMapping(value = "/findPersons") public List
findPerson() { return personRespository.findAll(); } } -
单表根据年龄查询
PersonRespository.java
增加一个根据年龄查询的抽象方法
public interface PersonRespository extends JpaRepository
{ Person findByAge(Integer age); } HelloController.java
@RestController public class HelloController { @Autowired private PersonRespository personRespository; @GetMapping(value = "/findPersonByAge/{age}") public Person findPersonByAge(@PathVariable(value = "age") Integer age) { return personRespository.findByAge(age); } }
事务管理
在方法名称上增加@Transactional,该方法就有事务管理了。
@Service
public class PersonService {
@Autowired
private PersonRespository personRespository;
@Transactional
public void insertTwo() {
Person person = new Person();
person.setName("jack");
person.setAge(40);
personRespository.save(person);
Person person1 = new Person();
person1.setName("rose");
person1.setAge(30000);
int a = 1 / 0;
personRespository.save(person1);
}
}
校验
在 model 增加注解 @Min(value = 18,message = "不能小于18岁")
@Entity
public class Person {
@Id
@GeneratedValue
private Integer id;
private String name;
@Min(value = 18,message = "不能小于18岁")
private Integer age;
。。。set、get 方法
}
在 controller 的 model 增加@Valid
注解,同时可以BindingResult
获取校验的信息。
@PostMapping(value = "/addPerson")
public Person addPerson(@Valid Person person, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
//输出错误信息:不能小于18岁
System.out.print(bindingResult.getFieldError().getDefaultMessage());
}
return personRespository.save(person);
}
AOP
-
创建切面方式一:
@Aspect @Component public class HttpAspect { @Before("execution(public * com.example.HelloController.*(..))") public void logBefore() { System.out.print("logBefore"); } @After("execution(public * com.example.HelloController.*(..))") public void logAfter() { System.out.print("logAfter"); } }
-
创建切面方式二:(抽取切点出来,减少重复代码)
@Aspect @Component public class HttpAspect { @Pointcut("execution(public * com.example.HelloController.*(..))") public void log() { } @Before("log()") public void logBefore() { System.out.print("logBefore"); } @After("log()") public void logAfter() { System.out.print("logAfter"); } }
@Aspect
@Component
public class HttpAspect {
public static Logger logger = LoggerFactory.getLogger(HttpAspect.class);
@Pointcut("execution(public * com.example.HelloController.*(..))")
public void log() {
}
@Before("log()")
public void logBefore(JoinPoint joinPoint) {
logger.info("logBefore");
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
logger.info("url={}", request.getRequestURL());
logger.info("method={}", request.getMethod());
logger.info("ip={}", request.getRemoteAddr());
logger.info("class.method={}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("args={}", joinPoint.getArgs());
}
@After("log()")
public void logAfter() {
logger.info("logAfter");
}
@AfterReturning(returning = "object", pointcut = "log()")
public void logAfterReturning(Object object) {
logger.info("response={}", object.toString());
}
}
输出日志:
logBefore
url=http://localhost:8080/updatePerson
method=PUT
ip=0:0:0:0:0:0:0:1
class.method=com.example.HelloController.updatePerson
args=10
m person person0_ where person0_.id=?
logAfter
response=com.example.Person@34b98c82
日志
public static Logger logger = LoggerFactory.getLogger(HttpAspect.class);
@Pointcut("execution(public * com.example.HelloController.*(..))")
public void log() {
}
@Before("log()")
public void logBefore() {
logger.info("logBefore");
}
@After("log()")
public void logAfter() {
logger.info("logAfter");
}
异常处理
定义常量枚举:ResultEnum.java
public enum ResultEnum {
UNKNOW(-1, "未知错误"),
OKKK(100, "小于18岁"),
NOOO(101, "大于50岁"),;
private Integer code;
private String msg;
ResultEnum(Integer code, String mag) {
this.code = code;
this.msg = mag;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMag() {
return msg;
}
public void setMag(String mag) {
this.msg = mag;
}
}
自定义异常类:PersonException.java
public class PersonException extends Exception {
private Integer code;
private String msg;
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 PersonException(ResultEnum resultEnum) {
this.code = resultEnum.getCode();
this.msg = resultEnum.getMag();
}
}
定义返回结果类:ResultInfo.java
public class ResultInfo implements Serializable {
private int code;
private String msg;
private T data;
public ResultInfo(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int 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;
}
}
全局异常处理类:ResultExceptionHandle.java
@ControllerAdvice
public class ResultExceptionHandle {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResultInfo handleException(Exception e) {
if (e instanceof PersonException) {
PersonException personException = (PersonException) e;
return new ResultInfo(personException.getCode(), personException.getMsg());
}
return new ResultInfo(-1, "未知错误");
}
}
Controller 正常调用,全局 Exception 会自动拦截处理 HelloController.java
@RestController
public class HelloController {
@Autowired
private PersonRespository personRespository;
@PostMapping(value = "/addPerson")
public Person addPerson(@Valid Person person, BindingResult bindingResult) throws Exception {
if (bindingResult.hasErrors()) {
//输出错误信息
System.out.print(bindingResult.getFieldError().getDefaultMessage());
}
return personService.save(person);
}
}
单元测试
需要模拟 http 请求进行单元测试,类上要添加注解(@RunWith(SpringRunner.class)
、@SpringBootTest
、@AutoConfigureMockMvc
),方法上也要添加注解(@Test
),使用MockMvc进行请求模拟。
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class PersonControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void findPerson() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/findPersons")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string("hello"));
}
}
打包默认会运行所有单元测试,可以通过参数跳过单元测试。