1. 程式人生 > >SpringBoot的高階教程

SpringBoot的高階教程

SpringBoot的高階教程

一、SpringBoot快取

快取的場景

  • 臨時性資料儲存【校驗碼】
  • 避免頻繁因為相同的內容查詢資料庫【查詢的資訊】

1、JSR107快取規範

用的比較少

Java Caching定義了5個核心介面

  • CachingProvider

    定義了建立、配置、獲取、管理和控制多個CacheManager。一個應用可以在執行期間訪問多個CachingProvider

  • CacheManager

    定義了建立、配置、獲取、管理和控制多個唯一命名的Cache,這些Cache存在於CacheManage的上下文中,一個CacheManage只被一個CachingProvider擁有

  • Cache

    類似於Map的資料結構並臨時儲存以key為索引的值,一個Cache僅僅被一個CacheManage所擁有

  • Entry

    儲存在Cache中的key-value對

  • Expiry

    儲存在Cache的條目有一個定義的有效期,一旦超過這個時間,就會設定過期的狀態,過期無法被訪問,更新,刪除。快取的有效期可以通過ExpiryPolicy設定。

    35.cache

2、Spring的快取抽象

包括一些JSR107的註解

CahceManager

Cache

1、基本概念

重要的概念&快取註解

功能
Cache 快取介面,定義快取操作,實現有:RedisCache、EhCacheCache、ConcurrentMapCache等
CacheManager 快取管理器,管理各種快取(Cache)元件
@Cacheable 針對方法配置,根據方法的請求引數對其結果進行快取
@CacheEvict 清空快取
@CachePut 保證方法被呼叫,又希望結果被快取 update,呼叫,將資訊更新快取
@EnableCaching 開啟基於註解的快取
KeyGenerator 快取資料時key生成的策略
serialize 快取資料時value序列化策略

2、整合專案

1、新建一個SpringBoot1.5+web+mysql+mybatis+cache

2、編寫配置檔案,連線Mysql

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.179.131:3306/mybatis01
spring.datasource.username=root
spring.datasource.password=Welcome_1
mybatis.configuration.map-underscore-to-camel-case=true
server.port=9000

3、建立一個bean例項

Department

package com.wdjr.cache.bean;

public class Department {
    private Integer id;
    private String deptName;

    public Department(){

    }

    public Department(Integer id, String deptName) {
        this.id = id;
        this.deptName = deptName;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", deptName='" + deptName + '\'' +
                '}';
    }
}

Employee

package com.wdjr.cache.bean;

public class Employee {
    private Integer id;
    private String lastName;
    private String gender;
    private String email;
    private Integer dId;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getdId() {
        return dId;
    }

    public void setdId(Integer dId) {
        this.dId = dId;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastName='" + lastName + '\'' +
                ", gender='" + gender + '\'' +
                ", email='" + email + '\'' +
                ", dId=" + dId +
                '}';
    }
}

4、建立mapper介面對映資料庫,並訪問資料庫中的資料

package com.wdjr.cache.mapper;


import com.wdjr.cache.bean.Employee;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

@Mapper
public interface EmployeeMapper {

    @Select("SELECT * FROM employee WHERE id = #{id}")
    public Employee getEmpById(Integer id);
    @Update("UPDATE employee SET lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} WHERE id=#{id}")
    public void updateEmp(Employee employee);
}

5、主程式添加註解MapperScan,並且使用@EnableCaching開啟快取

package com.wdjr.cache;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@EnableCaching
@MapperScan("com.wdjr.cache.mapper")
@SpringBootApplication
public class Springboot01CacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(Springboot01CacheApplication.class, args);
    }
}

6、編寫service,來具體實現mapper中的方法

package com.wdjr.cache.service;

