Home

FastJson工具包

package sc.whorl.system.utils.json;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.util.HashMap;
import java.util.Map;

/**
 * FastJson工具包
 */
public class FastJsonUtil {
    /**
     * 将string转化为序列化的json字符串
     */
    public static Object textToJson(String text) {
        Object objectJson = JSON.parse(text);
        return objectJson;
    }

    /**
     * json字符串转化为map
     */
    public static Map stringToCollect(String s) {
        Map m = (Map) JSONObject.parseObject(s);
        return m;
    }

    /**
     * 将map转化为string
     */
    public static String collectToString(Map m) {
        String s = JSONObject.toJSONString(m);
        return s;
    }

    /**
     * 返回解析json后对应的Map
     */
    public static Map<String, Object> jsonToMap(String json) {
        JSONObject jsonObject = (JSONObject) JSON.parse(json);
        Map<String, Object> map = new HashMap<String, Object>(jsonObject);
        return map;
    }


    /**
     * 返回解析json后对应的Object
     */
    public static JSONObject jsonToObject(String json) {
        JSONObject jsonObject = (JSONObject) JSON.parse(json);
        return jsonObject;
    }
}

Read more

Aspect实现样例

package sc.whorl.system.commons.aspect.weblog;


import net.logstash.logback.marker.Markers;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import io.swagger.annotations.ApiOperation;
import sc.whorl.system.utils.mapper.JsonMapper;
import sc.whorl.system.utils.rest.RequestUtil;

/**
 * 统一日志处理切面
 */
@Aspect
@Component
//为了统计执行时间
@Order(1)
public class WebLogAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class);
    private ThreadLocal<Long> startTime = new ThreadLocal<>();

    @Pointcut("execution(public * sc.whorl.web.*.*(..))")
    public void webLog() {
    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());
    }

    @AfterReturning(value = "webLog()", returning = "ret")
    public void doAfterReturning(Object ret) throws Throwable {
    }

    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取当前请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //记录请求信息(通过logstash传入elasticsearch)
        WebLog webLog = new WebLog();
        Object result = joinPoint.proceed();
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method.isAnnotationPresent(ApiOperation.class)) {
            ApiOperation log = method.getAnnotation(ApiOperation.class);
            webLog.setDescription(log.value());
        }
        long endTime = System.currentTimeMillis();
        webLog.setBasePath(RequestUtil.getBasePath(request));
        webLog.setIp(request.getRemoteUser());
        webLog.setMethod(request.getMethod());
        webLog.setParameter(getParameter(method, joinPoint.getArgs()));
        webLog.setResult(result);
        webLog.setSpendTime((int) (endTime - startTime.get()));
        webLog.setStartTime(startTime.get());
        webLog.setUri(request.getRequestURI());
        webLog.setUrl(request.getRequestURL().toString());
        Map<String,Object> logMap = new HashMap<>();
        logMap.put("url",webLog.getUrl());
        logMap.put("method",webLog.getMethod());
        logMap.put("parameter",webLog.getParameter());
        logMap.put("spendTime",webLog.getSpendTime());
        logMap.put("description",webLog.getDescription());
        //        LOGGER.info("{}", JsonUtil.objectToJson(webLog));
        LOGGER.info(Markers.appendEntries(logMap), JsonMapper.nonDefaultMapper().toJson(webLog));
        return result;
    }

    /**
     * 根据方法和传入的参数获取请求参数
     */
    private Object getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
            }
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map<String, Object> map = new HashMap<>();
                String key = parameters[i].getName();
                if (!StringUtils.isEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        if (argList.size() == 0) {
            return null;
        } else if (argList.size() == 1) {
            return argList.get(0);
        } else {
            return argList;
        }
    }
}

Read more

java代码设计

数据库设计

使用tk.mybits 使用example 代替mapper.xml条件查询 使用范型 实现通用sql操作

Services设计

service 在Controller 的调用用 private 修饰

    @Autowired
    private UserService userService;

@Autowired的使用:推荐对构造函数进行注释

建议后写成下面的样子:

private final EnterpriseDbService service;

@Autowired
public EnterpriseDbController(EnterpriseDbService service) {
   this.service = service;
}

切面编程

使用场景 将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来 代码实现 1. 用自定义注解的方式实现

@Around("execution(public * *(..)) && @annotation(sc.whorl.system.commons.limitrate.Limit)")

对返回给前端的数据进行格式封装处理

