Spring Boot 系列二(手写一个Starter)

157 阅读2分钟

通过Idea新建一个Gradle项目

image.png
image.png

配置Spring Boot的Gradle信息

配置插件信息

plugins {
    id 'java-gradle-plugin'
    id 'maven-publish'
    id 'com.gradle.plugin-publish' version '0.12.0'
}

配置发布信息

group = 'com.springboot.starter.demo'
version = '1.0-SNAPSHOT'

publishing {
    repositories {
        maven {
            url "https://mirrors.cloud.tencent.com/nexus/repository/maven-public/"
        }
    }
}

配置仓库信息

repositories {
    mavenLocal()
    maven {
        url "https://mirrors.cloud.tencent.com/nexus/repository/maven-public/"
    }
}

配置依赖的jar包信息

dependencies {

    ext {
        springBootVersion = "2.3.7.RELEASE"
    }

    implementation "org.springframework.boot:spring-boot-autoconfigure:${springBootVersion}"
    annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:${springBootVersion}"
    compileOnly "org.springframework.boot:spring-boot-starter-web:${springBootVersion}"
    compileOnly "org.springframework.boot:spring-boot-starter-aop:${springBootVersion}"

    implementation 'org.redisson:redisson:3.15.2'
    
    // lombok
    compileOnly 'org.projectlombok:lombok:1.18.10'
    annotationProcessor 'org.projectlombok:lombok:1.18.10'
}

compileJava.dependsOn(processResources)

开始Starter编写

定义一个句解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EnableRedissonLock {
    int waitTime() default 0;//等待时间
    TimeUnit timeUnit() default TimeUnit.SECONDS;//时间单位
    int leaseTime() default 3;//锁定时间
}

定义一个切面

@Aspect
@ConditionalOnWebApplication
@Slf4j
public class RedissonLockAspect {

    private RedissonProvider redissonProvider;

    public RedissonLockAspect(RedissonProvider redissonProvider) {
        this.redissonProvider = redissonProvider;
    }

    @Around(value = "@annotation(com.springboot.redisson.starter.annotation.EnableRedissonLock)")
    public Object handleRedisLock(ProceedingJoinPoint joinPoint) throws Throwable, Exception {
        Object obj = null;
        EnableRedissonLock enableRedisLock = ((MethodSignature) joinPoint.getSignature())
                .getMethod()
                .getAnnotation(EnableRedissonLock.class);
        Object[] args = joinPoint.getArgs();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String uri = request.getRequestURI();
        String method = request.getMethod();
        String userId = request.getHeader("USER-ID");
        String params = Objects.nonNull(request.getParameterMap()) ? request.getParameterMap().toString() : "";
        StringBuilder lockKey = new StringBuilder();
        lockKey.append(uri)
                .append("_")
                .append(method)
                .append("_")
                .append(userId)
                .append("_")
                .append(params);
        int waitTime = enableRedisLock.waitTime();
        TimeUnit timeUnit = enableRedisLock.timeUnit();
        int leaseTime = enableRedisLock.leaseTime();
        log.debug("handleRedissonLock lockKey={}", lockKey.toString());
        try {
            if (redissonProvider.tryLock(lockKey.toString(), timeUnit, waitTime, leaseTime)) {
                log.debug("handleRedissonLock lock success");
                obj = joinPoint.proceed(args);
                redissonProvider.unlock(lockKey.toString());
                return obj;
            } else {
                log.debug("handleRedissonLock lock failure");
                Map<String, String> map = new HashMap<>();
                map.put("error", "请求太频繁");
                ResponseUtil response = new ResponseUtil(905, map, null);
                return response;
            }
        } catch (Exception ex){
            redissonProvider.unlock(lockKey.toString());
            throw ex;
        }
    }
}

定义redisson属性相关配置

@Data
@ConfigurationProperties(prefix = "redisson.starter")
public class RedissonProperties {

    private String host;

    private Integer port;

    private String prefix = "redisson_starter_";

    private String password;
}

定义redisson 实现类

@Slf4j
@ConditionalOnClass(Redisson.class)
public class RedissonProvider {

    private RedissonClient redissonClient;

    private String prefix;

    public RedissonProvider(RedissonClient redissonClient, String prefix) {
        this.redissonClient = redissonClient;
        this.prefix = prefix;
    }

    public RLock lock(String lockKey) {
        lockKey = prefix + lockKey;
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
    }

    public RLock lock(String lockKey, int leaseTime) {
        lockKey = prefix + lockKey;
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, TimeUnit.SECONDS);
        return lock;
    }

    public RLock lock(String lockKey, TimeUnit unit ,int timeout) {
        lockKey = prefix + lockKey;
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
        return lock;
    }

    public void unlock(String lockKey) {
        try {
            lockKey = prefix + lockKey;
            RLock lock = redissonClient.getLock(lockKey);
            lock.unlock();
        } catch (Exception ex) {

        }
    }

    public void unlock(RLock lock) {
        lock.unlock();
    }

    public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
        lockKey = prefix + lockKey;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
    }
}

定义自动配置信息

@Configuration
@ConditionalOnProperty(prefix = "redisson.starter", name = "enabled", havingValue = "true")
@EnableConfigurationProperties(RedissonProperties.class)
@Import({RedissonLockAspect.class})
public class RedissonConfiguration {

    @Resource
    private RedissonProperties redissonProperties;

    @Bean
    public RedissonProvider redissonProvider() {
        Config config = new Config();
        String redisUrl = String.format("redis://%s:%s", redissonProperties.getHost()+"", redissonProperties.getPort()+"");
        if(StringUtils.isNotEmpty(redissonProperties.getPassword())) {
            config.useSingleServer()
                    .setAddress(redisUrl)
                    .setPassword(redissonProperties.getPassword())
                    .setTimeout(3000)
                    .setConnectionMinimumIdleSize(2)
                    .setConnectionPoolSize(32);
        } else {
            config.useSingleServer()
                    .setAddress(redisUrl)
                    .setTimeout(3000)
                    .setConnectionMinimumIdleSize(2)
                    .setConnectionPoolSize(32);
        }
        return new RedissonProvider(Redisson.create(config), redissonProperties.getPrefix());
    }
}

配置EnableAutoConfiguration信息

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
net.coding.testing.base.spring.boot.starter.autoconfigure.RedissonConfiguration

具体源码地址:springboot-redisson-starter

好了,文章写到这结束了,欢迎小伙伴们点赞、收藏、评论,一键三连走起呀,下篇见~~