欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解決mapper接口無法映射mapper.xml的問題

 更新時(shí)間:2023年06月26日 11:02:05   作者:wcdunf  
這篇文章主要介紹了解決mapper接口無法映射mapper.xml的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

解決mapper接口無法映射mapper.xml

錯(cuò)誤信息:22-Mar-2019 15:15:53.542 嚴(yán)重 [http-nio-8080-exec-4] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [springmvc] in context with path [] threw exception [Request processing failed; nested exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.hn.mapper.UserMapper.findNameByUser] with root cause
 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.hn.mapper.UserMapper.findNameByUser

第一:在spring-mybatis.xml 配置文件中把mybatis主配置文件和接口映射文件配置好

?? ?<!--注冊(cè)sql工廠-->
?? ?<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
?? ??? ?<property name="dataSource" ref="dataSource"/>
?? ??? ?<!-- 加載mybatis.xml配置文件 -->
?? ??? ?<property name="configLocation" value="classpath:spring/mybatis.xml" />
?? ??? ?<property name="mapperLocations" value="classpath:com/hn/mapper/*.xml" />
?? ?</bean>

第二:把xml中的入?yún)㈩愋腿サ?/p>

?? ?<select id="findNameByUser" resultType="com.hn.pojo.User">
? ? ?SELECT * FROM user WHERE userName = #{userName}
? ? </select>

第三:確認(rèn)實(shí)體類中不能有“有參構(gòu)造”方法

第四:mybatis主配置文件中的配置不能和spring-mybatis中的配置重復(fù).xml 

mapper文件與接口的映射

我們?cè)谑褂?Mybatis 的時(shí)候、只需要定義一個(gè) Mapper xml 文件和一個(gè)對(duì)應(yīng)的 Mapper 接口、并需要實(shí)現(xiàn)該接口、即可在程序中使用該 Mapper 接口、調(diào)用里面的方法對(duì)其進(jìn)行查詢(當(dāng)然 xml 中的 namespace 要關(guān)聯(lián)接口)。

那么這個(gè)功能是怎么實(shí)現(xiàn)的呢 ?

MapperRegistry

我們知道在使用 Mybatis 的時(shí)候都會(huì)去創(chuàng)建一個(gè) Configuration 類、而在這個(gè)類中、則會(huì)創(chuàng)建一個(gè) MapperRegistry

public class MapperRegistry {
  private final Configuration config;
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
  public MapperRegistry(Configuration config) {
    this.config = config;
  }
  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
  public <T> boolean hasMapper(Class<T> type) {
    return knownMappers.containsKey(type);
  }
  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
  public Collection<Class<?>> getMappers() {
    return Collections.unmodifiableCollection(knownMappers.keySet());
  }
  public void addMappers(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }
  public void addMappers(String packageName) {
    addMappers(packageName, Object.class);
  }
}

我們知道注冊(cè)一個(gè) Mapper 類會(huì)加入到 knownMappers 這個(gè) Map 里面、key 為對(duì)應(yīng)的 Mapper 類型、value 為 MapperProxyFactory

在 getMapper 方法中我們看到 mapperProxyFactory.newInstance(sqlSession);

MapperProxyFactory

public class MapperProxyFactory<T> {
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
  public Class<T> getMapperInterface() {
    return mapperInterface;
  }
  public Map<Method, MapperMethodInvoker> getMethodCache() {
    return methodCache;
  }
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}

我們看到這里創(chuàng)建了一個(gè)名為 MapperProxy 的代理類、哦吼、這里也是代理模式

