如何实现自己的spring boot starter(一)
我们在SpringBoot中使用redis的时候,直接引用 spring-boot-starter-data-redis 的pom,然后配置
spring.redis.host = localhost spring.redis.port = 6379 spring.redis.password = 123456
就可以使用在我们的业务系统里注入redisTemplate来操作redis了,但是他是怎么工作的呢?
让我们走进SpringBoot的源码,看几张截图:
在 spring-boot-autoconfigure 模块中
然后暴露出一个 spring-boot-starter-data-redis 模块,留给业务方引用
可以看到这个 starter 其实就是一个门面,里面组装了redis所需要的依赖, 那我们也模仿着这种写法,实现自己的 starter。我的项目结构是这样的:
以glocalcommon-redis为例子,这里写的是redis的自动配置,下面的glocalcommon-redis-spring-boot-starter里仅仅引用了glocalcommon-redis的jar而已,然后业务系统想使用redis的话,直接引用下面的pom,然后配置redis的host,password即可。
glocalcommon-redis pomcom.gane.glocalcommon glocalcommon-redis-spring-boot-starter1.0-SNAPSHOT
4.0.0 com.gane.glocalcommon glocalcommon-redis1.0-SNAPSHOT org.springframework.boot spring-boot-starter2.2.6.RELEASE compile com.gane.glocalcommon glocalcommon-utils-spring-boot-starter1.0-SNAPSHOT redis.clients jedis2.9.0
这里的 glocalcommon-utils-spring-boot-starter 你们可以换成其他的工具类
RedisProperties到时候业务费写配置的时候,会以 glocalcommon.redis 开头,如:glocalcommon.redis.host=localhost
package com.gane.glocalcommon.redis;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "glocalcommon.redis")
public class RedisProperties {
private static final String DEFAULT_HOST = "localhost";
private static final int DEFAULT_PORT = 6379;
private static final String DEFAULT_URL = DEFAULT_HOST + ":" + DEFAULT_PORT;
private String host = DEFAULT_URL;
private int port = DEFAULT_PORT;
private int timeout = 2000;
private String password;
private int database = 0;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getDatabase() {
return database;
}
public void setDatabase(int database) {
this.database = database;
}
}
RedisClient
这里主要是redis的操作方法
package com.gane.glocalcommon.redis;
import com.gane.glocalcommon.redis.serializer.ObjectSerializer;
import com.gane.glocalcommon.redis.serializer.RedisSerializer;
import com.gane.glocalcommon.redis.serializer.StringSerializer;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public abstract class RedisClient {
public static final Logger log = LoggerFactory.getLogger(RedisClient.class);
private static final int DEFAULT_EXPIRE = -1;
private RedisSerializer keySerializer = new StringSerializer();
private RedisSerializer valueSerializer = new ObjectSerializer();
protected abstract Jedis getResource();
private JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
public JedisPoolConfig getJedisPoolConfig() {
return jedisPoolConfig;
}
public String get(String key) {
String value = null;
Jedis jedis = null;
try {
jedis = this.getResource();
if (jedis.exists(key)) {
value = jedis.get(key);
value = StringUtils.isNotBlank(value) && !"nil".equalsIgnoreCase(value) ? value : null;
log.debug("get {} = {}", key, value);
}
} catch (Exception e) {
log.error("get {} = {}", new Object[]{key, value, e});
} finally {
jedis.close();
}
return value;
}
public Object getObj(String key) {
Object obj = null;
Jedis jedis = null;
try {
jedis = this.getResource();
if (jedis.exists(key)) {
byte[] bytes = jedis.get(key.getBytes());
if (bytes != null && bytes.length != 0) {
obj = valueSerializer.deserialize(bytes);
}
log.debug("getObj {} = {}", key, obj);
}
} catch (Exception e) {
log.error("getObj {} = {}", new Object[]{key, obj, e});
} finally {
jedis.close();
}
return obj;
}
public String set(String key, String value) {
return this.set(key, value, DEFAULT_EXPIRE);
}
public String setObj(String key, Object value) {
return this.setObj(key, value, DEFAULT_EXPIRE);
}
public String set(String key, String value, int expire) {
Jedis jedis = null;
String result = null;
try {
jedis = this.getResource();
result = jedis.set(key, value);
if (expire >= 0) {
jedis.expire(key, expire);
}
log.debug("set {} = {}", key, value);
} catch (Exception e) {
log.error("set {} = {}", new Object[]{key, value, e});
} finally {
jedis.close();
}
return result;
}
public String setObj(String key, Object value, int expire) {
Jedis jedis = null;
String result = null;
try {
jedis = this.getResource();
result = jedis.set(key.getBytes(), valueSerializer.serialize(value));
if (expire >= 0) {
jedis.expire(key, expire);
}
log.debug("setObj {} = {}", key, value);
} catch (Exception e) {
log.error("setObj {} = {}", new Object[]{key, value, e});
} finally {
jedis.close();
}
return result;
}
public Long del(String key) {
Long value = null;
Jedis jedis = null;
try {
jedis = this.getResource();
if (jedis.exists(key)) {
value = jedis.del(key);
log.debug("del {} = {}", key, value);
}
} catch (Exception e) {
log.error("del {} = {}", new Object[]{key, value, e});
} finally {
jedis.close();
}
return value;
}
public boolean exists(String key) {
boolean result = false;
Jedis jedis = null;
try {
jedis = this.getResource();
result = jedis.exists(key);
} catch (Exception e) {
log.error("exists {}", key, e);
} finally {
jedis.close();
}
return result;
}
public Long incr(String key) {
return incrWithExpire(key, 0);
}
public Long incrWithExpire(String key, int seconds) {
Jedis jedis = null;
Long incr;
try {
if (StringUtils.isBlank(key)) {
incr = null;
return incr;
}
jedis = this.getResource();
incr = jedis.incr(key);
if (seconds >= 0) {
jedis.expire(key, seconds);
}
} catch (Exception e) {
log.error("incr {} ", key, e);
return null;
} finally {
jedis.close();
}
return incr;
}
public Long incrBy(String key, Long integer) {
Jedis jedis = null;
Long incrBy;
try {
if (StringUtils.isBlank(key)) {
incrBy = null;
return incrBy;
}
if (integer == null) {
integer = 0L;
}
jedis = this.getResource();
incrBy = jedis.incrBy(key, integer);
} catch (Exception e) {
log.error("incr {} ", key, e);
return null;
} finally {
jedis.close();
}
return incrBy;
}
public Long incrByWithExpire(String key, Long integer, int seconds) {
Jedis jedis = null;
Long incrBy;
try {
if (StringUtils.isBlank(key)) {
incrBy = null;
return incrBy;
}
if (integer == null) {
integer = 0L;
}
jedis = this.getResource();
incrBy = jedis.incrBy(key, integer);
if (seconds >= 0) {
jedis.expire(key, seconds);
}
} catch (Exception e) {
log.error("incr {} ", key, e);
return null;
} finally {
jedis.close();
}
return incrBy;
}
public Long decrBy(String key, Long integer) {
Jedis jedis = null;
Long decrBy;
try {
if (StringUtils.isBlank(key)) {
decrBy = null;
return decrBy;
}
if (integer == null) {
integer = 0L;
}
jedis = this.getResource();
decrBy = jedis.decrBy(key, integer);
} catch (Exception e) {
log.error("incr {} ", key, e);
return null;
} finally {
jedis.close();
}
return decrBy;
}
public Long decr(String key) {
Jedis jedis = null;
Long decr;
try {
if (StringUtils.isBlank(key)) {
decr = null;
return decr;
}
jedis = this.getResource();
decr = jedis.decr(key);
return decr;
} catch (Exception e) {
log.error("decr {} ", key, e);
return null;
} finally {
jedis.close();
}
}
public Long ttl(String key) {
Long result = 0L;
Jedis jedis = null;
try {
jedis = this.getResource();
result = jedis.ttl(key);
} catch (Exception e) {
log.error("ttl {}", key, e);
} finally {
jedis.close();
}
return result;
}
public Map hgetAll(String key) {
Map map = new HashMap();
Jedis jedis = null;
try {
if (StringUtils.isBlank(key)) {
return map;
}
jedis = this.getResource();
map = jedis.hgetAll(key);
return map;
} catch (Exception e) {
log.error("hgetAll {} ", key, e);
return null;
} finally {
jedis.close();
}
}
public String hmset(String key, Map map) {
Jedis jedis = null;
try {
if (StringUtils.isBlank(key)) {
return null;
}
jedis = this.getResource();
return jedis.hmset(key, map);
} catch (Exception e) {
log.error("hmset {} ", key, e);
return null;
} finally {
jedis.close();
}
}
public Long hdel(String key, String... fields) {
Jedis jedis = null;
try {
jedis = this.getResource();
return jedis.hdel(key, fields);
} catch (Exception e) {
log.error("hdel {} ", key, e);
return null;
} finally {
jedis.close();
}
}
public Long llen(String key) {
Jedis jedis = null;
try {
jedis = this.getResource();
return jedis.llen(key);
} catch (Exception e) {
log.error("llen {} ", key, e);
return null;
} finally {
jedis.close();
}
}
public Long lpushx(String key, String value) {
return lpush(key, value);
}
public Long lpush(String key, String... fields) {
Jedis jedis = null;
try {
jedis = this.getResource();
return jedis.lpush(key, fields);
} catch (Exception e) {
log.error("lpush {} ", key, e);
return null;
} finally {
jedis.close();
}
}
public String rpop(String key) {
Jedis jedis = null;
try {
jedis = this.getResource();
return jedis.rpop(key);
} catch (Exception e) {
log.error("rpop {} ", key, e);
return null;
} finally {
jedis.close();
}
}
public Long zadd(String key, double score, String member) {
Jedis jedis = null;
try {
jedis = this.getResource();
return jedis.zadd(key, score, member);
} catch (Exception e) {
log.error("zadd {} ", key, e);
return null;
} finally {
jedis.close();
}
}
public Long sadd(String key, String... member) {
Jedis jedis = null;
try {
jedis = this.getResource();
return jedis.sadd(key, member);
} catch (Exception e) {
log.error("sadd {} ", key, e);
return null;
} finally {
jedis.close();
}
}
public Set smembers(String key) {
Jedis jedis = null;
try {
jedis = this.getResource();
return jedis.smembers(key);
} catch (Exception e) {
log.error("smembers {} ", key, e);
return null;
} finally {
jedis.close();
}
}
public boolean getLock(String lockKey) {
return this.getLock(lockKey, "1", 5);
}
public boolean getLock(String lockKey, String requestId, int expireTime) {
Jedis jedis = null;
try {
jedis = this.getResource();
String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);
if ("OK".equals(result)) {
return true;
}
return false;
} catch (Exception e) {
log.error("getLock {} ", lockKey, e);
return false;
} finally {
jedis.close();
}
}
public boolean unLock(String lockKey) {
return this.unLock(lockKey, "1");
}
public boolean unLock(String lockKey, String requestId) {
Jedis jedis = null;
try {
String script = "if redis.call("get",KEYS[1]) == ARGV[1] thenn" +
" return redis.call("del",KEYS[1])n" +
"elsen" +
" return 0n" +
"end";
jedis = this.getResource();
Object result = jedis.eval(script, Collections.singletonList(lockKey),
Collections.singletonList(requestId));
if ("OK".equals(result)) {
return true;
}
return false;
} catch (Exception e) {
log.error("unLock {} ", lockKey, e);
return false;
} finally {
jedis.close();
}
}
}
RedisManager
这里 是拿到业务方配置的redis参数,通过这些参数生成jedisPool,获取到Jedis,供RedisClient操作redis,业务方在项目中主要是注入这个类来使用
package com.gane.glocalcommon.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class RedisManager extends RedisClient{
private RedisProperties redisProperties;
private JedisPool jedisPool;
@Override
protected Jedis getResource() {
if (jedisPool == null) {
init();
}
return jedisPool.getResource();
}
private void init() {
synchronized (this) {
if (jedisPool == null) {
if (redisProperties.getHost().contains(":")) {
String[] hostAndPort = redisProperties.getHost().split(":");
jedisPool = new JedisPool(getJedisPoolConfig(),
hostAndPort[0],
Integer.parseInt(hostAndPort[1]),
redisProperties.getTimeout(),
redisProperties.getPassword(),
redisProperties.getDatabase());
} else {
jedisPool = new JedisPool(getJedisPoolConfig(),
redisProperties.getHost(),
redisProperties.getPort(),
redisProperties.getTimeout(),
redisProperties.getPassword(),
redisProperties.getDatabase());
}
}
}
}
public void setRedisProperties(RedisProperties redisProperties) {
this.redisProperties = redisProperties;
}
}
RedisAutoConfiguration
package com.gane.glocalcommon.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties({RedisProperties.class})
public class RedisAutoConfiguration {
private RedisProperties redisProperties;
@Autowired
public RedisAutoConfiguration(RedisProperties redisProperties) {
this.redisProperties = redisProperties;
}
@Bean
@ConditionalOnMissingBean(name = "redisManager")
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setRedisProperties(redisProperties);
return redisManager;
}
}
spring.factories
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration= com.gane.glocalcommon.redis.RedisAutoConfiguration
这样 glocalcommon-redis 就已经完成了,打个 jar 包,让 glocalcommon-redis-spring-boot-starter 引用
再让 glocalcommon-redis-spring-boot-starter 打成 jar 包,给业务方引用
com.gane.glocalcommon glocalcommon-redis-spring-boot-starter1.0-SNAPSHOT
业务方配置redis参数
启动并测试



