全面解读ConcurrentSkipListMap:高并发环境下的有序映射实现

在分布式系统和多线程应用日益普及的今天,高效处理并发数据成为开发者必须面对的挑战。ConcurrentSkipListMap作为Java并发容器中的重要成员,以其独特的设计解决了高并发下有序数据管理的难题。

一、ConcurrentSkipListMap概述

ConcurrentSkipListMap是Java集合框架中一个线程安全的有序映射实现,位于java.util.concurrent包下。它最早在Java 6中引入,基于跳表(Skip List) 数据结构实现,提供了平均时间复杂度为O(log n)的大多数操作。

与传统的平衡树结构不同,ConcurrentSkipListMap采用了一种概率性的平衡方法,这使得它在高并发环境下表现出色。最新Java版本(如Java 17、21)中,ConcurrentSkipListMap继续优化,性能得到了进一步提升。

二、跳表数据结构解析

跳表是ConcurrentSkipListMap的核心所在,它是一种可以替代平衡树的数据结构。跳表通过维护一个多层次的有序链表来实现快速查找:

    • 基础层:包含所有元素的完整有序链表

    • 索引层:由稀疏元素组成的快速访问层,层级越高,元素越稀疏

    • 空间换时间:通过额外的索引节点牺牲空间复杂度,换取查询性能的提升

java

// 跳表节点结构示意

static class Index<K,V> {

final Node<K,V> node; // 基础节点

final Index<K,V> down; // 下层索引

volatile Index<K,V> right; // 右侧索引

}

这种多层结构使得跳表在查询时可以从最高层开始,大幅跳过不必要的比较,实现了类似二分查找的效率。

三、并发控制机制

ConcurrentSkipListMap采用了无锁编程(Lock-Free)CAS(Compare-And-Swap) 操作来实现线程安全,这比使用传统锁机制具有更好的可伸缩性。

3.1 CAS操作的应用

在插入和删除操作中,ConcurrentSkipListMap大量使用CAS来保证原子性:

java

// CAS操作示例

boolean casNext(Node<K,V> cmp, Node<K,V> val) {

return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);

}

3.2 乐观锁策略

ConcurrentSkipListMap采用乐观锁策略,先假设不会发生冲突进行操作,如果检测到冲突再重试。这种策略在低竞争环境下性能优异。

四、核心源码深度解析

4.1 节点结构设计

```java

static final class Node {

final K key;

volatile Object value;

volatile Node next;

// 构造函数和其他方法...

}

```

节点设计考虑了并发访问的特点,使用volatile关键字保证内存可见性,同时避免使用重量级锁。

4.2 插入操作实现

put方法的实现体现了ConcurrentSkipListMap的并发智慧:

    • 定位插入位置:从最高层索引开始查找

    • 维护索引结构:使用随机算法决定新节点的层级

    • CAS链式更新:通过CAS操作原子性地更新链接

java

public V put(K key, V value) {

// 参数检查

// 查找插入位置

// 随机生成层级

// 链式更新节点和索引

}

4.3 查询操作优化

get操作充分利用跳表的层次结构:

```java

public V get(Object key) {

return doGet(key);

}

private V doGet(Object key) {

// 从最高层开始搜索

// 利用索引快速定位

// 精确匹配返回结果

}

```

五、性能分析与对比

5.1 与ConcurrentHashMap对比

| 特性 | ConcurrentSkipListMap | ConcurrentHashMap |

|------|----------------------|------------------|

| 有序性 | 支持 | 不支持 |

| 时间复杂度 | O(log n) | 平均O(1) |

| 内存占用 | 较高 | 较低 |

| 范围查询 | 支持 | 不支持 |

5.2 与TreeMap对比

TreeMap使用红黑树实现,在单线程环境下性能优秀,但在高并发场景下需要外部同步,性能较差。ConcurrentSkipListMap天生线程安全,在多线程环境下表现卓越。

六、实战应用场景

6.1 高性能缓存

需要保持数据有序的缓存场景,如时间序列数据缓存:

java

ConcurrentSkipListMap<Long, CacheItem> timeSeriesCache =

new ConcurrentSkipListMap<>();

6.2 实时排行榜

游戏积分榜、股票价格排行等需要频繁更新和范围查询的场景:

java

