赞
踩
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
一般函数式接口可以在类上加一个
@FunctionalInterface注解以标识其为函数式接口
优点:
缺点:
下面举一个比较典型的例子,码云上的GVP开源权限认证框架Sa-token中有一段代码:
/** * 封装 注销、踢人、顶人 三个动作的相同代码(无API含义方法) * @param loginId 账号id * @param device 设备标识 * @param appendFun 追加操作 * @param isLogoutSession 是否注销 User-Session */ protected void clearTokenCommonMethod(Object loginId, String device, Consumer<String> appendFun, boolean isLogoutSession) { // 1. 如果此账号尚未登录,则不执行任何操作 SaSession session = getSessionByLoginId(loginId, false); if(session == null) { return; } // 2. 循环token签名列表,开始删除相关信息 for (TokenSign tokenSign : session.getTokenSignList()) { if(device == null || tokenSign.getDevice().equals(device)) { // -------- 共有操作 // s1. 获取token String tokenValue = tokenSign.getValue(); // s2. 清理掉[token-last-activity] clearLastActivity(tokenValue); // s3. 从token签名列表移除 session.removeTokenSign(tokenValue); // -------- 追加操作 appendFun.accept(tokenValue); } } // 3. 尝试注销session if(isLogoutSession) { session.logoutByTokenSignCountToZero(); } }
clearTokenCommonMethod这个方法是为了在执行某些操作后,从系统中将token删除掉,并清空用户信息,注销session,因为在注销、踢人、顶人这几个地方都有类似的逻辑,但是相似的逻辑中有又一部分不太一样,这种情况下,直接抽成一个方法是不能直接通用的,那么这个时候一般有两个方案
doAfterClearToken方法。在Shiro和Spring Security的代码中,经常可以看到有这种方式,即在本类中写一个抽象方法doAfterClearToken,在删除token后调用这个方法,这个方法在这个类中不做任何事情,交由子类来实现。当然,此时这个类必须是抽象类,然后再写注销类、踢人类、顶人类来继承这个类,并重写doAfterClearToken方法来实现各自的逻辑,大概就是下面这样public abstract class StpLogic{ protected void clearTokenCommonMethod(Object loginId, String device, boolean isLogoutSession) { // 1. 如果此账号尚未登录,则不执行任何操作 SaSession session = getSessionByLoginId(loginId, false); if(session == null) { return; } // 2. 循环token签名列表,开始删除相关信息 for (TokenSign tokenSign : session.getTokenSignList()) { if(device == null || tokenSign.getDevice().equals(device)) { // -------- 共有操作 // s1. 获取token String tokenValue = tokenSign.getValue(); // s2. 清理掉[token-last-activity] clearLastActivity(tokenValue); // s3. 从token签名列表移除 session.removeTokenSign(tokenValue); // -------- 追加操作 doAfterClearToken(tokenValue); } } // 3. 尝试注销session if(isLogoutSession) { session.logoutByTokenSignCountToZero(); } } public abstract void doAfterClearToken(String tokenValue); }
public class kickout extends StpLogic{
@Override
public void doAfterClearToken(String tokenValue){
//*******这里写具体的踢人的逻辑
}
}
public class LogOut extends StpLogic{
@Override
public void doAfterClearToken(String tokenValue){
//*******这里写具体的注销的逻辑
}
}
public class replaced extends StpLogic{
@Override
public void doAfterClearToken(String tokenValue){
//*******这里写具体的顶人的逻辑
}
}
Consumer接口,然后在清除token后执行accept方法,这样就完成了不同的后续操作,然后在调用这个方法时编写具体的执行逻辑代码,这样调用的好处是你不用单独为了一小段代码再撰写一个类(当然函数式接口使用过的是匿名内部类,本质上也是创建了类的,只不过不用撰写类放在项目目录下),这样可以提高开发效率,当然可读性比直接撰写了差一些,代码如下 /**
* 顶人下线,根据账号id 和 设备标识
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-4 </p>
*
* @param loginId 账号id
* @param device 设备标识 (填null代表顶替所有设备)
*/
public void replaced(Object loginId, String device) {
clearTokenCommonMethod(loginId, device, tokenValue -> {
// 将此 token 标记为已被顶替
updateTokenToIdMapping(tokenValue, NotLoginException.BE_REPLACED);
SaManager.getSaTokenListener().doReplaced(loginType, loginId, tokenValue);
}, false);
}
/**
* 踢人下线,根据账号id 和 设备标识
* <p> 当对方再次访问系统时,会抛出NotLoginException异常,场景值=-5 </p>
*
* @param loginId 账号id
* @param device 设备标识 (填null代表踢出所有设备)
*/
public void kickout(Object loginId, String device) {
clearTokenCommonMethod(loginId, device, tokenValue -> {
// 将此 token 标记为已被踢下线
updateTokenToIdMapping(tokenValue, NotLoginException.KICK_OUT);
SaManager.getSaTokenListener().doKickout(loginType, loginId, tokenValue);
}, true);
}
/**
* 会话注销,根据账号id 和 设备标识
*
* @param loginId 账号id
* @param device 设备标识 (填null代表所有注销设备)
*/
public void logout(Object loginId, String device) {
clearTokenCommonMethod(loginId, device, tokenValue -> {
// 删除Token-Id映射 & 清除Token-Session
deleteTokenToIdMapping(tokenValue);
deleteTokenSession(tokenValue);
SaManager.getSaTokenListener().doLogout(loginType, loginId, tokenValue);
}, true);
}
函数式接口Consumer介绍,这个函数式接口是为了消费数据而定义的,其代码如下
package java.util.function; import java.util.Objects; /** * Represents an operation that accepts a single input argument and returns no * result. Unlike most other functional interfaces, {@code Consumer} is expected * to operate via side-effects. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #accept(Object)}. * * @param <T> the type of the input to the operation * * @since 1.8 */ @FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); /** * Returns a composed {@code Consumer} that performs, in sequence, this * operation followed by the {@code after} operation. If performing either * operation throws an exception, it is relayed to the caller of the * composed operation. If performing this operation throws an exception, * the {@code after} operation will not be performed. * * @param after the operation to perform after this operation * @return a composed {@code Consumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */ default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。