Spring Cache(二) - 自定义两级缓存(Caff

引言

上一篇文章Cache在Springboot中的实现与原理已经介绍了Spring Cache在SpringBoot中的实现与原理, 本文就来聊一聊如何在使用spring cache的时候结合本地缓存 + redis. 也就是自定义两级缓存.

1: 自定义缓存的原理

上一篇文章中已经提到, Cache接口定义了缓存操作的行为,CacheManager定义了如何产生Cache,我们需要定义自己的两级Cache,所以我们就需要定义自己的CacheCacheManager。在Spring已经帮我们提供了一个CacheManager的实现类CompositeCacheManager

2:如何实现

设计与实现思路

  1. 这里我们采用Caffeine(一级缓存) + Redis(二级缓存)。实现效果的流程图如下:

两级缓存流程图

2. 需要的关键类与接口

  • CompositeCacheManager:它是组合CacheManager的一个实现,其中它的setCacheManagers方法允许设置一个或者多个CacheManager。
  • Cache:定义缓存操作的行为,比如我们可以存到Redis或者内存中等,都可以由我们自己来实现
  • CacheSyncManager: 自定义缓存同步管理接口,定义了如何进行缓存的同步, 比如使用Redis发布订阅或者RabbitMq消息来实现缓存的同步
  • CaffeineRedisCacheManager:组合CacheManager, 存储自定义两级缓存
  • MultipleCache:Cache的具体实现
  • MultipleCacheNode:缓存节点, 本地+Redis

3. 部分代码及其实现

CacheManager相关如下:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
java复制代码@Bean
@ConditionalOnMissingBean(CacheManager.class)
public CompositeCacheManager cacheManager(CacheSyncManager cacheSyncManager, RedisCacheWriter redisCacheWriter) {
List<CacheManager> cacheManagerList = new LinkedList<>();

//caffeine
if (!CollectionUtils.isEmpty(cacheProperties.getCaffeine())) {
//构建CaffeineCacheManager
cacheManagerList.add(buildCaffeineCacheManager(cacheProperties.getCaffeine(), cacheSyncManager));
}

//redis
if (!CollectionUtils.isEmpty(cacheProperties.getRedis())) {
//构建RedisCacheManager
cacheManagerList.add(buildRedisCacheManager(redisCacheWriter, cacheSyncManager, cacheProperties.getRedis()));
}

//caffeine + redis
if (!CollectionUtils.isEmpty(cacheProperties.getMultiple())) {
cacheManagerList.add(buildCaffeineRedis(cacheSyncManager, redisCacheWriter));
}

CompositeCacheManager cacheManager = new CompositeCacheManager();
cacheManager.setCacheManagers(cacheManagerList);
return cacheManager;
}

/*
* 构建CaffeineCacheManger缓存
*/
private CaffeineCacheManagerAdapter buildCaffeineCacheManager(Collection<CacheConfigProperties.CaffeineCacheConfig> configs,
CacheSyncManager cacheSyncManager) {
Set<CaffeineCacheManagerAdapter.CacheConfig> caffeineCacheConfigs = new LinkedHashSet<>();
Set<String> caffeineCacheNames = new LinkedHashSet<>();
Map<String, AbstractCaffeineCacheStrategy> cacheStrategyMap = new HashMap<>(8);
Map<String, Set<CacheDecorationHandler>> decorationHandlers = new HashMap<>(8);
configs.forEach(item -> {
caffeineCacheNames.add(item.getName());
CaffeineCacheManagerAdapter.CacheConfig cacheConfig = new CaffeineCacheManagerAdapter.CacheConfig();
cacheConfig.setExpireAfterAccess(item.getExpireAfterAccess());
cacheConfig.setExpireAfterWrite(item.getExpireAfterWrite());
cacheConfig.setInitialCapacity(item.getInitialCapacity());
cacheConfig.setMaximumSize(item.getMaximumSize());
cacheConfig.setName(item.getName());
cacheConfig.setDisableSync(item.isDisableSync());
cacheConfig.setEnableSoftRef(item.isEnableSoftRef());

//Caffeine 的CacheLoader。
if (!ObjectUtils.isEmpty(item.getCacheLoader())
&& cacheLoaderMap.containsKey(item.getCacheLoader())) {
cacheConfig.setCacheLoader(cacheLoaderMap.get(item.getCacheLoader()));
}

//Caffeine缓存配置信息
caffeineCacheConfigs.add(cacheConfig);
//Caffeine自定义缓存存储策略, 如果有的话则应用。
if (!ObjectUtils.isEmpty(item.getStrategy()) && strategyMap.containsKey(item.getStrategy())) {
CacheStrategy strategy = strategyMap.get(item.getStrategy());
if (strategy instanceof AbstractCaffeineCacheStrategy) {
cacheStrategyMap.put(item.getName(), (AbstractCaffeineCacheStrategy) strategy);
}
}

//缓存包装策略, 如果有的话则应用
if (!ObjectUtils.isEmpty(item.getDecorators())) {
List<String> decoratorList = Arrays.asList(item.getDecorators().split(","));
Set<CacheDecorationHandler> collect = decoratorList.stream()
.map(decorationHandlerMap::get).collect(Collectors.toSet());
decorationHandlers.put(item.getName(), collect);
}
});
//CacheManager 继承于 CaffeineCacheManager
return new CaffeineCacheManagerAdapter(caffeineCacheNames, caffeineCacheConfigs, cacheStrategyMap,
decorationHandlers, cacheSyncManager);
}

/*
* 构建RedisCacheManager
*/
private RedisCacheManagerAdapter buildRedisCacheManager(RedisCacheWriter cacheWriter,
CacheSyncManager cacheSyncManager,
Collection<CacheConfigProperties.RedisCacheConfig> configs) {
Set<String> redisCacheNames = new LinkedHashSet<>();
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
Map<String, AbstractRedisCacheStrategy> cacheStrategyMap = new HashMap<>(8);
Map<String, Set<CacheDecorationHandler>> decorationHandlers = new HashMap<>(8);
configs.forEach(item -> {
//缓存名与缓存的key - value关系等
redisCacheNames.add(item.getName());
redisCacheConfigurationMap.put(item.getName(), redisCacheConfiguration(item));

//自定义的缓存策略,如果有则应用,没有就是用默认策略
if (item.getStrategy() != null && strategyMap.containsKey(item.getStrategy())) {
CacheStrategy strategy = strategyMap.get(item.getStrategy());
if (strategy instanceof AbstractRedisCacheStrategy) {
cacheStrategyMap.put(item.getName(), (AbstractRedisCacheStrategy) strategy);
}
} else {
DefaultRedisCacheStrategy cacheStrategy = new DefaultRedisCacheStrategy(item.getName());
cacheStrategyMap.put(item.getName(), cacheStrategy);
}

//缓存包装策略,如果有则应用
if (!ObjectUtils.isEmpty(item.getDecorators())) {
List<String> decoratorList = Arrays.asList(item.getDecorators().split(","));
Set<CacheDecorationHandler> collect = decoratorList.stream()
.map(decorationHandlerMap::get).collect(Collectors.toSet());
decorationHandlers.put(item.getName(), collect);
}
});
//CacheManager 继承于 RedisCacheManager
RedisCacheManagerAdapter redisCacheManager = new RedisCacheManagerAdapter(cacheWriter, redisCacheNames,
false, redisCacheConfigurationMap, cacheStrategyMap, decorationHandlers);
redisCacheManager.initCaches();
return redisCacheManager;
}

/*
* 构建CaffeineRedisCacheManager
*/
private CaffeineRedisCacheManager buildCaffeineRedis(CacheSyncManager cacheSyncManager, RedisCacheWriter redisCacheWriter) {
Set<CacheConfigProperties.CaffeineCacheConfig> caffeineCacheConfigs = new LinkedHashSet<>();
Set<CacheConfigProperties.RedisCacheConfig> redisCacheConfigs = new LinkedHashSet<>();
Map<String, Set<CacheDecorationHandler>> decorationHandlers = new HashMap<>(8);
cacheProperties.getMultiple().forEach(item -> {
//缓存配置参数
CacheConfigProperties.CaffeineCacheConfig caffeineCacheConfig = item.getCaffeine();
caffeineCacheConfig.setName(item.getName());
CacheConfigProperties.RedisCacheConfig redisCacheConfig = item.getRedis();
redisCacheConfig.setName(item.getName());
caffeineCacheConfigs.add(caffeineCacheConfig);
redisCacheConfigs.add(redisCacheConfig);

//缓存装饰策略
if (!ObjectUtils.isEmpty(item.getDecorators())) {
List<String> decoratorList = Arrays.asList(item.getDecorators().split(","));
Set<CacheDecorationHandler> collect = decoratorList.stream()
.map(decorationHandlerMap::get).collect(Collectors.toSet());
decorationHandlers.put(item.getName(), collect);
}

});

//CacheManager, 由RedisCacheManagerAdapter + CaffeineCacheManagerAdapter共同实现
CaffeineRedisCacheManager multipleCacheManager
= new CaffeineRedisCacheManager(
buildCaffeineCacheManager(caffeineCacheConfigs, cacheSyncManager),
buildRedisCacheManager(redisCacheWriter, cacheSyncManager, redisCacheConfigs), decorationHandlers);
multipleCacheManager.initializeCaches();
return multipleCacheManager;
}

Cache相关如下:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
java复制代码public class MultipleCache implements Cache {

private MultipleCacheNode cacheNode;

public static MultipleCacheBuilder builder() {
return new MultipleCacheBuilder();
}

public MultipleCache(MultipleCacheNode cacheNode) {
this.cacheNode = cacheNode;
}

@Override
public String getName() {
return cacheNode.getName();
}

@Override
public Object getNativeCache() {
return cacheNode.getNativeCache();
}

@Override
public ValueWrapper get(Object key) {
return cacheNode.get(key);
}

@Override
public <T> T get(Object key, Class<T> type) {
return (T) cacheNode.get(key, type);
}

@Override
public <T> T get(Object key, Callable<T> valueLoader) {
return (T) cacheNode.get(key, valueLoader);
}

@Override
public void put(Object key, Object value) {
cacheNode.put(key, value);
}

@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
return cacheNode.putIfAbsent(key, value);
}

@Override
public void evict(Object key) {
cacheNode.evict(key);
}

@Override
public void clear() {
cacheNode.clear();
}

public static class MultipleCacheBuilder {
private MultipleCacheNode cache;

public MultipleCacheBuilder nextNode(Cache cache) {
MultipleCacheNode node = new MultipleCacheNode(cache);
if (this.cache == null) {
this.cache = node;
} else {
this.cache.setNext(node);
}
return this;
}

public MultipleCache build() {
return new MultipleCache(cache);
}
}
}

