并发编程原理与实战(三十六)深入解析 ThreadLocal 在 Spring 框架中的核心应用与实战技巧

Spring定时任务中使用ThreadLocal的坑 NULL 博文链接:https://bijian1013.iteye.com/blog/2380233 立即下载

本文继续分析ThreadLocal在springmvc、spring等框架下的应用实战。

用户信息存储

在以springmvc 为主要框架的Web应用中,ThreadLocal最常见的应用是在拦截器中存储用户身份信息,然后在Controller、Service和DAO层中直接获取使用。这样可以避免在方法参数中层层传递用户信息,保持代码的简洁性。

拦截器示例代码:

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try{
            //从请求中获取token.
            String token = getToken(request);
            //根据token获取用户信息
          	UserInfo userInfo = getUserInfo(token);
            if(null != userInfo) {
                //储用户身份信息
                SecurityContextHolder.setUserInfo(userInfo);
                return true;
            }else {
                return false;
            }
        }catch(Exception ex) {
            log.error("拦截器异常",ex);
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //请求处理完成后调用清理方法
        SecurityContextHolder.clear();
    }

SecurityContextHolder示例代码:

public class SecurityContextHolder {
    private static final ThreadLocal<UserInfo> USER_INFO_THREAD_LOCAL = new ThreadLocal<>();

    public static void setUserInfo(UserInfo userInfo) {
        USER_INFO_THREAD_LOCAL.set(userInfo);
    }

    public static UserInfo getUserInfo() {
        return USER_INFO_THREAD_LOCAL.get();
    }

    public static void clear() {
        USER_INFO_THREAD_LOCAL.remove();
    }

}

Controller层中直接获取使用:

@PostMapping("/update")
public AjaxResult update(@RequestBody params request,HttpServletRequest servletRequest) {
   UserInfo loguser = SecurityContextHolder.getUserInfo();
   ...
}

数据库连接管理

Spring框架中使用ThreadLocal来管理数据库连接,确保在同一线程的整个事务过程中使用同一个Connection对象。这在声明式事务管理中尤为重要,通过TransactionSynchronizationManager来维护线程本地的数据库连接。以spring 5.3.6版本为例,我们分析下ThreadLocal在其中的应用。

NamedThreadLocal

public abstract class TransactionSynchronizationManager {
    private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
    private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal("Transaction synchronizations");
    private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal("Current transaction name");
    private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal("Current transaction read-only status");
    private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal("Current transaction isolation level");
    private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal("Actual transaction active");
    ......
}

从源码可以看出,spring 内部使用NamedThreadLocal来管理数据库连接,而NamedThreadLocal的核心作用与 ThreadLocal 一致,即‌实现线程本地变量隔离‌(每个线程拥有独立的局部变量),但额外增加了‌变量名称(name)‌ 的支持,主要用于‌调试和日志输出‌,方便在多线程环境中追踪变量归属。

(1)resources‌ - 存储当前线程的事务资源

  • 保存数据库连接等事务资源,以 Map<Object, Object> 结构存储
  • 键通常为数据源对象,值为对应的连接持有者(如 ConnectionHolder)
  • 支持一个事务中操作多个数据源的情况

(2)synchronizations‌ - 管理事务同步回调

  • 存储 TransactionSynchronization 对象集合,用于注册事务生命周期回调
  • 允许在事务提交、回滚等关键节点执行自定义逻辑

(3)currentTransactionName‌ - 记录当前事务名称

  • 存储描述性的事务名称,便于监控和调试

(4)currentTransactionReadOnly‌ - 标识事务只读状态

  • 布尔值,标记当前事务是否为只读模式

(5)currentTransactionIsolationLevel‌ - 保存事务隔离级别

  • 存储当前事务的隔离级别设置

(6)actualTransactionActive‌ - 指示实际事务激活状态

  • 布尔值,表示当前是否有活跃的事务

Spring事务管理调用链

下图是调用一个保存接口,从Controller到数据库的调用过程,分析其中Spring事务管理调用链,剖析NamedThreadLocal来管理数据库连接的过程。
在这里插入图片描述

(1)Controller层方法‌

作为事务调用的入口点,通常接收HTTP请求并调用Service层方法。Controller本身不直接参与事务管理,但它是事务传播的起点。