当代码中已经有 MsgResponseBody 作为Response封装类时,项目中还用到 ResultUtil 作为Response的封装,看了下源码,MsgResponseBody都出现在被@RestController 注释的控制类中,而@RestController 是注释了 @ResponseBody的,而其他非控制类,则乖乖的使用Response挨个处理

Read more

Autowired的使用:推荐对构造函数进行注释

在编写代码的时候,使用@Autowired注解是,发现IDE报的一个警告,如下:

Spring Team recommends “Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies”.

翻译:Spring建议”总是在您的bean中使用构造函数建立依赖注入。总是使用断言强制依赖”。

这段代码警告原来的写法是:

@Autowired
private EnterpriseDbService service;

建议后写成下面的样子:

private final EnterpriseDbService service;

@Autowired
public EnterpriseDbController(EnterpriseDbService service) {
   this.service = service;
}

奇怪,为何会有这样的建议。? 我们知道:@Autowired 可以对成员变量、方法以及构造函数进行注释。那么对成员变量和构造函数进行注释又有什么区别呢?

@Autowired注入bean,相当于在配置文件中配置bean,并且使用setter注入。而对构造函数进行注释,就相当于是使用构造函数进行依赖注入了吧。莫非是这两种注入方法的不同。

以下是:@Autowired和构造方法执行的顺序解析

先看一段代码,下面的代码能运行成功吗?

@Autowired
private User user;
private String school;

public UserAccountServiceImpl(){
    this.school = user.getSchool();
}

答案是不能。

因为Java类会先执行构造方法,然后再给注解了@Autowired 的user注入值,所以在执行构造方法的时候,就会报错。

报错信息可能会像下面:

  Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name '...' defined in file [....class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Constructor threw exception; nested exception is java.lang.NullPointerException

报错信息说:创建Bean时出错,出错原因是实例化bean失败,因为bean时构造方法出错,在构造方法里抛出了空指针异常。

解决办法是,使用构造器注入,如下:

private User user;
private String school;

@Autowired
public UserAccountServiceImpl(User user){
    this.user = user;
    this.school = user.getSchool();
}

可以看出,使用构造器注入的方法,可以明确成员变量的加载顺序。

PS:Java变量的初始化顺序为:静态变量或静态语句块–>实例变量或初始化语句块–>构造方法–>@Autowired

那么最开始Spring建议,为何要将成员变量加上final类型呢?

网上有解释如下:spring配置默认的bean的scope是singleton,也就是启动后一直有。通过设置bean的scope属性为prototype来声明该对象为动态创建。但是,如果你的service本身是singleton,注入只执行一次。

@Autowired本身就是单例模式,只会在程序启动时执行一次,即使不定义final也不会初始化第二次,所以这个final是没有意义的吧。

可能是为了防止,在程序运行的时候,又执行了一遍构造函数;

或者是更容易让人理解的意思,加上final只会在程序启动的时候初始化一次,并且在程序运行的时候不会再改变。

Read more

通过实现ConstraintValidator完成自定义校验注解

首先贴上代码

@Data
public class LoginVO {
    @NotNull
    @IsMobile
    private String mobile;

    @NotNull
    @Length(min = 32)
    private String password;

}
public class IsMobileValidator implements ConstraintValidator<IsMobile,String> {

    private boolean require = false;

    @Override
    public void initialize(IsMobile constraintAnnotation) {
        require = constraintAnnotation.required();
    }

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        if(require){
            return ValidatorUtils.isMobile(s);
        }else {
           if (StringUtils.isEmpty(s)) {
               return true;
           }else {
               return ValidatorUtils.isMobile(s);
           }
        }
    }
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
        validatedBy = {IsMobileValidator.class}
)
public @interface IsMobile {

    boolean required() default true;

    String message() default "手机号格式不正确";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};


}
public class ValidatorUtils {

    private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}");

    public static boolean isMobile(String src) {
        if (StringUtils.isEmpty(src)) {
            return false;
        }
        Matcher m = mobile_pattern.matcher(src);
        return m.matches();
    }
}
1. LoginVO类为一个普通的实体类其中的mobile属性添加了自定义注解
2. IsMobile类为自定义的字段注解未了解过注解的同学请自行百度在此不做基础介绍该自定义注解类中用到了四种元注解最后一个@Constraint指定了校验类也就是接下来的IsMobileValidator类值得一提的是除了自定义的messagerequire属性外下面的groups和payload也是必须添加的
3. ValidatorUtils为一个工具类简单校验了是否为手机号没有了解过的可以看我之前的一篇博文https://blog.csdn.net/qq_38439885/article/details/80230753
4. IsMobileValidator为自定义注解的校验类
校验类需要实现ConstraintValidator接口
接口使用了泛型需要指定两个参数第一个自定义注解类第二个为需要校验的数据类型
实现接口后要override两个方法分别为initialize方法和isValid方法其中initialize为初始化方法可以在里面做一些初始化操作isValid方法就是我们最终需要的校验方法了可以在该方法中实现具体的校验步骤本示例中进行了简单的手机号校验