public class MultipleCacheNode<T extends Cache> implements Cache {

//下一个缓存Node
private MultipleCacheNode next;
//当前缓存
private T cache;

public MultipleCacheNode(T cache) {
this.cache = cache;
}

public void setNext(MultipleCacheNode next) {
this.next = next;
}

public boolean hasNext() {
return null != next;
}

@Override
public String getName() {
return cache.getName();
}

@Override
public Object getNativeCache() {
return cache.getNativeCache();
}

@Override
public ValueWrapper get(Object key) {
ValueWrapper value = cache.get(key);
if (null == value && hasNext()) {
value = next.get(key);
if (null != value) {
cache.putIfAbsent(key, value.get());
}
}
return value;
}

@Override
public <T> T get(Object key, Class<T> type) {
return cache.get(key, type);
}

@Override
public <T> T get(Object key, Callable<T> valueLoader) {
return cache.get(key, valueLoader);
}

@Override
public void put(Object key, Object value) {
if (hasNext()) {
next.put(key, value);
}
cache.put(key, value);
}

@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
return cache.putIfAbsent(key, value);
}

@Override
public void evict(Object key) {
if (hasNext()) {
next.evict(key);
}
cache.evict(key);
}

@Override
public void clear() {
if (hasNext()) {
next.clear();
}
cache.clear();
}
}

