文章
问答
冒泡
利用SpringBootCondition实现自动装配非必要包不引入
背景
我们要一个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
spring boot

关于作者

落雁沙
非典型码农
获得点赞
文章被阅读