(2)Service层方法‌

业务逻辑的核心实现层,通常使用@Transactional注解标记需要进行事务管理的方法。Spring会在运行时为这些Service类创建代理对象。对Service类创建代理对象的核心目的是在不修改原有业务逻辑的前提下,通过代理实现功能增强、解耦与扩展。事务管理便是其功能增强之一,将事务管理等切面逻辑集中在代理中,实现代码复用,减少重复开发。

(3)CglibAopProxy‌

从上图可以看出,Spring AOP采用的是CGLIB代理模式。当目标类没有实现接口时,Spring会使用CGLIB生成子类来创建代理对象,拦截所有方法调用并织入事务管理等横切逻辑。上图中,OrderServicelmplEnhancerBySpringCGLIBEnhancerBySpringCGLIBEnhancerBySpringCGLIB1ac47b72就是我们的service类的代理对象。

这里可能会有一个疑问,我们的service类明明是实现了接口的,那为什么还用CGLIB代理而不是JDK动态代理‌?

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
......
}

这是因为Spring Boot 环境下默认开启 proxy-target-class=true,强制使用类代理(CGLIB)‌,即使目标类实现了接口。这一配置确保了对 ServiceImpl 具体方法的增强逻辑能被正确应用,同时避免接口代理的局限性。

这点可以从Spring Boot自动配置的jar中的json元数据中的配置可以验证。比如

spring-boot-autoconfigure-2.4.5.jar中的additional-spring-configuration-metadata.json有如下配置:

{
  "name": "spring.aop.auto",
  "type": "java.lang.Boolean",
  "description": "Add @EnableAspectJAutoProxy.",
  "defaultValue": true
},
{
  "name": "spring.aop.proxy-target-class",
  "type": "java.lang.Boolean",
  "description": "Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).",
  "defaultValue": true
},

spring.aop.auto=true (表示启用AOP)
spring.aop.proxy-target-class=true (表示强制CGLIB)

(4)ReflectiveMethodInvocation‌

该类封装了被代理的目标方法、参数等信息,通过proceed()方法沿着拦截器链依次执行各个拦截器。

(5)TransactionInterceptor‌

Spring事务管理的核心拦截器,实现了MethodInterceptor接口。它负责在目标方法执行前后进行事务处理,包括开启事务、提交事务或回滚事务。

(6)TransactionAspectSupport‌

事务切面支持基类,为事务拦截器提供基础设施支持。它包含了事务处理的主要逻辑,如获取事务属性、创建事务、处理回滚等。在这个类里,我们终于看到了NamedThreadLocal的使用。

/**
 * Holder to support the {@code currentTransactionStatus()} method,
 * and to support communication between different cooperating advices
 * (e.g. before and after advice) if the aspect involves more than a
 * single method (as will be the case for around advice).
 */
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
        new NamedThreadLocal<>("Current aspect-driven transaction");

用于支持currentTransactionStatus()方法的持有者,以及当切面涉及多个方法时(对于around advice而言就是这种情况),在不同协同增强建议(如before和after advice)之间提供通信支持。

从源码可以看出,transactionInfoHolder对象为当前线程保存了一个 TransactionInfo 对象,该对象包含了管理事务所需的关键信息,例如事务状态 (TransactionStatus)、事务属性等
,这确保了在同一线程内执行的多个数据库操作可以共享同一个事务上下文。

/**
 * Opaque object used to hold transaction information. Subclasses
 * must pass it back to methods on this class, but not see its internals.
 */
protected static final class TransactionInfo {

    @Nullable
    private final PlatformTransactionManager transactionManager;

    @Nullable
    private final TransactionAttribute transactionAttribute;

    private final String joinpointIdentification;

    @Nullable
    private TransactionStatus transactionStatus;

    @Nullable
    private TransactionInfo oldTransactionInfo;

    public TransactionInfo(@Nullable PlatformTransactionManager transactionManager,
            @Nullable TransactionAttribute transactionAttribute, String joinpointIdentification) {

        this.transactionManager = transactionManager;
        this.transactionAttribute = transactionAttribute;
        this.joinpointIdentification = joinpointIdentification;
    }

    public PlatformTransactionManager getTransactionManager() {
        Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
        return this.transactionManager;
    }