CacheSync相关:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
java复制代码@Bean(name = "redisCacheMessageSyncListenerContainer")
@ConditionalOnMissingBean(name = "redisCacheMessageSyncListenerContainer")
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, CacheSyncMessageListener receiver,
@Qualifier("syncCacheTaskExecutor") TaskExecutor executor) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setTaskExecutor(executor);
container.addMessageListener(receiver, new ChannelTopic(receiver.getChannelName()));
return container;
}

@Bean
@ConditionalOnMissingBean(CacheSyncManager.class)
public CacheSyncManager redisBasedCacheSyncServce(RedisTemplate redisTemplate) {
return new RedisCacheSyncManager(applicationName, redisTemplate);
}

@Bean
@ConditionalOnMissingBean(CacheSyncMessageListener.class)
public CacheSyncMessageListener cacheSyncMessageListener(CacheSyncManager cacheSyncManager, RedisTemplate redisTemplate) {
return new CacheSyncMessageListener(redisTemplate, cacheSyncManager);
}

/*
* 缓存同步处理接口定义
*/
public interface CacheSyncManager{

String SYNCCHANNEL = "cache-sync";

void publish(CacheSyncEvent event);

void handle(CacheSyncEvent event);

String getChannelName();
}

/*
* 抽象缓存同步类
*/
@Slf4j
public abstract class AbstractCacheSyncManager implements CacheSyncManager {


private static Map<String, CacheSyncEventHandler> handlerMap = new ConcurrentHashMap<>();

public static void registHandler(String name, CacheSyncEventHandler handler) {
handlerMap.put(name, handler);
}

protected String applicationName;

public AbstractCacheSyncManager(String appName) {
this.applicationName = appName;
}

public static void doHandle(CacheSyncEvent event) {
CacheSyncEventHandler handler = handlerMap.get(event.getCacheName());
if (null == handler) {
log.warn("不存在的缓存消息同步器:{}", event);
return;
}
//相关缓存事件
if (event instanceof PutEvent) {
handler.handlePut((PutEvent) event);
} else if (event instanceof EvictEvent) {
handler.handleEvict((EvictEvent) event);
} else if (event instanceof ClearEvent) {
handler.handleClear((ClearEvent) event);
} else {
log.warn("不支持的事件:{}", event);
}
}

@Override
public void handle(CacheSyncEvent event) {
doHandle(event);
}

@Override
public String getChannelName() {
return applicationName + ":" + SYNCCHANNEL;
}
}