import com.wdjr.cache.bean.Employee;
import com.wdjr.cache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    /**
     * 將方法的執行結果進行快取,以後要是再有相同的資料,直接從快取中獲取,不用呼叫方法
     * CacheManager中管理多個Cache元件,對快取的真正CRUD操作在Cache元件中,每個快取元件都有自己的唯一名字;
     *
     * 屬性:
     *  CacheName/value:指定儲存快取元件的名字
     *  key:快取資料使用的key,可以使用它來指定。預設是使用方法引數的值,1-方法的返回值
     *  編寫Spel表示式:#id 引數id的值, #a0/#p0 #root.args[0]
     *  keyGenerator:key的生成器,自己可以指定key的生成器的元件id
     *  key/keyGendertor二選一使用
     *
     *  cacheManager指定Cache管理器,或者cacheReslover指定獲取解析器
     *  condition:指定符合條件的情況下,才快取;
     *  unless:否定快取,unless指定的條件為true,方法的返回值就不會被快取,可以獲取到結果進行判斷
     *  sync:是否使用非同步模式,unless不支援
     *
     *
     * @param id
     * @return
     */
    @Cacheable(cacheNames = {"emp"},key = "#id",condition = "#id>0",unless = "#result==null")
    public Employee getEmp(Integer id){
        System.out.println("查詢id= "+id+"的員工");
        return employeeMapper.getEmpById(id);
    }
}

7、編寫controller測試

@RestController
public class EmployeeController {
    @Autowired
    EmployeeService employeeService;

    @GetMapping("/emp/{id}")
    public Employee getEmp(@PathVariable("id")Integer id){
        return employeeService.getEmp(id);
    }
}

8、測試結果

35.cache

繼續訪問,就不會執行方法,因為直接在快取中取值

3、快取原理

原理:

1、CacheAutoConfiguration

2、匯入快取元件

36.importcache

3、檢視哪個快取配置生效

SimpleCacheConfiguration生效

4、給容器註冊一個CacheManager:ConcurrentMapCacheManager

5、可以獲取和建立ConcurrentMapCache,作用是將資料儲存在ConcurrentMap中

執行流程

1、方法執行之前,先查Cache(快取元件),按照cacheName的指定名字獲取;

(CacheManager先獲取相應的快取),第一次獲取快取如果沒有cache元件會自己建立

2、去Cache中查詢快取的內容,使用一個key,預設就是方法的引數;

key是按照某種策略生成的,預設是使用keyGenerator生成的,預設使用SimpleKeyGenerator生成key

沒有引數 key=new SimpleKey()

如果有一個引數 key=引數值

如果多個引數 key=new SimpleKey(params);

3、沒有查到快取就呼叫目標方法

4、將目標方法返回的結果,放回快取中

方法執行之前,@Cacheable先來檢查快取中是否有資料,按照引數的值作為key去查詢快取,如果沒有,就執行方法,存入快取,如果有資料,就取出map的值。

4、Cache的註解

1、@Cacheput

修改資料庫的某個資料,同時更新快取

執行時機

先執行方法,再將目標結果快取起來

cacheable的key是不能使用result的引數的

1、編寫更新方法

@CachePut(value = {"emp"},key = "#result.id")
public Employee updateEmp(Employee employee){
    System.out.println("updateEmp"+employee);
    employeeMapper.updateEmp(employee);
    return employee;
}

2、編寫Controller方法

@GetMapping("/emp")
public Employee updateEmp(Employee employee){
    employeeService.updateEmp(employee);
    return employee;
}

測試

測試步驟

1、先查詢1號員工

2、更新1號員工資料

3、查詢1號員工

可能並沒有更新,

是因為查詢和更新的key不同

效果:

  • 第一次查詢:查詢mysql
  • 第二次更新:更新mysql
  • 第三次查詢:呼叫記憶體

2、CacheEvict

清除快取

編寫測試方法

@CacheEvict(value = "emp",key = "#id")
public  void  deleteEmp(Integer id){
    System.out.println("delete的id"+id);
}

allEntries = true,代表不論清除那個key,都重新重新整理快取