MapperProxy

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? try {
? ? if (Object.class.equals(method.getDeclaringClass())) {
? ? ? return method.invoke(this, args);
? ? } else {
? ? ? return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
? ? }
? } catch (Throwable t) {
? ? throw ExceptionUtil.unwrapThrowable(t);
? }
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
? try {
? ? // A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372
? ? // It should be removed once the fix is backported to Java 8 or
? ? // MyBatis drops Java 8 support. See gh-1929
? ? MapperMethodInvoker invoker = methodCache.get(method);
? ? if (invoker != null) {
? ? ? return invoker;
? ? }
? ? return methodCache.computeIfAbsent(method, m -> {
? ? ? if (m.isDefault()) {
? ? ? ? try {
? ? ? ? ? if (privateLookupInMethod == null) {
? ? ? ? ? ? return new DefaultMethodInvoker(getMethodHandleJava8(method));
? ? ? ? ? } else {
? ? ? ? ? ? return new DefaultMethodInvoker(getMethodHandleJava9(method));
? ? ? ? ? }
? ? ? ? } catch (IllegalAccessException | InstantiationException | InvocationTargetException
? ? ? ? ? ? | NoSuchMethodException e) {
? ? ? ? ? throw new RuntimeException(e);
? ? ? ? }
? ? ? } else {
? ? ? ? return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
? ? ? }
? ? });
? } catch (RuntimeException re) {
? ? Throwable cause = re.getCause();
? ? throw cause == null ? re : cause;
? }
}

正常來說我們創(chuàng)建的接口都不使用 default 修飾、如果使用了、那就代表其有方法的實(shí)現(xiàn)、那么就直接調(diào)用該方法、而不會(huì)再走 Mybatis 的邏輯。 如果走正常邏輯的話就會(huì)創(chuàng)建 MapperMethod

MapperMethod

public class MapperMethod {
? private final SqlCommand command;
? private final MethodSignature method;
? public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
? ? this.command = new SqlCommand(config, mapperInterface, method);
? ? this.method = new MethodSignature(config, mapperInterface, method);
? }
.......
}

我們首先來看看 SqlCommand

public static class SqlCommand {
? private final String name;
? private final SqlCommandType type;
? public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
? ? final String methodName = method.getName();
? ? final Class<?> declaringClass = method.getDeclaringClass();
? ? MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
? ? ? ? configuration);
? ? if (ms == null) {
? ? ? if (method.getAnnotation(Flush.class) != null) {
? ? ? ? name = null;
? ? ? ? type = SqlCommandType.FLUSH;
? ? ? } else {
? ? ? ? throw new BindingException("Invalid bound statement (not found): "
? ? ? ? ? ? + mapperInterface.getName() + "." + methodName);
? ? ? }
? ? } else {
? ? ? name = ms.getId();
? ? ? type = ms.getSqlCommandType();
? ? ? if (type == SqlCommandType.UNKNOWN) {
? ? ? ? throw new BindingException("Unknown execution method for: " + name);
? ? ? }
? ? }
? }
......
? private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
? ? ? Class<?> declaringClass, Configuration configuration) {
? ? String statementId = mapperInterface.getName() + "." + methodName;
? ? if (configuration.hasStatement(statementId)) {
? ? ? return configuration.getMappedStatement(statementId);
? ? } else if (mapperInterface.equals(declaringClass)) {
? ? ? return null;
? ? }
? ? for (Class<?> superInterface : mapperInterface.getInterfaces()) {
? ? ? if (declaringClass.isAssignableFrom(superInterface)) {
? ? ? ? MappedStatement ms = resolveMappedStatement(superInterface, methodName,
? ? ? ? ? ? declaringClass, configuration);
? ? ? ? if (ms != null) {
? ? ? ? ? return ms;
? ? ? ? }
? ? ? }
? ? }
? ? return null;
? }
}

非常簡(jiǎn)單的邏輯、根據(jù)被代理的接口的名稱和調(diào)用方法名稱、組裝成一個(gè) statementId(namespace + sqlId)、去 Configuration 對(duì)象中查找是否存在對(duì)應(yīng)的 MappedStatement

SqlCommandType 則是

public enum SqlCommandType {
? UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
}

再來看看 MethodSignature

