持续更新中:
函数式(Functional)接口 什么是函数式(Functional)接口 只包含一个抽象方法的接口,称为函数式接口。
你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式
抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽 象方法上进行声明)。我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检 查它是否是一个函数式接口。同时 javadoc
也会包含一条声明,说明这个 接口是一个函数式接口。
在java.util.function包下定义了Java 8 的丰富的函数式接口:
package com.ly.frauddataplatform.luoyan;
//TODO: 需要在接口上定义@FunctionalInterface注解,表示这是一个函数式的接口
//TODO: MyFunc<R,T>这里是标明泛型
@FunctionalInterface
public interface MyFunc<R,T> {
/**
* @Author luoyan
* @Description: 函数式编程
* @Date 2024/3/4 20:42
* @param t: 入参,泛型可以传任何类型
* @return R: 返回参数,泛型可以传任何类型
**/
R getValue(T t);
}
这里是自定义的函数式编程的自定义的实现接口方式:
//TODO: 这里自定义一个方法,这个方法可以实现前置通知,后置通知,可以在调用方法的前后做一些公用的操作.
//TODO: 在调用到自己的实现方法中的时候,去设计实现自己所需要的代码逻辑流程.
/**
* @Author luoyan
* @Description: 封装的方法,泛型中<R,T>:这个是声明泛型,R:返回的类型,也可以修改R为String,T为String.那么入参和出现就必须是String的类型.
* @Date 2024/3/4 20:46
* @param myFunc: 这个是函数方法
* @param name: 入参
* @return R
**/
public <R,T> R toUpDate(MyFunc<R, T> myFunc, T name){
System.out.println("前置通知");
//TODO: 调用自己设定订代码逻辑流程.
R value = myFunc.getValue(name);
System.out.println(value);
System.out.println("后置通知");
return myFunc.getValue(name);
}
//TODO: 使用一个方法进行测试.
@Test
public void testDemoOne(){
//TODO: 这里实现自己的代码逻辑.
//TODO: 这里name是T,也就是入参,"1231"是R,也就是出参,可以自己随意定义,因为是泛型.
String s = toUpDate(str -> {
System.out.println(str);
return "1231";
}, true);
System.out.println(s);
}
Predicate:函数接口
Predicate<Integer> predicate = n ->{
if (n > 4){
return true;
}
return false;
};
Predicate<Integer> and = predicate.and(k -> {
if (k < 6) {
return true;
}
return false;
}).or(t -> {
if (t == 10) {
return true;
}
return false;
});
boolean test1 = and.test(5);
System.out.println(test1);
这里使用:Function接口来实现。此接口的特点:有入参,有出参,可以直接很好的使用。还有其他的函数。
比如:只需要入参,不需要出参,可以根据自己的情况而定。
举一个例子:
比如我们要使用前置要做的事情,然后调用方法自己的逻辑,最后在执行一个后置要做的事情,其中前置流程和后置流程都是共有的流程。那么就可以使用函数式的变成去封装方法,然后每个地方都可以直接调用。这样很方便。
如果没有函数式编程,我们可能会想到反射的方式,传入一个类,一个方法,然后通过反射的方式去调用这个类中的某个方法,从而可以达到这种实现,但是太麻烦了。
目前我们需要对于redis进行上锁,然后解锁,中间是走自己的流程,那么我们就可以使用函数式编程:
package com.elong.fraud.rcenginedataconvert.constants;
import com.elong.fraud.rcenginedataconvert.model.dto.TryLockParamData;
import com.ly.tcbase.cacheclient.CacheClientHA;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.util.UUID;
import java.util.function.Function;
/**
* @Author luoyan
* @Description: redis锁
* @Date 2024年01月18日 10:38
* @Version: V1.0
*/
@Component
public class RedissionTemplate {
@Resource
private CacheClientHA cacheClientHa;
/**
* @Author luoyan
* @Description:
* @Date 2024/1/19 15:15
* @param tryLockParamData: 入参值.
* LockKey:key,
* param:请求参数
* @param consumer: Function<T,R>
* T:入参,
* R:返回参数
* @return R
**/
public <R> R tryLock(TryLockParamData tryLockParamData, Function<Object,R> consumer){
//TODO: 前置通知,对于参数进行校验
String lockKey = tryLockParamData.getLockKey();
if (ObjectUtils.isEmpty(lockKey)){
return null;
}
//TODO: 前置通知,进行上锁
String lockVal = UUID.randomUUID().toString();
Boolean lock = cacheClientHa.String().setnx(lockKey, 60L, lockVal);
try {
long millis = System.currentTimeMillis();
while (!lock) {
//等待锁释放
try {
if (System.currentTimeMillis() - millis > 3000) {
throw new RuntimeException("程序异常,未获取锁!");
}
Thread.sleep(100);
} catch (Exception ignore) {
}
lock = cacheClientHa.String().setnx(lockKey, 60L, lockVal);
}
//TODO: 这里去调用自己的代码流程
return consumer.apply(tryLockParamData.getParam());
}finally {
//TODO: 后置通知,去进行解锁。
if (lockVal.equals(cacheClientHa.String().setcas(lockKey, lockVal, lockVal))) {
cacheClientHa.Key().del(lockKey);
}
}
}
}
第一处调用:
private JSONObject saveOrUpdateOriginData(JSONObject newValue, String uniqueId, String sourceName) {
//分布式锁
String lockKey = String.format(RedisCacheConstants.DATA_SOURCE_DETAIL_LOCK_FORMAT, sourceName, uniqueId);
//TODO:new TryLockParamData(lockKey):入参。逗号后面就是函数方法,也就是自己的代码逻辑。
JSONObject newValReturn = redissionTemplate.tryLock(new TryLockParamData(lockKey), consumer -> {
//TODO: 自己的代码逻辑流程,先上锁,然后走这里的代码逻辑流程,最后进行解锁。
return newVal;
});
return newValReturn == null ? new JSONObject() : newValReturn;
}
第二处调用:
//分布式锁
String lockKey = String.format(RedisCacheConstants.FEATURE_LIST_UPDATE_LOCK_FORMAT, uniqueId);
//TODO:new TryLockParamData(lockKey):入参。逗号后面就是函数方法,也就是自己的代码逻辑。
redissionTemplate.tryLock(new TryLockParamData(lockKey), rLock -> {
//TODO: 自己的代码逻辑流程,先上锁,然后走这里的代码逻辑流程,最后进行解锁。
//先执行更新,更新失败执行插入
int update = dcdbFraudMapper.update(origin.getTableName(), fieldList, StrUtil.toUnderlineCase(origin.getUpdateKey()), uniqueId);
if (update == 0) {
boolean insert = dcdbFraudMapper.insert(origin.getTableName(), fieldList);
}
return null;
});
可以看到上面两处的调用使用函数式编程非常方便且代码也很美观,不用其他的方式就能够快速的实现,对于同事的阅读和理解也是非常快速的。