/*
* 使用Redis的发布订阅来实现消息同步
*/
@Slf4j
public class RedisCacheSyncManager extends AbstractCacheSyncManager {

private RedisTemplate redisTemplate;

public RedisCacheSyncManager(String appName, RedisTemplate redisTemplate) {
super(appName);
this.redisTemplate = redisTemplate;
}

@Override
public void publish(CacheSyncEvent event) {
redisTemplate.convertAndSend(getChannelName(), event);
log.info("发送缓存同步消息: channel: {}, event: {}", getChannelName(), event);
}
}

/*
* 缓存事件定义
*/
public interface CacheSyncEventHandler {
/**
* 放入缓存事件
* @param event
*/
void handlePut(PutEvent event);

/**
* 清理缓存事件
* @param event
*/
void handleEvict(EvictEvent event);

/**
* 清除缓存事件
* @param event
*/
void handleClear(ClearEvent event);
}

/*
* 缓存消息监听器
*/
@Slf4j
public class CacheSyncMessageListener implements MessageListener {

private RedisTemplate redisTemplate;

private CacheSyncManager cacheSyncManager;

public CacheSyncMessageListener(RedisTemplate redisTemplate, CacheSyncManager cacheSyncManager) {
this.redisTemplate = redisTemplate;
this.cacheSyncManager = cacheSyncManager;
}

@Override
public void onMessage(Message message, byte[] pattern) {
log.debug("接收到缓存同步消息:{}", message);
try {
CacheSyncEvent event = (CacheSyncEvent) redisTemplate
.getValueSerializer().deserialize(message.getBody());
if (ObjectUtils.nullSafeEquals(HostUtil.getHostName(), event.getHost())) {
log.debug("该消息由本机发出,无须处理:{}", event);
return;
}
cacheSyncManager.handle(event);
} catch (Exception e) {
log.error("同步消息异常!", e);
}
}

public String getChannelName() {
return cacheSyncManager.getChannelName();
}
}