public static class MethodSignature {
? private final boolean returnsMany;
? private final boolean returnsMap;
? private final boolean returnsVoid;
? private final boolean returnsCursor;
? private final boolean returnsOptional;
? private final Class<?> returnType;
? private final String mapKey;
? private final Integer resultHandlerIndex;
? private final Integer rowBoundsIndex;
? private final ParamNameResolver paramNameResolver;
? ......

主要是對(duì)方法進(jìn)行一系列的解釋、返回值啊、對(duì)參數(shù)的解釋

再回到 MapperMethod 類中、我們知道它的 invoke 方法被 MapperProxy 調(diào)用

public Object execute(SqlSession sqlSession, Object[] args) {
? Object result;
? switch (command.getType()) {
? ? case INSERT: {
? ? ? Object param = method.convertArgsToSqlCommandParam(args);
? ? ? result = rowCountResult(sqlSession.insert(command.getName(), param));
? ? ? break;
? ? }
? ? case UPDATE: {
? ? ? Object param = method.convertArgsToSqlCommandParam(args);
? ? ? result = rowCountResult(sqlSession.update(command.getName(), param));
? ? ? break;
? ? }
? ? case DELETE: {
? ? ? Object param = method.convertArgsToSqlCommandParam(args);
? ? ? result = rowCountResult(sqlSession.delete(command.getName(), param));
? ? ? break;
? ? }
? ? case SELECT:
? ? ? if (method.returnsVoid() && method.hasResultHandler()) {
? ? ? ? executeWithResultHandler(sqlSession, args);
? ? ? ? result = null;
? ? ? } else if (method.returnsMany()) {
? ? ? ? result = executeForMany(sqlSession, args);
? ? ? } else if (method.returnsMap()) {
? ? ? ? result = executeForMap(sqlSession, args);
? ? ? } else if (method.returnsCursor()) {
? ? ? ? result = executeForCursor(sqlSession, args);
? ? ? } else {
? ? ? ? Object param = method.convertArgsToSqlCommandParam(args);
? ? ? ? result = sqlSession.selectOne(command.getName(), param);
? ? ? ? if (method.returnsOptional()
? ? ? ? ? ? && (result == null || !method.getReturnType().equals(result.getClass()))) {
? ? ? ? ? result = Optional.ofNullable(result);
? ? ? ? }
? ? ? }
? ? ? break;
? ? case FLUSH:
? ? ? result = sqlSession.flushStatements();
? ? ? break;
? ? default:
? ? ? throw new BindingException("Unknown execution method for: " + command.getName());
? }
? if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
? ? throw new BindingException("Mapper method '" + command.getName()
? ? ? ? + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
? }
? return result;
}

如果是 Insert、Update、Delete 的話、那么則先進(jìn)行 convertArgsToSqlCommandParam 然后則調(diào)用 rowCountResult 處理參數(shù)

public Object convertArgsToSqlCommandParam(Object[] args) {
? return paramNameResolver.getNamedParams(args);
}
public Object getNamedParams(Object[] args) {
? final int paramCount = names.size();
? if (args == null || paramCount == 0) {
? ? return null;
? } else if (!hasParamAnnotation && paramCount == 1) {
? ? Object value = args[names.firstKey()];
? ? return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
? } else {
? ? final Map<String, Object> param = new ParamMap<>();
? ? int i = 0;
? ? for (Map.Entry<Integer, String> entry : names.entrySet()) {
? ? ? param.put(entry.getValue(), args[entry.getKey()]);
? ? ? // add generic param names (param1, param2, ...)
? ? ? final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
? ? ? // ensure not to overwrite parameter named with @Param
? ? ? if (!names.containsValue(genericParamName)) {
? ? ? ? param.put(genericParamName, args[entry.getKey()]);
? ? ? }
? ? ? i++;
? ? }
? ? return param;
? }
}

這里主要是將參數(shù)放進(jìn)到 Map 中、這里還額外增加了一些參數(shù)、比如我們經(jīng)常在 mapper 文件中使用的 param1 、param2…

就是在這里實(shí)現(xiàn)的

wrapToMapIfCollection 則是對(duì)集合數(shù)組進(jìn)行處理的

public static Object wrapToMapIfCollection(Object object, String actualParamName) {
? if (object instanceof Collection) {
? ? ParamMap<Object> map = new ParamMap<>();
? ? map.put("collection", object);
? ? if (object instanceof List) {
? ? ? map.put("list", object);
? ? }
? ? Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
? ? return map;
? } else if (object != null && object.getClass().isArray()) {
? ? ParamMap<Object> map = new ParamMap<>();
? ? map.put("array", object);
? ? Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
? ? return map;
? }
? return object;
}

再回到 rowCountResult、則是對(duì)返回值進(jìn)行處理的、邏輯比較簡(jiǎn)單。比如說返回布爾值的、則判斷其影響值是否大于 0

private Object rowCountResult(int rowCount) {
? final Object result;
? if (method.returnsVoid()) {
? ? result = null;
? } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
? ? result = rowCount;
? } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
? ? result = (long) rowCount;
? } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
? ? result = rowCount > 0;
? } else {
? ? throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
? }
? return result;
}