ConcurrentSkipListMap<Integer, Player> leaderboard =

new ConcurrentSkipListMap<>(Comparator.reverseOrder());

6.3 分布式系统协调

在分布式系统中维护有序的元数据或任务队列。

七、最佳实践与注意事项

    • 合理选择比较器:自定义比较器应保证一致性,否则可能导致不可预期的行为

    • 注意内存使用:跳表结构相比HashMap有更高的内存开销,在内存敏感场景需谨慎使用

    • 利用范围查询:善用subMap、headMap、tailMap等方法提高查询效率

    • 迭代器弱一致性:ConcurrentSkipListMap的迭代器是弱一致的,反映创建时刻或之后的状态

八、最新版本优化

在Java 17及更高版本中,ConcurrentSkipListMap继续受益于JVM的优化:

    • 模式匹配优化:instanceof模式匹配提高类型检查效率

    • 垃圾回收改进:ZGC和Shenandoah GC减少停顿时间

    • 向量API:潜在的数据处理性能提升

总结

ConcurrentSkipListMap是Java并发编程中一颗璀璨的明珠,它通过精巧的跳表设计和先进的无锁编程技术,在高并发有序数据管理方面表现出色。虽然它在某些场景下内存开销较大,但其出色的并发性能和有序性特性使其在特定应用场景中不可替代。

随着Java语言的不断发展,ConcurrentSkipListMap也在持续优化,为构建高性能、高并发的分布式系统提供了坚实的技术基础。深入理解其实现原理和适用场景,有助于我们在实际项目中做出更合理的技术选型。

Dubbo源码实战解析:从0到1探究RPC通信协议与服务治理核心设计

本文将深入剖析Apache Dubbo框架的核心架构,通过源码解析和实战代码演示,带你全面理解Dubbo的RPC通信机制与服务治理实现原理。

1. Dubbo框架概述与架构设计

Apache Dubbo作为一款高性能、轻量级的开源Java RPC框架,提供了三大核心能力:面向接口的远程方法调用智能容错和负载均衡,以及服务自动注册和发现

1.1 Dubbo整体架构

让我们先通过一个代码示例直观理解Dubbo的基本使用:

```java

// 服务接口定义

public interface UserService {

User getUserById(Long id);

}

// 服务提供方实现

@Service

public class UserServiceImpl implements UserService {

@Override

public User getUserById(Long id) {

return new User(id, "user_" + id);

}

}

// 服务消费方调用

@RestController

public class UserController {

@Reference

private UserService userService;

@GetMapping("/user/{id}")

public User getUser(@PathVariable Long id) {

return userService.getUserById(id);

}

}

```

Dubbo架构的核心组件包括:

    • Provider: 服务提供方,暴露服务并处理消费方调用

    • Consumer: 服务消费方,通过代理调用远程服务

    • Registry: 注册中心,负责服务注册与发现

    • Monitor: 监控中心,统计服务调用次数和耗时

    • Container: 服务运行容器,负责启动、加载、运行服务提供者

2. RPC通信协议深度解析

2.1 Dubbo协议设计原理

Dubbo协议采用单一长连接和NIO异步通信,适合小数据量的大并发服务调用。让我们通过源码分析协议头结构:

```java

// Dubbo协议头定义(参考Dubbo 3.x源码)

public class Header {

// 魔术位,固定为0xdabb

private short magic = (short) 0xdabb;

// 请求/响应标志位

private byte flag;

// 状态位

private byte status;

// 消息ID

private long id;

// 数据长度

private int dataLength;

// 序列化方法掩码

private static final byte SERIALIZATION_MASK = 0x1f;

public boolean isRequest() {

return (flag & FLAG_REQUEST) != 0;

}

public byte getSerializationId() {

return (byte) (flag & SERIALIZATION_MASK);

}

}

```

2.2 网络通信层实现

Dubbo使用Netty作为默认的网络通信组件。以下是简化版的通信处理器实现:

```java

public class NettyServerHandler extends ChannelInboundHandlerAdapter {

private final Map<String, Exporter<?>> exporterMap;

@Override

public void channelRead(ChannelHandlerContext ctx, Object msg) {

if (msg instanceof Request) {

Request request = (Request) msg;

// 处理请求并返回响应

Response response = handleRequest(request);

ctx.writeAndFlush(response);

}

}

private Response handleRequest(Request request) {

String serviceKey = buildServiceKey(request);

Exporter<?> exporter = exporterMap.get(serviceKey);

if (exporter == null) {

return buildErrorResponse("Service not found: " + serviceKey);

}

try {

// 通过反射调用实际服务方法

Object result = exporter.invoke(request.getData());

return buildSuccessResponse(result);

} catch (Exception e) {

return buildErrorResponse("Service invocation error: " + e.getMessage());

}

}

}

```

2.3 序列化机制详解

Dubbo支持多种序列化协议,包括Hessian2、JSON、Kryo等。以下是自定义序列化器的实现示例:

```java

public class DubboSerialization implements Serialization {

@Override

public ObjectOutput serialize(OutputStream output) throws IOException {

// 根据配置选择具体的序列化实现

String serializationName = getSerializationName();

switch (serializationName) {

case "hessian2":

return new Hessian2ObjectOutput(output);

case "json":

return new JsonObjectOutput(output);

case "kryo":

return new KryoObjectOutput(output);

default:

return new Hessian2ObjectOutput(output);

}

}

@Override

public ObjectInput deserialize(InputStream input) throws IOException {

// 反序列化实现

String serializationName = getSerializationName();

switch (serializationName) {

case "hessian2":

return new Hessian2ObjectInput(input);

case "json":

return new JsonObjectInput(input);

case "kryo":

return new KryoObjectInput(input);

default:

return new Hessian2ObjectInput(input);

}

}

}

```

3. 服务治理核心组件源码实现

3.1 服务注册与发现机制

Dubbo 3.x引入了应用级服务发现,大幅提升了大规模微服务场景下的性能。以下是服务注册的关键代码:

```java

public class ServiceDiscoveryRegistry implements ServiceRegistry {

@Override

public void register(ServiceInstance serviceInstance) {

// 构建服务实例元数据

ServiceInstanceMetadata metadata = buildMetadata(serviceInstance);

// 注册到注册中心

try {

namingService.registerInstance(

serviceInstance.getServiceName(),

buildInstance(serviceInstance, metadata)

);

logger.info("Service registered successfully: {}", serviceInstance.getServiceName());

} catch (Exception e) {

logger.error("Failed to register service: " + serviceInstance.getServiceName(), e);

throw new RuntimeException("Service registration failed", e);

}

}

@Override

public void subscribe(String serviceName, NotifyListener listener) {

// 订阅服务变化

try {

namingService.subscribe(serviceName, event -> {

List<ServiceInstance> instances = convertToInstances(event.getInstances());

listener.notify(instances);

});

} catch (Exception e) {

logger.error("Failed to subscribe service: " + serviceName, e);

}

}

private ServiceInstanceMetadata buildMetadata(ServiceInstance instance) {

Map<String, String> metadata = new HashMap<>();

metadata.put("version", instance.getVersion());

metadata.put("group", instance.getGroup());

metadata.put("timestamp", String.valueOf(System.currentTimeMillis()));

// 添加自定义元数据

metadata.putAll(instance.getMetadata());

return new ServiceInstanceMetadata(metadata);

}

}

```

3.2 集群容错与负载均衡

Dubbo提供了丰富的集群容错策略和负载均衡算法。以下是自定义负载均衡器的实现:

```java

public class WeightedRoundRobinLoadBalance extends AbstractLoadBalance {

// 权重缓存

private final ConcurrentMap<String, WeightedRoundRobin> weightMap =

new ConcurrentHashMap<>();

@Override

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {

String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();

// 计算总权重并检测权重变化

int totalWeight = 0;

long maxCurrent = Long.MIN_VALUE;

long now = System.currentTimeMillis();

Invoker<T> selectedInvoker = null;

WeightedRoundRobin selectedWRR = null;

for (Invoker<T> invoker : invokers) {

int weight = getWeight(invoker, invocation);

String identifyString = invoker.getUrl().toIdentityString();

WeightedRoundRobin weightedRoundRobin = weightMap.get(identifyString);

if (weightedRoundRobin == null) {

weightedRoundRobin = new WeightedRoundRobin();

weightedRoundRobin.setWeight(weight);

weightMap.putIfAbsent(identifyString, weightedRoundRobin);

}

if (weight != weightedRoundRobin.getWeight()) {

// 权重发生变化

weightedRoundRobin.setWeight(weight);

}

long cur = weightedRoundRobin.increaseCurrent();

weightedRoundRobin.setLastUpdate(now);

if (cur > maxCurrent) {

maxCurrent = cur;

selectedInvoker = invoker;

selectedWRR = weightedRoundRobin;

}

totalWeight += weight;

}

// 清理过期invoker

if (invokers.size() != weightMap.size()) {

weightMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);

}

if (selectedInvoker != null) {

selectedWRR.sel(totalWeight);

return selectedInvoker;

}

return invokers.get(0);

}

}

```