    @Nullable
    public TransactionAttribute getTransactionAttribute() {
        return this.transactionAttribute;
    }

    /**
     * Return a String representation of this joinpoint (usually a Method call)
     * for use in logging.
     */
    public String getJoinpointIdentification() {
        return this.joinpointIdentification;
    }

    public void newTransactionStatus(@Nullable TransactionStatus status) {
        this.transactionStatus = status;
    }

    @Nullable
    public TransactionStatus getTransactionStatus() {
        return this.transactionStatus;
    }

    /**
     * Return whether a transaction was created by this aspect,
     * or whether we just have a placeholder to keep ThreadLocal stack integrity.
     */
    public boolean hasTransaction() {
        return (this.transactionStatus != null);
    }

    private void bindToThread() {
        // Expose current TransactionStatus, preserving any existing TransactionStatus
        // for restoration after this transaction is complete.
        this.oldTransactionInfo = transactionInfoHolder.get();
        transactionInfoHolder.set(this);
    }

    private void restoreThreadLocalStatus() {
        // Use stack to restore old transaction TransactionInfo.
        // Will be null if none was set.
        transactionInfoHolder.set(this.oldTransactionInfo);
    }

    @Override
    public String toString() {
        return (this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction");
    }
}

我们跟中下进入TransactionAspectSupport的invokeWithinTransaction()方法后事务的创建过程。

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {
    ...
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);        
    ...
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);   
    ...
    // Standard transaction demarcation with getTransaction and commit/rollback calls.    
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
    ...
}      

如果有需要则创建事务

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

		// If no name specified, apply method identification as transaction name.
		if (txAttr != null && txAttr.getName() == null) {
			txAttr = new DelegatingTransactionAttribute(txAttr) {
				@Override
				public String getName() {
					return joinpointIdentification;
				}
			};
		}

		TransactionStatus status = null;
		if (txAttr != null) {
			if (tm != null) {
                // 获取事务的状态
				status = tm.getTransaction(txAttr);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
							"] because no transaction manager has been configured");
				}
			}
		}
    	// 准备事务
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}

从源码中可以看出,准备事务信息之前通过TransactionStatus接口检查事务的当前状态。TransactionStatus 接口是 Spring 事务管理的核心接口之一,主要用于在事务执行过程中获取状态信息和进行程序化控制。包括判断当前事务是否为新创建的事务、是否已被标记为只能回滚、否已完成、是否包含保存点,还支持通过编程方式将事务标记为回滚状态、保存点管理等功能。

(7)AbstractPlatformTransactionManager‌

获取事务的状态需要通过抽象类AbstractPlatformTransactionManager‌的getTransaction()方法完成。该抽象类是Spring事务管理器的抽象基类,实现了PlatformTransactionManager接口。它定义了事务管理的基本流程,包括事务的创建、提交、回滚和挂起恢复操作。

/**
 * This implementation handles propagation behavior. Delegates to
 * {@code doGetTransaction}, {@code isExistingTransaction}
 * and {@code doBegin}.
 * @see #doGetTransaction
 * @see #isExistingTransaction
 * @see #doBegin
 */
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
        throws TransactionException {

    // 当没有提供事务定义时,系统会使用默认的事务配置。这意味着如果方法上未明确使用@Transactional注解或未在配置中指定事务属性,框架将采用默认的事务管理行为。
    TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());

    Object transaction = doGetTransaction();
    boolean debugEnabled = logger.isDebugEnabled();

    if (isExistingTransaction(transaction)) {
        // 如果检测到存在事务 -> 则检查传播行为以确定应如何执行。
        return handleExistingTransaction(def, transaction, debugEnabled);
    }

    // 如果是新建事务,则检查新事务的定义设置
    if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
    }

    // 如果未找到现有事务 -> 则检查传播行为以确定后续操作。
    if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException(
                "No existing transaction found for transaction marked with propagation 'mandatory'");
    }
    else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
            def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
            def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        SuspendedResourcesHolder suspendedResources = suspend(null);
        if (debugEnabled) {
            logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
        }
        try {
            //开启事务
            return startTransaction(def, transaction, debugEnabled, suspendedResources);
        }
        catch (RuntimeException | Error ex) {
            resume(null, suspendedResources);
            throw ex;
        }
    }
    else {
        // 创建“空”事务:无实际事务,但可能存在同步。“空”事务通常是指只读的事务,方法被标记为@Transactional(readOnly = true)
        if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
            logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                    "isolation level will effectively be ignored: " + def);
        }
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
    }
}