至于如果 sql 類型是 Select 的話

? if (method.returnsVoid() && method.hasResultHandler()) {
? ? executeWithResultHandler(sqlSession, args);
? ? result = null;
? } else if (method.returnsMany()) {
? ? result = executeForMany(sqlSession, args);
? } else if (method.returnsMap()) {
? ? result = executeForMap(sqlSession, args);
? } else if (method.returnsCursor()) {
? ? result = executeForCursor(sqlSession, args);
? } else {
? ? Object param = method.convertArgsToSqlCommandParam(args);
? ? result = sqlSession.selectOne(command.getName(), param);
? ? if (method.returnsOptional()
? ? ? ? && (result == null || !method.getReturnType().equals(result.getClass()))) {
? ? ? result = Optional.ofNullable(result);
? ? }
? }
? break;

針對(duì)存在 ResultHandler 的、判斷是否存在 RowBounds

private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
? MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
? if (!StatementType.CALLABLE.equals(ms.getStatementType())
? ? ? && void.class.equals(ms.getResultMaps().get(0).getType())) {
? ? throw new BindingException("method " + command.getName()
? ? ? ? + " needs either a @ResultMap annotation, a @ResultType annotation,"
? ? ? ? + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
? }
? Object param = method.convertArgsToSqlCommandParam(args);
? if (method.hasRowBounds()) {
? ? RowBounds rowBounds = method.extractRowBounds(args);
? ? sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
? } else {
? ? sqlSession.select(command.getName(), param, method.extractResultHandler(args));
? }
}

針對(duì)返回集合的

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
? List<E> result;
? Object param = method.convertArgsToSqlCommandParam(args);
? if (method.hasRowBounds()) {
? ? RowBounds rowBounds = method.extractRowBounds(args);
? ? result = sqlSession.selectList(command.getName(), param, rowBounds);
? } else {
? ? result = sqlSession.selectList(command.getName(), param);
? }
? // issue #510 Collections & arrays support
? if (!method.getReturnType().isAssignableFrom(result.getClass())) {
? ? if (method.getReturnType().isArray()) {
? ? ? return convertToArray(result);
? ? } else {
? ? ? return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
? ? }
? }
? return result;
}

針對(duì)返回 Map 的

private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
? Map<K, V> result;
? Object param = method.convertArgsToSqlCommandParam(args);
? if (method.hasRowBounds()) {
? ? RowBounds rowBounds = method.extractRowBounds(args);
? ? result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);
? } else {
? ? result = sqlSession.selectMap(command.getName(), param, method.getMapKey());
? }
? return result;
}

針對(duì)返回游標(biāo)的

private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
? Cursor<T> result;
? Object param = method.convertArgsToSqlCommandParam(args);
? if (method.hasRowBounds()) {
? ? RowBounds rowBounds = method.extractRowBounds(args);
? ? result = sqlSession.selectCursor(command.getName(), param, rowBounds);
? } else {
? ? result = sqlSession.selectCursor(command.getName(), param);
? }
? return result;
}

針對(duì) selectOne 的

Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
? ? && (result == null || !method.getReturnType().equals(result.getClass()))) {
? result = Optional.ofNullable(result);
}