3.3 动态配置中心集成

Dubbo支持多种配置中心,如Nacos、Apollo、ZooKeeper等。以下是配置监*器的实现:

```java

public class DynamicConfigurationListener {

private final ConcurrentMap<String, Set<ConfigurationListener>> listeners = 

new ConcurrentHashMap<>();

public void addListener(String key, ConfigurationListener listener) {

listeners.computeIfAbsent(key, k -> ConcurrentHashMap.newKeySet())

.add(listener);

// 初始配置获取

String config = configuration.getConfig(key);

if (config != null) {

listener.process(new ConfigChangeEvent(key, config));

}

}

public void removeListener(String key, ConfigurationListener listener) {

Set<ConfigurationListener> listenerSet = listeners.get(key);

if (listenerSet != null) {

listenerSet.remove(listener);

}

}

// 配置变化回调

private void onConfigChange(ConfigChangeEvent event) {

String key = event.getKey();

Set<ConfigurationListener> listenerSet = listeners.get(key);

if (listenerSet != null) {

for (ConfigurationListener listener : listenerSet) {

try {

listener.process(event);

} catch (Exception e) {

logger.error("Error processing config change for key: " + key, e);

}

}

}

}

}

```

4. 高级特性与扩展机制

4.1 过滤器链设计与实现

Dubbo的过滤器链采用责任链模式,支持自定义扩展。以下是过滤器链的执行逻辑:

```java

public class FilterChainBuilder {

public static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, 

String key,

String group) {

Invoker<T> last = invoker;

// 获取所有激活的过滤器

List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class)

.getActivateExtension(invoker.getUrl(), key, group);

// 构建过滤器链

if (!filters.isEmpty()) {

for (int i = filters.size() - 1; i >= 0; i--) {

final Filter filter = filters.get(i);

final Invoker<T> next = last;

last = new Invoker<T>() {

@Override

public Result invoke(Invocation invocation) throws RpcException {

return filter.invoke(next, invocation);

}

@Override

public URL getUrl() {

return invoker.getUrl();

}

};

}

}

return last;

}

}

// 自定义过滤器示例

@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})

public class LogFilter implements Filter {

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

@Override

public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

long startTime = System.currentTimeMillis();

try {

// 前置处理

logger.info("Before invocation: {}", invocation.getMethodName());

// 执行调用

Result result = invoker.invoke(invocation);

// 后置处理

long elapsed = System.currentTimeMillis() - startTime;

logger.info("After invocation: {}, elapsed: {}ms",

invocation.getMethodName(), elapsed);

return result;

} catch (RpcException e) {

logger.error("RPC invocation failed: {}", invocation.getMethodName(), e);

throw e;

}

}

}

```

4.2 异步调用与泛化调用

Dubbo支持多种调用方式,满足不同业务场景需求:

```java

// 异步调用示例

public class AsyncInvocationDemo {

public void asyncInvoke() {

// 方式1:使用CompletableFuture

UserService userService = getRemoteService();

CompletableFuture<User> future = userService.getUserAsync(1L);

future.whenComplete((user, throwable) -> {

if (throwable != null) {

System.err.println("Async call failed: " + throwable.getMessage());

} else {

System.out.println("Async call result: " + user);

}

});

// 方式2:使用RpcContext

userService.getUserById(1L);

Future<User> rpcFuture = RpcContext.getContext().getFuture();

// 处理future结果

}

}

// 泛化调用示例(无需依赖服务接口)

public class GenericInvocationDemo {

public void genericCall() {

// 引用泛化服务

ReferenceConfig<GenericService> reference = new ReferenceConfig<>();

reference.setInterface("com.example.UserService");

reference.setVersion("1.0.0");

reference.setGeneric(true);

GenericService genericService = reference.get();

// 泛化调用

Object result = genericService.$invoke("getUserById",

new String[]{"java.lang.Long"},

new Object[]{1L});

System.out.println("Generic call result: " + result);

}

}

```