开启事务

/**
 * Start a new transaction.
 */
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
        boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {

    boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    DefaultTransactionStatus status = newTransactionStatus(
            definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
    doBegin(transaction, definition);
    prepareSynchronization(status, definition);
    return status;
}

(8)DataSourceTransactionManager‌

抽象类AbstractPlatformTransactionManager‌定义了事务管理的基本流程,包括事务的创建、提交、回滚和挂起恢复操作,具体的实现则需要由子类去实现。DataSourceTransactionManager‌就是其实现类之一,针对JDBC数据源的具体事务管理器实现。它管理数据库连接的事务状态,负责将事务的开启、提交和回滚操作委托给底层的JDBC连接。

protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject)transaction;
    Connection con = null;

    try {
        if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            Connection newCon = this.obtainDataSource().getConnection();
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
            }

            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }

        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
        con = txObject.getConnectionHolder().getConnection();
        Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);
        txObject.setReadOnly(definition.isReadOnly());
        if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
            }

            con.setAutoCommit(false);
        }

        this.prepareTransactionalConnection(con, definition);
        txObject.getConnectionHolder().setTransactionActive(true);
        int timeout = this.determineTimeout(definition);
        if (timeout != -1) {
            txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
        }

        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
        }

    } catch (Throwable ex) {
        if (txObject.isNewConnectionHolder()) {
            DataSourceUtils.releaseConnection(con, this.obtainDataSource());
            txObject.setConnectionHolder((ConnectionHolder)null, false);
        }

        throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
    }
}

(9)TransactionAspectSupport,将事务信息绑定当当前线程

获取了事务状态之后回到TransactionAspectSupport,下一步就是将事务信息绑定当当前线程。

/**
 * Prepare a TransactionInfo for the given attribute and status object.
 * @param txAttr the TransactionAttribute (may be {@code null})
 * @param joinpointIdentification the fully qualified method name
 * (used for monitoring and logging purposes)
 * @param status the TransactionStatus for the current transaction
 * @return the prepared TransactionInfo object
 */
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
        @Nullable TransactionAttribute txAttr, String joinpointIdentification,
        @Nullable TransactionStatus status) {

    TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
    if (txAttr != null) {
        // We need a transaction for this method...
        if (logger.isTraceEnabled()) {
            logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
        // The transaction manager will flag an error if an incompatible tx already exists.
        txInfo.newTransactionStatus(status);
    }
    else {
        // The TransactionInfo.hasTransaction() method will return false. We created it only
        // to preserve the integrity of the ThreadLocal stack maintained in this class.
        if (logger.isTraceEnabled()) {
            logger.trace("No need to create transaction for [" + joinpointIdentification +
                    "]: This method is not transactional.");
        }
    }

    // We always bind the TransactionInfo to the thread, even if we didn't create
    // a new transaction here. This guarantees that the TransactionInfo stack
    // will be managed correctly even if no transaction was created by this aspect.
    txInfo.bindToThread();
    return txInfo;
}

准备好事务信息后调用内部类TransactionInfo中的bindToThread()方法将当前事务绑定到当前线程。

private void bindToThread() {
    // 暴露当前 TransactionStatus,保留任何现有的 TransactionStatus
    // 以便在此事务完成后进行恢复
    this.oldTransactionInfo = transactionInfoHolder.get();
    transactionInfoHolder.set(this);
}

获取事务状态则通过currentTransactionInfo()方法实现。

@Nullable
protected static TransactionInfo currentTransactionInfo() throws NoTransactionException {
    return transactionInfoHolder.get();
}

(10)TransactionInterceptor,清理事务信息

回到Spring事务管理的核心拦截器,invokeWithinTransaction()方法的finally块中,会调用cleanupTransactionInfo()来确保事务结束后相关资源被正确清理。

/**
 * Reset the TransactionInfo ThreadLocal.
 * <p>Call this in all cases: exception or normal return!
 * @param txInfo information about the current transaction (may be {@code null})
 */
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
    if (txInfo != null) {
        txInfo.restoreThreadLocalStatus();
    }
}