beforeInvocation=true.方法執行前,清空快取,預設是false,如果程式異常,就不會清除快取

3、Caching

組合

  • Cacheable
  • CachePut
  • CacheEvict

CacheConfig抽取快取的公共配置

@CacheConfig(cacheNames = "emp")
@Service
public class EmployeeService {

然後下面的value=emp就不用寫了

@Caching(
        cacheable = {
                @Cacheable(value = "emp",key = "#lastName")
        },
        put = {
                @CachePut(value = "emp",key = "#result.id"),
                @CachePut(value = "emp",key = "#result.gender")
        }
)
public Employee getEmpByLastName(String lastName){
    return employeeMapper.getEmpByLastName(lastName);
}

如果查完lastName,再查的id是剛才的值,就會直接從快取中獲取資料

5、Redis

預設的快取是在記憶體中定義HashMap,生產中使用Redis的快取中介軟體

Redis 是一個開源(BSD許可)的,記憶體中的資料結構儲存系統,它可以用作資料庫、快取和訊息中介軟體

1、安裝Docker

安裝redis在docker上

#拉取redis映象
docker pull redis
#啟動redis[bfcb1f6df2db]docker images的id
 docker run -d -p 6379:6379 --name redis01 bfcb1f6df2db

2、Redis的Template

Redis的常用五大資料型別

String【字串】、List【列表】、Set【集合】、Hash【雜湊】、ZSet【有序集合】

分為兩種一種是StringRedisTemplate,另一種是RedisTemplate

根據不同的資料型別,大致的操作也分為這5種,以StringRedisTemplate為例

stringRedisTemplate.opsForValue()  --String
stringRedisTemplate.opsForList()  --List
stringRedisTemplate.opsForSet()  --Set
stringRedisTemplate.opsForHash()  --Hash
stringRedisTemplate.opsForZset()  -Zset

1、匯入依賴

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

2、修改配置檔案

spring.redis.host=192.168.179.131

3、新增測試類

    @Autowired
    StringRedisTemplate stringRedisTemplate;//操作字串【常用】

    @Autowired
    RedisTemplate redisTemplate;//操作k-v都是物件    
	@Test
    public void test01(){
//        stringRedisTemplate.opsForValue().append("msg", "hello");
        String msg = stringRedisTemplate.opsForValue().get("msg");
        System.out.println(msg);
    }

寫入資料

37.redis

讀取資料

38.redis02

3、測試儲存物件

物件需要序列化

1、序列化bean物件

public class Employee implements Serializable {

2、將物件儲存到Redis

@Test
public  void test02(){
    Employee emp = employeeMapper.getEmpById(2);
    redisTemplate.opsForValue().set("emp-01", emp);
}

3、效果演示

38.redis03

4、以json方式傳輸物件

1、新建一個Redis的配置類MyRedisConfig,

@Configuration
public class MyRedisConfig {
    @Bean
    public RedisTemplate<Object, Employee> empRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate<Object, Employee> template = new RedisTemplate<Object, Employee>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<Employee> jsonRedisSerializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
        template.setDefaultSerializer(jsonRedisSerializer);
        return template;
    }

2、編寫測試類

 @Autowired
 RedisTemplate<Object,Employee> empRedisTemplate;
@Test
public  void test02(){
    Employee emp = employeeMapper.getEmpById(2);
    empRedisTemplate.opsForValue().set("emp-01", emp);

}

3、測試效果

39.redis04

二、SpringBoot的訊息中介軟體

1、JMS&AMQP簡介

1、非同步處理

同步機制

09.同步

併發機制

10.非同步

訊息佇列機制

11.訊息

2、應用解耦

使用中介軟體,將兩個服務解耦,一個寫入,一個訂閱

3、流量削鋒

例如訊息佇列的FIFO,限定元素的長度,防止出現多次請求導致的誤操作

概述

1、大多數應用,可以通過訊息服務中介軟體來提升系統的非同步通訊、拓展解耦能力

2、訊息服務中的兩個重要概念:

訊息代理(message broker)和目的地(destination),當訊息傳送者傳送訊息以後,將由訊息代理接管,訊息代理保證訊息傳遞到指定的目的地。

3、訊息佇列主要的兩種形式的目的地

1)、佇列(queue):點對點訊息通訊【point-to-point】,取出一個沒一個,一個釋出,多個消費

2)、主題(topic):釋出(publish)/訂閱(subscribe)訊息通訊,多人【訂閱者】可以同時接到訊息

