全面解读Java容器源码:ConcurrentSkipListMap的高并发有序结构
全面解读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 {
@Overridepublic 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 {
@Overridepublic 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 {
@Beanpublic 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;
}
}
更多推荐
所有评论(0)