Spring Boot学习

基础知识

Spring Boot 特点

  • 是 Spring MVC 的升级版,没有必然的联系
  • 简化配置,编码,部署,监控
  • 是下一代框架
  • 入门级微框架,学习 SpringCloud 必须学会 SpringBoot,SpringCloud 建立在 boot 的基础之上
  • 学习 maven,Spring 的注解,restful API

属性配置

两种配置方式,properties 和 yml。
yml 树状结构,对中文支持更好。

Server

  • port:默认 8080
  • context-path:给路径加上前缀

自定义单项配置

在配置文件中写:

    name: sherlock

在 java 文件中用:声明 String 或者 Integer 就可以自动转

    @Value("${name}")

在配置文件中使用配置文件:

    content:“name:${name},age${age}”

多项配置

1
2
3
person:
name: sherlock
age: 18

新建一个类,用@ConfigurationProperties(prefix=”person”)获取前缀是 person 的配置。注意@Component 成为 Bean。

创建私有属性 name ,age ,get 和 set 等

此时在使用的时候就不用@Value(“${name}”)了,直接@Autowired,将新建的类自动导入即可。

生产和上线不同的环境

新建两个 yml 文件:application-dev.yml 和 application-prod.yml。

在 application.yml 中配置:dev 或者 prod

    spring.profiles.active:dev

注意:在用命令行启动 jar 文件的时候,我们可以指定生产环境如下:

    java -jar target/*.jar --spring.profiles.active=prod

注意在 application.yml 中进行的配置,两个环境都可以使用

Controller 的使用

映射 URL

用来接收用户端的请求

  • @Controller:处理 http 请求。返回页面,配合模板来使用
  • @RestController:是@Controller+@ResponseBody。返回 json。【推荐使用】
  • @RequestMapping:配置 url 映射

使用方式:

  • 可以给整个类指定 url,用@RequestMapping 再给方法指定 url
  • url 可以是 json 模式{“/“,”/“}
  • get 可以从 url 进行测试,测试 post 的时候,可以用 postman 来进行尝试

获取参数

  • @PathVariable
  • @RequestParam

@PathVariable
整个 url 非常的简洁

1
2
3
4
5
value="/{id}/path"

func(@PathVariable("id") Integer id){

}

@RequestParam

1
2
3
4
5
6
7
8
9
value="/path"

func(@RequestParam("id") Integer myId){

}
//设置默认值
func(@RequestParam(value="id",required=false,defaultValue="0") Integer myId){

}

数据库操作

Spring-Data-Jpa
JPA(Java Persistence API):定义了一系列对象持久化的标准。

  • jpa.hibernate.ddl-auto: create(运行的时候创建一张新的表)
    update (第一次运行的时候创建一张表,如果有数据会保留),常用
    :create-drop(应用停下来了,会删掉表)
    :validate(验证数据库中和你的表是否一致)
  • jpa.show-sql: true

1
2
3
4
5
6
@Entity(这个是将这个类映射为数据库中的一张表)

@Id
@GeneratedValue
private Integer id;

  1. 创建表的实体,如上的 @Entity
  2. 创建实体的 reposity
  3. 创建 service,内嵌 reposity,对数据库的接口,进行再次包装
  4. 创建 controller,构建 url,解析参数

Restful 接口

  • get
  • post:新加入
  • put:更新,传入 id
  • delete:删除

事务管理

希望两条数据同时插入,要不然都不成功

  • @Transactional:在 Service 中的方法上面加入@Transactional,事务注解,表示这个方法内的多条数据,或者一条数据如果失败,那么就回退。只有查询的时候不需要加入事务操作。

  • @Test
    @Transactional
    连在一起使用,可以达到回滚的目的??也就是只用于测试,不真正的向数据库中插入

web 进阶

还需要学习

  • @Valid 表单验证
  • AOP 处理请求
  • AOP 统一异常处理
  • 单元测试

待了解

  • spring-data-jpa:关系型数据库
  • JdbcTemplate
  • Mybatis
  • redis+MongoDB:nosql 数据库
  • 用缓存应对高并发

@Valid 表单验证

@Valid 和@BindingResult 一大利器

AOP 统一处理请求日志

AOP 是一种程序的设计思想

POP:面向过程
OOP:面向对象
AOP:面向切面

将通用逻辑从业务逻辑中分出来

  • 增加 aop 的依赖
  • 增加处理文件 aspect

面向切面编程,增加 url 的切点,在之前和之后进行处理验证

@Slf4j:类注解,处理日志

1
2
3
4
5
6
7
8
9
10
11
12
@Pointcut("execution(public * edu.fudan.advancedweb.web3d.controller.UserController.findById(..))")
public void log(){

}
@Before("log()")
public void doBefore(){
System.out.println("1111");
}
@After("log()")
public void doAfter(){
System.out.println("2222");
}
  • @AfterReturning(returning = “object”, pointcut = “log()”):返回之后的对象

统一异常处理

将返回的信息进行包装,成为:code,msg,data

Spring 只对 RuntimeException,

当我们需要修改返回的 error 的 httpstatus 时,注意,有时候会出现自定义的错误,但是 status 为 200,只需要在一行的捕获下面,加上@ResponseStatus(HttpStatus.***)可。

项目优化

ab 压测模拟并发

用 Apache 进行压测

  • ab -n 100 -c 100 url(-n 表示 100 个请求,-c 表示 100 个并发。相当于 100 个人同时进行请求)

  • -t 60。表示 60s 内不停地模拟

  • 使用 synchronized:解决了问题,降低了性能。数据安全是第一位。

  • 无法做得到细粒度的控制

  • 只适合单点的情况

redis 分布式锁

  • setnx:set if not exist,不成立的时候来建立锁

  • getset:先 get,再次 set。返回原来的值,设置新的值。

  • 支持高可用,分布式集群

  • 更细粒度的锁

  • 多台机器上多个进程对一个数据进行操作的互斥

  • 他可以做分布式锁一个重要原因是它是单线程的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public boolean lock(String key, String value) {

//已经加锁成功了
if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}

String currentValue = redisTemplate.opsForValue().get(key);

//如果过期。这段代码就是CAS,乐观锁的实现。解决的是两个线程同时进入
//的情况。
if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);

//获取上一个锁的时间
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
return true;
}

}

return false;
}

public void unlock(String key, String value) {
try {
String currentValue = redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
log.error("【redis 分布式锁】解锁异常");
}

}

Redis 缓存

  • 命中
  • 失效:时间到了
  • 更新

进行缓存需要实现序列化

  • @Cacheable(cacheName=”userList”,key=”123”);
  • @CachePut(cacheName=”userList”,key=”123”);
  • @CacheEvict(cacheName=”userList”,key=”123”);
  • 类上面的@CacheConfig(cacheName=”userList”);
    取消下面的 cacheName

@Cacheable 中

  • key 可以动态,key=”#sellerId”

  • condition=”#sellerId > 30”

  • unless=”#result.getCode()!=0”(当返回 code 为 0 的时候,进行缓存)

  • 建表用 sql,不用 JPA 建表

  • 慎用@OneToMany 等,表的关系根据逻辑控制

mybatis:

  • 注解
  • xml