4、JMS(Java Message Service) Java訊息服務:

  • 基於JVM訊息規範的代理。ActiveMQ/HornetMQ是JMS的實現

5、AMQP(Advanced Message Queuing Protocol)

  • 高階訊息佇列協議,也是一個訊息代理的規範,相容JMS
  • RabbitMQ是AMQP的實現
JMS AMQP
定義 Java API 網路線級協議
跨平臺
跨語言
Model (1)、Peer-2-Peer
(2)、Pub/Sub
(1)、direct exchange
(2)、fanout exchange
(3)、topic change
(4)、headers exchange
(5)、system exchange
後四種都是pub/sub ,差別路由機制做了更詳細的劃分
支援訊息型別 TextMessage
MapMessage
ByteMessage
StreamMessage
ObjectMessage
Message
byte[]通常需要序列化

6、SpringBoot的支援

spring-jms提供了對JMS的支援

spring-rabbit提供了對AMQP的支援

需要建立ConnectionFactory的實現來連線訊息代理

提供JmsTemplate,RabbitTemplate來發送訊息

@JmsListener(JMS)[email protected](AMQP)註解在方法上的監聽訊息代理髮布的訊息

@EnableJms,@EnableRabbit開啟支援

7、SpringBoot的自動配置

  • JmsAutoConfiguration
  • RabbitAutoConfiguration

2、RabbitMQ簡介

AMQP的實現

1、核心概念

Message:訊息頭和訊息體組成,訊息體是不透明的,而訊息頭上則是由一系列的可選屬性組成,屬性:路由鍵【routing-key】,優先順序【priority】,指出訊息可能需要永續性儲存【delivery-mode】

Publisher:訊息的生產者,也是一個向交換器釋出訊息的客戶端應用程式

Exchange:交換器,用來接收生產者傳送的訊息並將這些訊息路由給伺服器中的佇列

Exchange的4中型別:direct【預設】點對點,fanout,topic和headers, 釋出訂閱,不同型別的Exchange轉發訊息的策略有所區別

Queue:訊息佇列,用來儲存訊息直到傳送給消費者,它是訊息的容器,也是訊息的終點,一個訊息可投入一個或多個佇列,訊息一直在佇列裡面,等待消費者連線到這個佇列將資料取走。

Binding:繫結,佇列和交換機之間的關聯,多對多關係

Connection:網路連線,例如TCP連線

Channel:通道,多路複用連線中的一條獨立的雙向資料流通道,通道是建立在真是的TCP連結之內的虛擬連線AMQP命令都是通過通道傳送出去的。不管是釋出訊息,訂閱佇列還是接受訊息,都是通道,減少TCP的開銷,複用一條TCP連線。

Consumer:訊息的消費者,表示一個從訊息佇列中取得訊息的客戶端的 應用程式

VirtualHost:小型的rabbitMQ,相互隔離

Broker:表示訊息佇列 服務實體

13.RabbitMQ結構

2、RabbitMQ的執行機制

Exchange的三種方式

direct:根據路由鍵直接匹配,一對一

14.RabbitMQDirect

fanout:不經過路由鍵,直接傳送到每一個佇列

14.RabbitMQfaout

topic:類似模糊匹配的根據路由鍵,來分配繫結的佇列

14.RabbitMQtopic

3、RabbitMQ安裝測試