完成这几部分之后一个简单的自定义校验注解就完成啦不要忘记在使用的时候加上@Valid注解开启valid校验

那么如何获取在注解中定义的message信息呢

在valid校验中如果校验不通过会产生BindException异常捕捉到异常后可以获取到defaultMessage也就是自定义注解中定义的内容具体实现如下
```java
    BindException ex = (BindException)e;
    List<ObjectError> errors = ex.getAllErrors();
    ObjectError error = errors.get(0);
    String msg = error.getDefaultMessage();

Read more

使用Redis实现分布式lock

/**
 * Redis分布式锁
 * 使用 SET resource-name anystring NX EX max-lock-time 实现
 * <p>
 * 该方案在 Redis 官方 SET 命令页有详细介绍。
 * http://doc.redisfans.com/string/set.html
 * <p>
 * 在介绍该分布式锁设计之前,我们先来看一下在从 Redis 2.6.12 开始 SET 提供的新特性,
 * 命令 SET key value [EX seconds] [PX milliseconds] [NX|XX],其中:
 * <p>
 * EX seconds — 以秒为单位设置 key 的过期时间;
 * PX milliseconds — 以毫秒为单位设置 key 的过期时间;
 * NX — 将key 的值设为value ,当且仅当key 不存在,等效于 SETNX。
 * XX — 将key 的值设为value ,当且仅当key 存在,等效于 SETEX。
 * <p>
 * 命令 SET resource-name anystring NX EX max-lock-time 是一种在 Redis 中实现锁的简单方法。
 * <p>
 * 客户端执行以上的命令:
 * <p>
 * 如果服务器返回 OK ,那么这个客户端获得锁。
 * 如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。
 *
 * @author zsy
 * @version 1.0
 * @date 2019年8月22日 22:21:27
 */
public class RedisLock {

    private static Logger logger = LoggerFactory.getLogger(RedisLock.class);

    private RedisTemplate redisTemplate;

    /**
     * 将key 的值设为value ,当且仅当key 不存在,等效于 SETNX。
     */
    public static final String NX = "NX";

    /**
     * seconds — 以秒为单位设置 key 的过期时间,等效于EXPIRE key seconds
     */
    public static final String EX = "EX";

    /**
     * 调用set后的返回值
     */
    public static final String OK = "OK";

    /**
     * 默认请求锁的超时时间(ms 毫秒)
     */
    private static final long TIME_OUT = 100;

    /**
     * 默认锁的有效时间(s)
     */
    public static final int EXPIRE = 60;

    /**
     * 解锁的lua脚本
     */
    public static final String UNLOCK_LUA;

    static {
        StringBuilder sb = new StringBuilder();
        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
        sb.append("then ");
        sb.append("    return redis.call(\"del\",KEYS[1]) ");
        sb.append("else ");
        sb.append("    return 0 ");
        sb.append("end ");
        UNLOCK_LUA = sb.toString();
    }

    /**
     * 锁标志对应的key
     */
    private String lockKey;

    /**
     * 记录到日志的锁标志对应的key
     */
    private String lockKeyLog = "";

    /**
     * 锁对应的值
     */
    private String lockValue;

    /**
     * 锁的有效时间(s)
     */
    private int expireTime = EXPIRE;

    /**
     * 请求锁的超时时间(ms)
     */
    private long timeOut = TIME_OUT;

    /**
     * 锁标记
     */
    private volatile boolean locked = false;

    final Random random = new Random();

    /**
     * 使用默认的锁过期时间和请求锁的超时时间
     *
     * @param redisTemplate
     * @param lockKey       锁的key(Redis的Key)
     */
    public RedisLock(RedisTemplate redisTemplate, String lockKey) {
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey + "_lock";
    }

    /**
     * 使用默认的请求锁的超时时间,指定锁的过期时间
     *
     * @param redisTemplate
     * @param lockKey       锁的key(Redis的Key)
     * @param expireTime    锁的过期时间(单位:秒)
     */
    public RedisLock(RedisTemplate redisTemplate, String lockKey, int expireTime) {
        this(redisTemplate, lockKey);
        this.expireTime = expireTime;
    }

    /**
     * 使用默认的锁的过期时间,指定请求锁的超时时间
     *
     * @param redisTemplate
     * @param lockKey       锁的key(Redis的Key)
     * @param timeOut       请求锁的超时时间(单位:毫秒)
     */
    public RedisLock(RedisTemplate redisTemplate, String lockKey, long timeOut) {
        this(redisTemplate, lockKey);
        this.timeOut = timeOut;
    }

    /**
     * 锁的过期时间和请求锁的超时时间都是用指定的值
     *
     * @param redisTemplate
     * @param lockKey       锁的key(Redis的Key)
     * @param expireTime    锁的过期时间(单位:秒)
     * @param timeOut       请求锁的超时时间(单位:毫秒)
     */
    public RedisLock(RedisTemplate redisTemplate, String lockKey, int expireTime, long timeOut) {
        this(redisTemplate, lockKey, expireTime);
        this.timeOut = timeOut;
    }

    /**
     * 尝试获取锁 超时返回
     *
     * @return
     */
    public boolean tryLock() {
        // 生成随机key
        lockValue = UUID.randomUUID().toString();
        // 请求锁超时时间,纳秒
        long timeout = timeOut * 1000000;
        // 系统当前时间,纳秒
        long nowTime = System.nanoTime();
        while ((System.nanoTime() - nowTime) < timeout) {
            if (OK.equalsIgnoreCase(this.set(lockKey, lockValue, expireTime))) {
                locked = true;
                // 上锁成功结束请求
                return locked;
            }

            // 每次请求等待一段时间
            seleep(10, 50000);
        }
        return locked;
    }

    /**
     * 尝试获取锁 立即返回
     *
     * @return 是否成功获得锁
     */
    public boolean lock() {
        lockValue = UUID.randomUUID().toString();
        //不存在则添加 且设置过期时间(单位ms)
        String result = set(lockKey, lockValue, expireTime);
        locked = OK.equalsIgnoreCase(result);
        return locked;
    }

    /**
     * 以阻塞方式的获取锁
     *
     * @return 是否成功获得锁
     */
    public boolean lockBlock() {
        lockValue = UUID.randomUUID().toString();
        while (true) {
            //不存在则添加 且设置过期时间(单位ms)
            String result = set(lockKey, lockValue, expireTime);
            if (OK.equalsIgnoreCase(result)) {
                locked = true;
                return locked;
            }

            // 每次请求等待一段时间
            seleep(10, 50000);
        }
    }

    /**
     * 解锁
     * <p>
     * 可以通过以下修改,让这个锁实现更健壮:
     * <p>
     * 不使用固定的字符串作为键的值,而是设置一个不可猜测(non-guessable)的长随机字符串,作为口令串(token)。
     * 不使用 DEL 命令来释放锁,而是发送一个 Lua 脚本,这个脚本只在客户端传入的值和键的口令串相匹配时,才对键进行删除。
     * 这两个改动可以防止持有过期锁的客户端误删现有锁的情况出现。
     */
    public Boolean unlock() {
        // 只有加锁成功并且锁还有效才去释放锁
        // 只有加锁成功并且锁还有效才去释放锁
        if (locked) {
            return (Boolean) redisTemplate.execute(new RedisCallback<Boolean>() {
                @Override
                public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                    Object nativeConnection = connection.getNativeConnection();
                    Long result = 0L;

                    List<String> keys = new ArrayList<>();
                    keys.add(lockKey);
                    List<String> values = new ArrayList<>();
                    values.add(lockValue);

                    // 集群模式
                    if (nativeConnection instanceof JedisCluster) {
                        result = (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, values);
                    }

                    // 单机模式
                    if (nativeConnection instanceof Jedis) {
                        result = (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, values);
                    }

                    if (result == 0 && !StringUtils.isEmpty(lockKeyLog)) {
                        logger.info("Redis分布式锁,解锁{}失败!解锁时间:{}", lockKeyLog, System.currentTimeMillis());
                    }

                    locked = result == 0;
                    return result == 1;
                }
            });
        }

        return true;
    }

    /**
     * 重写redisTemplate的set方法
     * <p>
     * 命令 SET resource-name anystring NX EX max-lock-time 是一种在 Redis 中实现锁的简单方法。
     * <p>
     * 客户端执行以上的命令:
     * <p>
     * 如果服务器返回 OK ,那么这个客户端获得锁。
     * 如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。
     *
     * @param key     锁的Key
     * @param value   锁里面的值
     * @param seconds 过去时间(秒)
     * @return
     */
    private String set(final String key, final String value, final long seconds) {
        Assert.isTrue(!StringUtils.isEmpty(key), "key不能为空");
        return (String) redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                Object nativeConnection = connection.getNativeConnection();
                String result = null;
                if (nativeConnection instanceof JedisCommands) {
                    result = ((JedisCommands) nativeConnection).set(key, value, NX, EX, seconds);
                }

                if (!StringUtils.isEmpty(lockKeyLog) && !StringUtils.isEmpty(result)) {
                    logger.info("获取锁{}的时间:{}", lockKeyLog, System.currentTimeMillis());
                }

                return result;
            }
        });
    }

    /**
     * @param millis 毫秒
     * @param nanos  纳秒
     * @Title: seleep
     * @Description: 线程等待时间
     * @author yuhao.wang
     */
    private void seleep(long millis, int nanos) {
        try {
            Thread.sleep(millis, random.nextInt(nanos));
        } catch (InterruptedException e) {
            logger.info("获取分布式锁休眠被中断:", e);
        }
    }

    public String getLockKeyLog() {
        return lockKeyLog;
    }

    public void setLockKeyLog(String lockKeyLog) {
        this.lockKeyLog = lockKeyLog;
    }

    public int getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(int expireTime) {
        this.expireTime = expireTime;
    }

    public long getTimeOut() {
        return timeOut;
    }

    public void setTimeOut(long timeOut) {
        this.timeOut = timeOut;
    }
}


Read more

基于redis的分布式锁

package sc.whorl.system.commons.preventresubmit;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.types.Expiration;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * 基于redis的分布式锁
 * // TODO 基于redis的分布式锁
 */
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisLockHelper {


    private static final String DELIMITER = "|";

    /**
     * 如果要求比较高可以通过注入的方式分配
     */
    private static final ScheduledExecutorService EXECUTOR_SERVICE = new ScheduledThreadPoolExecutor(10,
            new BasicThreadFactory.Builder().namingPattern("sen-unit-schedule-pool-%d").daemon(true).build());


    private final StringRedisTemplate stringRedisTemplate;

    public RedisLockHelper(StringRedisTemplate stringRedisTemplate) {

        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 获取锁(存在死锁风险)
     *
     * @param lockKey
     *         lockKey
     * @param value
     *         value
     * @param time
     *         超时时间
     * @param unit
     *         过期单位
     * @return true or false
     */
    public boolean tryLock(final String lockKey, final String value, final long time, final TimeUnit unit) {
        return stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> connection.set(lockKey.getBytes(), value.getBytes(), Expiration.from(time, unit), RedisStringCommands.SetOption.SET_IF_ABSENT));
    }

    /**
     * 删除对应的key参数
     *
     * @param lockKey
     */
    public void unlock(final String lockKey) {
        stringRedisTemplate.delete(lockKey);
    }


    /**
     * *****************************************************************************************************
     * 华丽的分割线
     * *****************************************************************************************************
     */














    /**
     * 获取锁
     *
     * @param lockKey
     *         lockKey
     * @param uuid
     *         UUID
     * @param timeout
     *         超时时间
     * @param unit
     *         过期单位
     * @return true or false
     */
    public boolean lock(String lockKey, final String uuid, long timeout, final TimeUnit unit) {
        final long milliseconds = Expiration.from(timeout, unit).getExpirationTimeInMilliseconds();
        boolean success = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, (System.currentTimeMillis() + milliseconds) + DELIMITER + uuid);
        if (success) {
            stringRedisTemplate.expire(lockKey, timeout, TimeUnit.SECONDS);
        } else {
            String oldVal = stringRedisTemplate.opsForValue().getAndSet(lockKey, (System.currentTimeMillis() + milliseconds) + DELIMITER + uuid);
            final String[] oldValues = oldVal.split(Pattern.quote(DELIMITER));
            if (Long.parseLong(oldValues[0]) + 1 <= System.currentTimeMillis()) {
                return true;
            }
        }
        return success;
    }


    /**
     * 立即释放锁
     * @see <a href="http://redis.io/commands/set">Redis Documentation: SET</a>
     */
    public void unlock(String lockKey, String value) {
        unlock(lockKey, value, 0, TimeUnit.MILLISECONDS);
    }

    /**
     * 延迟unlock
     *
     * @param lockKey
     *         key
     * @param uuid
     *         client(最好是唯一键的)
     * @param delayTime
     *         延迟时间
     * @param unit
     *         时间单位
     */
    public void unlock(final String lockKey, final String uuid, long delayTime, TimeUnit unit) {
        if (StringUtils.isEmpty(lockKey)) {
            return;
        }
        if (delayTime <= 0) {
            doUnlock(lockKey, uuid);
        } else {
            EXECUTOR_SERVICE.schedule(() -> {
                doUnlock(lockKey, uuid);
            }, delayTime, unit);
        }
    }

    /**
     * @param lockKey
     *         key
     * @param uuid
     *         client(最好是唯一键的)
     */
    private void doUnlock(final String lockKey, final String uuid) {
        String val = stringRedisTemplate.opsForValue().get(lockKey);
        final String[] values = val.split(Pattern.quote(DELIMITER));
        if (values.length <= 0) {
            return;
        }
        if (uuid.equals(values[1])) {
            stringRedisTemplate.delete(lockKey);
        }
    }


}

可参考文档

Read more

Dozer BeanMapper 对象转换

dozer是什么?

在开发过程中,常常会涉及到对象之间的转换,bean到dto到vo等等,dozer便是很好的一个对象转换的组件。它可以将一个对象递归拷贝到另外一个对象。既支持简单的对象映射,也支持复杂的对象映射。

对象映射

dozer支持两种映射方式,调用api的简单映射方式和使用xml的映射方式。api调用的方式约束我们定义的 两个bean中的成员变量名要一样,方便自动映射,至于类型可以不一样,比如类A的成员变量 Integer num 和类B的 String num 是可以直接映射的。xml的方式是通过编写xml文件来定义映射规则。

dozer使用

使用dozer时,只需要加上dozer的依赖即可,下面以maven为例(版本号可以根据最新的做修改):

<dependency>
    <groupId>com.github.dozermapper</groupId>
    <artifactId>dozer-core</artifactId>
    <version>6.2.0</version>
</dependency>

实例

/**
 * @Title: EntityObjectConverter.java
 * @Package com.joyce.util
 * @Copyright: Copyright (c) 2013
 *
 * @author Comsys-LZP
 * @date 2013-11-4 上午09:55:14
 * @version V2.0
 */
package com.joyce.util;

import java.util.ArrayList;
import java.util.List;

import net.sf.dozer.util.mapping.DozerBeanMapper;

/**
 * @Description: 两个对象间,相同属性名之间进行转换
 *
 * @ClassName: EntityObjectConverter
 * @Copyright: Copyright (c) 2013
 *
 * @author Comsys-LZP
 * @date 2013-11-4 上午09:55:14
 * @version V2.0
 */
public class EntityObjectConverter {
	/*
	 * 实例化对象
	 */
	private static DozerBeanMapper map = new DozerBeanMapper();

	/**
	 * @Description: 将目标对象转换为指定对象,相同属性名进行属性值复制
	 *
	 * @Title: EntityObjectConverter.java
	 * @Copyright: Copyright (c) 2013
	 *
	 * @author Comsys-LZP
	 * @date 2013-11-4 下午02:32:34
	 * @version V2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getObject(Object source,Class<T> cls){
		if (source==null) {
			return null;
		}
		return (T) map.map(source, cls);
	}

	/**
	 * @Description: 两个对象之间相同属性名的属性值复制
	 *
	 * @Title: EntityObjectConverter.java
	 * @Copyright: Copyright (c) 2013
	 *
	 * @author Comsys-LZP
	 * @date 2013-11-4 下午02:33:56
	 * @version V2.0
	 */
	public static void setObject(Object source,Object target){
		map.map(source, target);
	}

	/**
	 * @Description: 对象集合中对象相同属性名的属性值复制
	 *
	 * @Title: EntityObjectConverter.java
	 *
	 * @Copyright: Copyright (c) 2013
	 * @author Comsys-LZP
	 * @date 2013-11-4 下午02:34:26
	 * @version V2.0
	 */
	@SuppressWarnings("unchecked")
	public static List getList(List source,Class cls){
		List listTarget = new ArrayList();
		if(source != null){
			for (Object object : source) {
				Object objTarget = EntityObjectConverter.getObject(object, cls);
				listTarget.add(objTarget);
			}
		}
		return listTarget;
	}
}

参考链接

Read more