5. 实战:自定义Dubbo扩展开发

5.1 自定义协议扩展

基于Dubbo的SPI机制,我们可以轻松扩展自定义协议:

```java

// 在META-INF/dubbo/org.apache.dubbo.rpc.Protocol文件中添加:

// myprotocol=com.example.MyProtocol

public class MyProtocol implements Protocol {

private final ExporterMap exporterMap = new ExporterMap();

@Override

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {

// 创建导出器

MyExporter<T> exporter = new MyExporter<>(invoker);

exporterMap.addExporter(exporter);

// 启动服务端

openServer(invoker.getUrl());

return exporter;

}

@Override

public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {

// 创建调用代理

return new MyInvoker<>(type, url);

}

private void openServer(URL url) {

// 实现自定义的服务端启动逻辑

String key = url.getAddress();

if (!serverMap.containsKey(key)) {

MyServer server = new MyServer(url);

serverMap.put(key, server);

}

}

}

```

5.2 自定义路由规则

实现基于业务逻辑的自定义路由:

```java

public class GrayReleaseRouter implements Router {

private final URL url;

public GrayReleaseRouter(URL url) {

this.url = url;

}

@Override

public <T> List<Invoker<T>> route(List<Invoker<T>> invokers,

URL url,

Invocation invocation) {

// 获取灰度发布配置

String grayConfig = getGrayConfig(invocation.getMethodName());

if (StringUtils.isNotEmpty(grayConfig)) {

return invokers.stream()

.filter(invoker -> matchGrayRule(invoker, grayConfig))

.collect(Collectors.toList());

}

return invokers;

}

private <T> boolean matchGrayRule(Invoker<T> invoker, String grayConfig) {

// 实现灰度匹配逻辑

String version = invoker.getUrl().getParameter("version");

String grayVersions = parseGrayVersions(grayConfig);

return grayVersions.contains(version);

}

}

```

6. 性能优化与最佳实践

6.1 连接管理与线程池优化

```java

@Configuration

public class DubboOptimizationConfig {

@Bean

public ApplicationConfig applicationConfig() {

ApplicationConfig config = new ApplicationConfig();

config.setName("optimized-service");

config.setQosEnable(false); // 关闭QoS服务提升性能

return config;

}

@Bean

public ProtocolConfig protocolConfig() {

ProtocolConfig config = new ProtocolConfig();

config.setName("dubbo");

config.setPort(20880);

config.setThreads(200); // 优化线程池大小

config.setIothreads(Runtime.getRuntime().availableProcessors() + 1);

config.setQueues(0); // 不设置队列,直接创建新线程

return config;

}

@Bean

public ProviderConfig providerConfig() {

ProviderConfig config = new ProviderConfig();

config.setRetries(0); // 关闭重试

config.setTimeout(3000);

config.setCluster("failfast"); // 快速失败

return config;

}

}

```

6.2 监控与调优建议

```java

// 自定义监控统计

public class CustomMetricsFilter implements Filter {

private final MeterRegistry meterRegistry;

public CustomMetricsFilter(MeterRegistry meterRegistry) {

this.meterRegistry = meterRegistry;

}

@Override

public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

String serviceName = invoker.getUrl().getServiceInterface();

String methodName = invocation.getMethodName();

Timer.Sample sample = Timer.start(meterRegistry);

try {

Result result = invoker.invoke(invocation);

if (result.hasException()) {

recordError(serviceName, methodName, result.getException());

}

return result;

} finally {

sample.stop(Timer.builder("dubbo.invocation")

.tag("service", serviceName)

.tag("method", methodName)

.register(meterRegistry));

}

}

private void recordError(String serviceName, String methodName, Throwable error) {

Counter.builder("dubbo.errors")

.tag("service", serviceName)

.tag("method", methodName)

.tag("exception", error.getClass().getSimpleName())

.register(meterRegistry)

.increment();

}

}

```

7. 总结