1、開啟虛擬機器,在docker中安裝RabbitMQ

#1.安裝rabbitmq,使用映象加速
docker pull registry.docker-cn.com/library/rabbitmq:3-management
[[email protected] ~]# docker images
REPOSITORY                                     TAG                 IMAGE ID            CREATED             SIZE
registry.docker-cn.com/library/rabbitmq        3-management        c51d1c73d028        11 days ago         149 MB
#2.執行rabbitmq
##### 埠:5672 客戶端和rabbitmq通訊 15672:管理介面的web頁面

docker run -d -p 5672:5672 -p 15672:15672 --name myrabbitmq c51d1c73d028

#3.檢視執行
docker ps

2、開啟網頁客戶端並登陸,賬號【guest】,密碼【guest】,登陸

13.rabbitmq

3、新增 【direct】【faout】【topic】的繫結關係等

1)、新增Exchange,分別新增exchange.directexchange.fanoutexchange.topic

15.exchanges

2)、新增 Queues,分別新增lxy.news、wdjr、wdjr.emps、wdjr.news

16.queues

3)、點選【exchange.direct】新增繫結規則

17.bind

4)、點選【exchange.fanout】新增繫結規則

18,bindfanout

5)、點選【exchange.topic】新增繫結規則

19,bind_topic

/*: 代表匹配1個單詞

/#:代表匹配0個或者多個單詞

4、釋出資訊測試

【direct】釋出命令,點選 Publish message

20.publish-direct

檢視佇列的數量

21.queue-direct

點選檢視傳送的資訊

22.msg-direct

【fanout】的釋出訊息

23.pub-fanout

佇列資訊

24.queue-fanout

隨意一個數據信息例如:wdjr.emp

25.msg-fanout

【topic】釋出資訊測試

26.pub-topic

佇列的值

27.que-topic

資訊檢視

28.msg-topic

4、建立工程整合

1、RabbitAutoConfiguration
2、自動配置了連線工廠 ConnectionFactory
3、RabbitProperties封裝了 RabbitMQ
4、RabbitTemplate:給RabbitMQ傳送和接受訊息的
5、AmqpAdmin:RabbitMQ的系統管理功能元件
1、RabbitTemplate

1、新建SpringBoot工程,SpringBoot1.5+Integeration/RabbitMQ+Web

2、RabbitAutoConfiguration檔案

3、編寫配置檔案application.yml

spring:
  rabbitmq:
    host: 192.168.179.131
    port: 5672
    username: guest
    password: guest

4、編寫測試類,將HashMap寫入Queue

 @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    public void contextLoads() {
        //Message需要自己構建一個;定義訊息體內容和訊息頭
        // rabbitTemplate.send(exchange, routingKey, message);
        //Object 預設當成訊息體,只需要傳入要傳送的物件,自動化序列傳送給rabbitmq;
        Map<String,Object> map = new HashMap<>();
        map.put("msg", "這是第一個資訊");
        map.put("data", Arrays.asList("helloWorld",123,true));
        //物件被預設序列以後傳送出去
        rabbitTemplate.convertAndSend("exchange.direct","wdjr.news",map);
    }

5、檢視網頁的資訊

29.dir-idea

6、取出佇列的值

取出佇列中資料就沒了

@Test
public void reciverAndConvert(){

    Object o = rabbitTemplate.receiveAndConvert("wdjr.news");
    System.out.println(o.getClass());
    System.out.println(o);

}

結果

class java.util.HashMap
{msg=這是第一個資訊, data=[helloWorld, 123, true]}

7、使用Json方式傳遞,並傳入物件Book

1)、MyAMQPConfig

@Configuration
public class MyAMQPConfig  {

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}

2)、編寫Book實體類

package com.wdjr.amqp.bean;

public class Book {
    private String  bookName;
    private String author;

    public Book(){

    }

    public Book(String bookName, String author) {
        this.bookName = bookName;
        this.author = author;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override