背景
我们要一个java锁的实现,如果是单机环境就使用线程锁,如果是分布式环境就使用分布式锁。线程锁是基于thread实现的,分布式锁是基于redission实现的。如果在单机场景下,其实我们并不需要redission的包,所以,我们的思路是默认情况下不引入redission包,如果需要用可以在项目中引入。如果只是打包的话我们可以通过maven的optional属性设置为ture来解决。但是,在打成spring boot start包实现自动装配的时候,一般情况下会显示去引入依赖,如果jar包不存在会报错。那么,我们需要一个方案来解决这个问题。
思路
我们其实在spring的cache中有类似的场景,这里我们可以参考这个实现来做。
实现代码
前置条件,我们已经实现了ThreadLockTemplate和RedisLockTemplate这两个TrionesLockTemplate的实现类。
设置一个枚举,用于配置类型
public enum LockType {
THREAD,
REDIS
}
配置属性
@Data
@ConfigurationProperties(prefix = "triones.lock")
public class LockProperties {
private LockType type = LockType.THREAD;
}
LockConfigurations
package com.trionesdev.boot.lock.autoconfigure;
import org.springframework.util.Assert;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
public class LockConfigurations {
private static final Map<LockType, String> MAPPINGS;
static {
Map<LockType, String> mappings = new EnumMap<>(LockType.class);
mappings.put(LockType.THREAD, ThreadLockConfiguration.class.getName());
mappings.put(LockType.REDIS, RedisLockConfiguration.class.getName());
MAPPINGS = Collections.unmodifiableMap(mappings);
}
static String getConfigurationClass(LockType lockType) {
String configurationClassName = MAPPINGS.get(lockType);
Assert.state(configurationClassName != null, () -> "Unknown lock type " + lockType);
return configurationClassName;
}
static LockType getType(String configurationClassName) {
for (Map.Entry<LockType, String> entry : MAPPINGS.entrySet()) {
if (entry.getValue().equals(configurationClassName)) {
return entry.getKey();
}
}
throw new IllegalStateException("Unknown configuration class " + configurationClassName);
}
}
LockCondition
配置条件,满足条件的装配类才会生效
public class LockCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
String sourceClass = "";
if (metadata instanceof ClassMetadata classMetadata) {
sourceClass = classMetadata.getClassName();
}
ConditionMessage.Builder message = ConditionMessage.forCondition("Lock", sourceClass);
Environment environment = context.getEnvironment();
try {
BindResult<LockType> specified = Binder.get(environment).bind("triones.lock.type", LockType.class);
if (!specified.isBound()) { //如果没有配置 triones.lock.type 属性,当前Condition就是自动装配
return ConditionOutcome.match(message.because("automatic lock type"));
}
LockType required = LockConfigurations.getType(((AnnotationMetadata) metadata).getClassName());
if (specified.get() == required) {
return ConditionOutcome.match(message.because(specified.get() + " lock type"));
}
}
catch (BindException ex) {
// Ignore
}
return ConditionOutcome.noMatch(message.because("unknown lock type"));
}
}
ThreadLockConfiguration
线程锁装配,该配装配会在缺失TrionesLockTemplate.class实现Bean和满足LockCondition.class条件的时候进行装配
@Configuration
@ConditionalOnMissingBean({TrionesLockTemplate.class})
@Conditional({LockCondition.class})
public class ThreadLockConfiguration {
@Bean
public ThreadLockTemplate threadLockTemplate() {
return new ThreadLockTemplate();
}
}
RedisLockConfiguration
redis分布式锁装配,该配装配会在有RedissonClient.class并缺失TrionesLockTemplate.class实现Bean和满足LockCondition.class条件的时候进行装配
@RequiredArgsConstructor
@Configuration
@ConditionalOnClass({ RedissonClient.class })
@ConditionalOnMissingBean({TrionesLockTemplate.class})
@Conditional({LockCondition.class})
public class RedisLockConfiguration {
@Bean
public RedisLockTemplate redisLockTemplate(RedissonClient redissonClient) {
return new RedisLockTemplate(redissonClient);
}
}
LockAutoConfiguration
自动装配文件,这里要注意的是,要在ThreadLockConfiguration.class, RedisLockConfiguration.class之后进行装配
@AutoConfiguration(value = "com.trionesdev.boot.lock.autoconfigure.LockAutoConfiguration", after = {
ThreadLockConfiguration.class, RedisLockConfiguration.class
})
@EnableConfigurationProperties(value = {
LockProperties.class
})
@Import({LockAutoConfiguration.LockConfigurationImportSelector.class})
public class LockAutoConfiguration {
@Bean
public LockAspect lockAspect(TrionesLockTemplate lockTemplate) {
return new LockAspect(lockTemplate);
}
//通过@Import,不用将所有的Configuration类写在配置文件中
static class LockConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
LockType[] types = LockType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = LockConfigurations.getConfigurationClass(types[i]);
}
return imports;
}
}
}
这样,就可以在不使用redis分布式锁实现方案的时候,不用引入redisson的Jar包。一定程度上可以减少打包体积。
github地址 https://github.com/trionesdev/triones-spring-boot-starters/tree/develop/triones-spring-boot-starter/src/main/java/com/trionesdev/boot/lock/autoconfigure