restoreThreadLocalStatus()方法是TransactionAspectSupport‌中的方法

private void restoreThreadLocalStatus() {
    // Use stack to restore old transaction TransactionInfo.
    // Will be null if none was set.
    transactionInfoHolder.set(this.oldTransactionInfo);
}

从源码可以看出,使用栈来恢复旧的 TransactionInfo 事务信息, 如果之前没有设置过,则为 null。

到此,我们已经理清事务存储到NamedThreadLocal的过程,每个线程独立存储自己的事务信息,这也是spring在多线程环境下保证事务安全的重要手段之一。

Pytorch(一) Pytorch 的安装 安装步骤 1.检查是否有合适的GPU, 若有安装Cuda与CuDNN 2.下载pytorch安装文件,进行pytorch的安装 检查电脑是否有合适的GPU 在桌面上右击如果能找到NVIDA控制面板,则说明该电脑有GPU。控制面板如下,并通过查看系统信息获取支持的Cuda版本 点击 帮助->点击 系统信息 弹出下面的对话框,在驱动程序版本那一栏就能看到该计算机使用的驱动版本。 ... 阅读详情

相关推荐

【TM1650】STM8S TM1650芯片控制程序

以下为.h文件: #ifndef __TM1650_I2C_H #define __TM1650_I2C_H #include "stm8s.h" #include "stm8s_gpio.h" #include "tim1.h" #include "usart2.h" #include &lt;iostm8s103f3.h&gt; #include &lt;intrinsics....

cxw312864660的博客 1万+

Spring mvc 线程安全 threadLocal

我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突。我们使用模板类访问底层数据,根据持久化技术的不同,模板类需要绑定数据连接或会话的资源。但这些资源本身是非线程安全的,也就是说它们不能在同一时刻被多个线程共享。虽然模板类通过资源池获取数据连接或会话,但资源池本身解决的是数据连接或会...

weixin_43706468的博客 1255

matlab tolx,Nelder-Mead算法matlab

Nelder-Mead算法适合变量数不是很多的方程求极值.原理:Nelder-Mead法是利用多面体来逐步逼近最佳点x*.设函数变量为n维,则在n维空间里多面体有(n+1)个顶点.设x1,x2,...,xn+1为多面体的顶点,且满足:f(x1)<=f(x2)<=...<=f(xn+1)Nelder-Mead法试着将多面体中最差的顶点xn+1(也就是函数的最大点)以新的最佳点替代,...

weixin_36485298的博客 1480

SpringMVC- ThreadLocal变量的注意点

在Web应用中,尤其是在使用Spring框架或类似的服务器端Java技术时,是一种常用的方式来存储每个请求的用户信息或上下文数据。然而,由于Web服务器通常使用线程池来处理请求,因此理解和正确使用变得至关重要。

青衫客36的博客 1032

深度解析 Android 开发工程师岗位:技术栈、职责与面试全攻略

本文深度剖析Android开发工程师职位要求,从核心技术栈到面试准备进行全面解读。关键点包括:1)掌握Kotlin语言特性和Jetpack组件;2)具备UI开发、性能优化和逆向分析能力;3)理解MVVM架构和Room数据库;4)熟悉常用工具链和开发流程。文章提供了详细的面试题库及解析,涵盖基础概念、UI优化、架构设计等方向,并给出职业发展建议:夯实基础、拓展技术广度、培养工程素养。旨在帮助开发者系统性地准备Android岗位求职,把握移动开发领域的技术趋势。

郑伟强dev的专栏 172

旗鱼优化算法(SFO)详解:从海洋猎手到智能优化

旗鱼优化算法(SFO)是一种新型群体智能优化算法,灵感来源于旗鱼的群体捕食行为。该算法采用双种群机制,模拟旗鱼(精英个体)与沙丁鱼(普通个体)的交互过程,通过交替攻击策略实现探索与开发的平衡。核心机制包括:基于猎物密度的旗鱼位置更新、随迭代衰减的攻击力度控制、以及捕食替换机制。SFO具有参数少、全局搜索能力强等特点,在高维复杂优化问题中表现优异。算法通过模拟自然界中的群体智能行为,为解决工程优化问题提供了新思路。