可以看到 MapperMethod 也是依賴 Mybatis 的 SqlSession 去幫我們執(zhí)行而已、并沒有太高端的技術(shù)。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 解決IDEA使用Spring Initializr創(chuàng)建項(xiàng)目時(shí)無法連接到https://start.spring.io的問題

    解決IDEA使用Spring Initializr創(chuàng)建項(xiàng)目時(shí)無法連接到https://start.spring.io的問

    這篇文章主要介紹了解決IDEA使用Spring Initializr創(chuàng)建項(xiàng)目時(shí)無法連接到https://start.spring.io的問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • spring boot測(cè)試打包部署的方法

    spring boot測(cè)試打包部署的方法

    spring boot項(xiàng)目如何測(cè)試,如何部署,在生產(chǎn)中有什么好的部署方案嗎?這篇文章就來介紹一下spring boot 如何開發(fā)、調(diào)試、打包到最后的投產(chǎn)上線,感興趣的朋友一起看看吧
    2018-01-01
  • Spring緩存機(jī)制實(shí)例代碼

    Spring緩存機(jī)制實(shí)例代碼

    這篇文章主要介紹了Spring緩存機(jī)制實(shí)例代碼,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • Java中的字符串常量池詳細(xì)介紹

    Java中的字符串常量池詳細(xì)介紹

    這篇文章主要介紹了Java中的字符串常量池詳細(xì)介紹,JVM為了減少字符串對(duì)象的重復(fù)創(chuàng)建,其維護(hù)了一個(gè)特殊的內(nèi)存,這段內(nèi)存被成為字符串常量池或者字符串字面量池,需要的朋友可以參考下
    2015-01-01
  • Java8新特性Stream流中anyMatch和allMatch和noneMatch的區(qū)別解析

    Java8新特性Stream流中anyMatch和allMatch和noneMatch的區(qū)別解析

    這篇文章主要介紹了Java8新特性Stream流中anyMatch和allMatch和noneMatch的區(qū)別解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-01-01
  • java高級(jí)排序之希爾排序

    java高級(jí)排序之希爾排序

    這篇文章主要介紹了java高級(jí)排序之希爾排序 ,需要的朋友可以參考下
    2015-04-04
  • Spring Boot前后端分離開發(fā)模式中的跨域問題及解決方法

    Spring Boot前后端分離開發(fā)模式中的跨域問題及解決方法

    本文介紹了解決Spring Boot前端Vue跨域問題的實(shí)戰(zhàn)經(jīng)驗(yàn),并提供了后端和前端的配置示例,通過配置后端和前端,我們可以輕松解決跨域問題,實(shí)現(xiàn)正常的前后端交互,需要的朋友可以參考下
    2023-09-09
  • java獲取類名的方法詳解

    java獲取類名的方法詳解

    這篇文章主要介紹了java獲取類名的問題詳解,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-07-07
  • 使用JWT創(chuàng)建解析令牌及RSA非對(duì)稱加密詳解

    使用JWT創(chuàng)建解析令牌及RSA非對(duì)稱加密詳解

    這篇文章主要介紹了JWT創(chuàng)建解析令牌及RSA非對(duì)稱加密詳解,JWT是JSON Web Token的縮寫,即JSON Web令牌,是一種自包含令牌,一種情況是webapi,類似之前的阿里云播放憑證的功能,另一種情況是多web服務(wù)器下實(shí)現(xiàn)無狀態(tài)分布式身份驗(yàn)證,需要的朋友可以參考下
    2023-11-11
  • Java 中實(shí)現(xiàn)異步的多種方式

    Java 中實(shí)現(xiàn)異步的多種方式

    文章介紹了Java中實(shí)現(xiàn)異步處理的幾種常見方式,每種方式都有其特點(diǎn)和適用場(chǎng)景,通過選擇合適的異步處理方式,可以提高程序的性能和可維護(hù)性,感興趣的朋友一起看看吧
    2025-03-03

最新評(píng)論