使用spring.factories机制来确保能被SpringBoot工程扫描到

1
2
yml复制代码org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zy.github.multiple.cache.config.CacheManagerAutoConfiguration

以上就是关键部分的代码,总的来说关键部分就是:

  • 1: 实现自己的CacheManager
  • 2: 实现自己的Cache
  • 3: 实现本地缓存之间的同步

如何使用

1:在启动类上加上@EnableCaching

1
2
3
4
5
6
7
8
9
java复制代码@EnableCaching
@SpringBootApplication
public class MultipleCacheApplication {

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

}

2:配置Cache的属性信息

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
36
37
yml复制代码spring:
redis:
port: # redis server port
host: # redis server host
lettuce:
pool:
max-active: 50
max-wait: 2000
max-idle: 20
min-idle: 5
# cluster:
# nodes:
# lettuce:
# pool:
# max-active: 50
# max-wait: 2000
# max-idle: 20
# min-idle: 5
application:
name: aaaaaaaaaa
multiple-cache:
# redis:
# - name: testCache #缓存名称
# expire: 100 #缓存过期时间
# caffeine:
# - name: testCache #缓存名称
# expireAfterAccess: 30 #缓存过期时间
# initialCapacity: 100 #缓存初始化存储大小
# maximumSize: 1000 #缓存最大存储大小
multiple:
- name: testCache #缓存名称
caffeine:
expireAfterAccess: 30 #缓存过期时间
initialCapacity: 100 #缓存初始化存储大小
maximumSize: 1000 #缓存最大存储大小
redis:
expire: 100 #缓存过期时间

3: 使用方式没有任何变化,还是基于注解的形式即可。

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
java复制代码@RestController
public class DemoController {
@Autowired
private DemoService demoService;


@RequestMapping("cache-test")
public List<User> demo(){
return demoService.cacheTest("testId");
}
}

@Service
public class DemoService {

@Cacheable(cacheNames = "testCache", key = "#id")
public List<User> cacheTest(String id){
User user = new User();
user.setAge(22);
user.setName("xxx");

List<User> users = new ArrayList<>();
users.add(user);
return users;
}
}

4:效果

5:依赖相关

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
pom复制代码<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.zy</groupId>
<artifactId>multiple-cache</artifactId>
<version>0.0.3</version>
<name>multiple-cache</name>
<description>多级缓存</description>

<properties>
<java.version>1.8</java.version>
<jackson.version>2.11.3</jackson.version>
<commons-pool2.version>2.9.0</commons-pool2.version>
<caffeine.version>2.8.5</caffeine.version>
<lettuce.version>6.0.1.RELEASE</lettuce.version>
<maven-plugins.version>3.2.0</maven-plugins.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>


<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>${lettuce.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>${caffeine.version}</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${commons-pool2.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-plugins.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
<configuration>
<excludes>
<exclude>**/MultipleCacheApplication.java</exclude>
<exclude>**/application.yml</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-plugins.version}</version>
<configuration>
<excludes>
<exclude>**/application.yml</exclude>
<exclude>**/MultipleCacheApplication**</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>



</project>

传送门

最后附上 github链接

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%