qq_42212808的博客 163

SpringMVC:学习笔记(12)——ThreadLocal实现会话共享

SpringMVC:学习笔记(12)——ThreadLocal实现会话共享 ThreadLocal   ThreadLocal,被称为线程局部变量。在并发编程的情况下,使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。每个Thread对象内部都维护了一个ThreadLocalMap它可以存放若干个ThreadLocal。如下为Thread源码部分: /* ...

weixin_30457881的博客 275

windows下玩转vllm:在wsl下安装vllm

当前,部署通义千问2.5-vl已经是一件箭在弦上,不得不发的事儿了。网上搜了一下,发现还是用vllm用的比较多。于是乎,这就开始尝试部署vllm。但是我们之前说了,vllm是不支持直接在windows上部署的,我们还得搞wsl或者docker。咱今天先尝试使用wsl进行安装哈。

xiezhipu的博客 4136

【Spring MVC】拦截器、JWT 认证与 ThreadLocal 数据共享笔记

在Spring MVC框架中,拦截器、JWT认证和ThreadLocal工具共同构建了强大的安全与请求处理机制。它们协同工作,确保了应用的安全性、高效性和线程安全。拦截器、JWT和ThreadLocal在Web开发中扮演关键角色,提供安全认证、请求预处理和线程局部数据管理,强化了应用的安全性、效率和维护性,是构建现代Web服务不可或缺的技术栈。理解和掌握这些技术,对于提升开发质量至关重要。

ajsbxi的博客 1585

IP-Guard分类管理详解

大家都知道在配置IPGUARD策略前需要做分类库进行设置和分类。 1、应用程序:可以分类,例如上班时间只能用OFFICE和WPS,记事本等办公软件,可以在分类里设置一个类 ;办公软件,在未分类里找到OFFICE,WPS等拖入心建的分类里就可以在配置策略里调用。 2、网站分类也不一样,可以按照自己方式自定义。也可以导入厂家已经收集到的网站分类。 3、邮箱分类:可以提前定义好一个或...

IP-Guard专栏 589

这20个网络工程项目经验,帮我跳槽加薪不止一次

嘿,大家好,我是圈圈,一个在网络工程圈子里混了十多年的老鸟。从国企的网管起步,到现在给大厂做顾问,我见过太多简历:有的光鲜亮丽,证书一堆,但一聊项目,就露怯;有的简历朴实,但列出的项目经验让人眼前一亮,直接拿offer。为什么?因为招聘官最看重实战。理论谁都会背,项目经验才是你的“硬通货”。尤其在网络工程这行,项目不光证明你的技能,还展示你解决问题的能力。今天,我就分享20个我亲身经历或见过的网络工程项目经验。每个都值得写进简历,不是吹牛,这些项目帮我跳槽加薪不止一次。

网络技术联盟站 431

ThreadLocal在Spring中的应用

共享对象 如果在每个线程中ThreadLocal.set()进去的东西本来就是多线程共享的同一个对象,比如static对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题

Leon_Jinhai_Sun的博客 2309

2024年广东省职业院校技能大赛“区块链技术应用”任务书—区块链应用系统开发参考答案

摘要 本文档提供了2024年广东省职业院校技能大赛"区块链技术应用"项目中区块链电子签章前端功能开发的参考答案。主要内容包括:1)注册页面开发,实现身份证号码双向绑定和注册请求发送;2)登录页面开发,实现用户认证、状态保存和页面跳转;3)个人印章页面开发,实现注销功能和印章信息展示。每个任务均包含具体实现代码片段和功能测试要点,涵盖Vue.js框架下的数据绑定、axios请求处理和本地存储等关键技术点。

我是网络安全兼技能大赛工程师,专注网络安全技术学习与技能大赛实战!持续分享最新的技术文章,帮你少走弯路,一起在技术赛道上进步~ 343

Day527&528&529.图灵学院之面试题③ -面经

图灵学院之面试题③ 一、HashMap的扩容机制原理是什么? 新建一个老数组的两倍大小的新组,遍历老数组,且若含有链表,就再遍历(套了两层遍历)。根据hash+新数组容量计算出新数组数组下标位置,并转移到新数组。 1.8 若遍历链表时,发现为红黑树,依然重新计算新数组所对应的下标(老数组对应一个红黑色的位置,不代表新数组的位置是相同) ,经过再计算后,再次判断是否需要建立生成红黑色(代表扩容后,之前的红黑树会被拆分为链表+链表或链表+红黑树或不同位置的红黑树或仍然是相同位置的红黑树)。 二、Conc

阿昌爱Java 3171

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

登录拦截器

拦截器登录校验,查看token,快速实现模板

记录学习,共同进步 794

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

MVC拦截器、ThreadLocal来进行登录拦截

MVC拦截器、ThreadLocal来进行登录拦截

f552126506的博客 940

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

spring中ThreadLocal 的使用

解决问题: 在service层,使用RequestContextHolder获取controller层请求(HttpServletRequest)响应信息(HttpServletResponse) RequestContextHolder使用: RequestContextHolder含义持有上下文的Request容器 HttpServletRequest request= ((...

qq_38691015的博客 1352

讲一讲 SpringMVC,线程变量 ThreadLocal 的使用

讲一讲 SpringMVC,线程变量 ThreadLocal 的使用

道友 473

SpringMVC拦截器+ThreadLocal统一token处理标题

使用SpringMVC拦截器+JWT+ThreadLocal进行登录鉴权的总结。

qq_51515932的博客 937

ThreadLocal源码解读

1. 背景 ThreadLocal源码解读,网上面早已经泛滥了,大多比较浅,甚至有的连基本原理都说的很有问题,包括百度搜索出来的第一篇高访问量博文,说ThreadLocal内部有个map,键为线程对象,太误导人了。 ThreadLocal非常适合对Java多线程编程感兴趣的程序员作为入门类阅读,原因两方面: 加上注释源码也不过七八百行。 结构清晰,代码简洁。 本文重点导读ThreadLocal...

weixin_30326741的博客 603

Spring MVC 登录拦截器以及ThreadLocal

因为项目用到自定义的登录而且是前后端分离,接口都需要登录后才能访问,同时在做数据的增加、删除、修改的时候需要传进当前账户ID,因此自定义的一个公共方法和拦截器。代码如下: 定义公共方法: 它的作用就是在各个地方都可以通过UserContext .getUserSession获取当前session public class UserContext implements Serializab...

Yuanhaoxin的博客 1079

Spring中Controlle单例和ThreadLocal的使用

结论: controller默认是单例的,不要使用非静态的成员变量,否则会发生数据逻辑混乱。 正因为单例所以不是线程安全的。 我们下面来简单的验证下: package com.riemann.springbootdemo.controller; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import org.springframework.

啦啦啦啦啦啦博客 2519

SpringMvc之XML配置的aop问题&&threadlocal释放

但是,如果是Controller层调用Service层,从Service层抛出,依然会抛到Controller,所以还是会调用@ControllerAdvice进行处理(我们的xxjob调度也是controller的,但是为什么会触发BasicErrorController,这里自己想的是估计是xml配置的aop,所以触发的是BasicErrorController异常)。问题描述:xxjob调度的异常错误进入了正常app请求的线程中,导致app正常返回报文中加入了xxjob的异常信息。

qq_41458352的博客 386

SpringMVC NamedThreadLocal

参考:http://3001448.blog.51cto.com/2991448/1205771

海风的博客 1万+

spring(基础20) threadLocal在spring框架中的运用

一、ThreadLocal是什么  早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。  ThreadLocal,顾名思义,它不是一个线程,而是线程的一个本地化对象。当工作于多线程中的对象使用ThreadLocal维护变量时, ThreadLocal

babyZeng 7485

Spring单例模式与线程安全ThreadLocal

Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方。 单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。 当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线程会并发执行该请求多对应的业务逻辑(成员方法),此时就要注意了,如果该处理逻辑

刘书的IT博客 6202
上一篇: 开发避坑指南(68):Java Stream 统计List中元素某个字段值不同的次数实战指南
下一篇: 开发避坑指南(69):FastJSON 解析非标准 JSON 数据异常排查与解决方案
帧栈
博客等级 码龄2年 335粉丝 120原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帧栈

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值