通过本文的源码分析和实战演示,我们深入探讨了Dubbo框架的RPC通信协议和服务治理核心组件的设计实现。关键要点总结如下:

    • 协议设计:Dubbo协议通过精心设计的头部结构和序列化机制,实现了高性能的远程调用

    • 扩展机制:基于SPI的扩展机制使得Dubbo具备高度可定制性

    • 服务治理:完善的服务注册发现、负载均衡、集群容错机制保障了分布式系统的稳定性

    • 性能优化:合理的配置和自定义扩展可以进一步提升系统性能

Dubbo作为成熟的RPC框架,在微服务架构中发挥着重要作用。理解其底层原理和实现机制,有助于我们更好地使用和扩展Dubbo,构建高性能、高可用的分布式系统。

本文基于Dubbo 3.x版本进行分析,具体实现可能随版本更新而变化,建议参考官方文档和最新源码获取最准确的信息。

```java

public class IsInstanceDemo {

public static void main(String[] args) {

Object obj = "Hello World";

Number num = Integer.valueOf(42);

    // 使用isInstance进行类型检查

System.out.println("obj是String类型: " + String.class.isInstance(obj));

System.out.println("obj是Integer类型: " + Integer.class.isInstance(obj));

System.out.println("num是Number类型: " + Number.class.isInstance(num));

System.out.println("num是Double类型: " + Double.class.isInstance(num));

// 与instanceof操作符对比

System.out.println("obj instanceof String: " + (obj instanceof String));

System.out.println("num instanceof Number: " + (num instanceof Number));

}

@IgnoreAuth

@PostMapping(value = "/login")

public R login(String username, String password, String captcha, HttpServletRequest request) {

UsersEntity user = userService.selectOne(new EntityWrapper<UsersEntity>().eq("username", username));

if(user==null || !user.getPassword().equals(password)) {

return R.error("账号或密码不正确");

}

String token = tokenService.generateToken(user.getId(),username, "users", user.getRole());

return R.ok().put("token", token);

}

@Override

public String generateToken(Long userid,String username, String tableName, String role) {

TokenEntity tokenEntity = this.selectOne(new EntityWrapper<TokenEntity>().eq("userid", userid).eq("role", role));

String token = CommonUtil.getRandomString(32);

Calendar cal = Calendar.getInstance();

cal.setTime(new Date());

cal.add(Calendar.HOUR_OF_DAY, 1);

if(tokenEntity!=null) {

tokenEntity.setToken(token);

tokenEntity.setExpiratedtime(cal.getTime());

this.updateById(tokenEntity);

} else {

this.insert(new TokenEntity(userid,username, tableName, role, token, cal.getTime()));

}

return token;

}

/**

* 权限(Token)验证

*/

@Component

public class AuthorizationInterceptor implements HandlerInterceptor {

public static final String LOGIN_TOKEN_KEY = "Token";

@Autowired

private TokenService tokenService;

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

//支持跨域请求

response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");

response.setHeader("Access-Control-Max-Age", "3600");

response.setHeader("Access-Control-Allow-Credentials", "true");

response.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Token, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");

response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

// 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态

if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {

response.setStatus(HttpStatus.OK.value());

return false;

}

IgnoreAuth annotation;

if (handler instanceof HandlerMethod) {

annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);

} else {

return true;

}

//从header中获取token

String token = request.getHeader(LOGIN_TOKEN_KEY);

/**

* 不需要验证权限的方法直接放过

*/

if(annotation!=null) {

return true;

}

TokenEntity tokenEntity = null;

if(StringUtils.isNotBlank(token)) {

tokenEntity = tokenService.getTokenEntity(token);

}

if(tokenEntity != null) {

request.getSession().setAttribute("userId", tokenEntity.getUserid());

request.getSession().setAttribute("role", tokenEntity.getRole());

request.getSession().setAttribute("tableName", tokenEntity.getTablename());

request.getSession().setAttribute("username", tokenEntity.getUsername());

return true;

}

PrintWriter writer = null;

response.setCharacterEncoding("UTF-8");

response.setContentType("application/json; charset=utf-8");

try {

writer = response.getWriter();

writer.print(JSONObject.toJSONString(R.error(401, "请先登录")));

} finally {

if(writer != null){

writer.close();

}

}

// throw new EIException("请先登录", 401);

return false;

}

}

更多推荐