ThreadLocal<String> contextHolder = new ThreadLocal<>()
时间: 2024-05-16 21:11:32 浏览: 118
ThreadLocal<String> contextHolder 是 Java 中的一个线程本地变量,它提供了一种线程安全的方式来存储每个线程的数据,同时保证这些数据对其他线程不可见。在多线程环境下,使用 ThreadLocal 可以避免线程间的数据共享问题,从而避免出现竞态条件等问题。
在具体应用场景中,ThreadLocal 可以用来存储一些需要在线程间隔离的数据,比如当前用户的登录信息、请求的上下文信息等。通过将这些信息存储到 ThreadLocal 中,在整个线程处理过程中都可以方便地获取到这些信息,而不需要在每个方法中传递这些数据。
例如在 SpringMVC 中,可以使用 ThreadLocal 存储当前请求的上下文信息,包括请求路径、请求参数等,然后在整个请求处理过程中都可以通过 ThreadLocal 获取到这些信息,而不需要在每个方法中传递这些数据。
相关问题
ThreadLocal<AuthInfo> contextHolder = new ThreadLocal<>()
引用提到了ThreadLocal类的使用示例和原理。ThreadLocal是Java中的一个线程局部变量,它提供了线程私有的变量副本,每个线程都可以独立地修改自己的副本,互不影响。在这个引用中,ThreadLocal类的泛型参数是AuthInfo,即ThreadLocal<AuthInfo>表示每个线程都有一个独立的AuthInfo对象副本。
在引用中的DynamicDataSourceContextHolderV2类中,也使用了ThreadLocal来存储一个Map对象,即ThreadLocal<Map<Long,SqlSession>> contextHolder。这个contextHolder变量被命名为mySqlSessionMap,用于存储不同线程的数据源信息。每个线程通过调用setDataSourceKey方法来切换不同的数据源,而getDataSourceKey方法可以获取当前线程的数据源信息。clearDataSourceKey方法可以用来重置数据源。
根据引用中的代码片段,可以看出setDataSourceKey方法可以通过传入一个String类型的key来切换数据源,但具体的实现细节在引用中并未给出。
在引用中的SysUserController类中,使用了@DataSource注解来指定不同的数据源,@DataSource(value="master")表示使用名为"master"的数据源,@DataSource(value="slave")表示使用名为"slave"的数据源。这个注解可能与ThreadLocal的使用有一定的关联,但具体的实现细节在引用中并未给出。
综上所述,ThreadLocal<AuthInfo> contextHolder = new ThreadLocal<>()是一个初始化了的ThreadLocal变量,用于存储每个线程的AuthInfo对象副本。但具体的实现细节需要结合代码中的其他部分来进行理解和分析。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--开启Spring的注解--> <context:annotation-config/> <!--Spring的注解扫描包路径--> <context:component-scan base-package="com.example"/> <!--加载数据源连接池--> <bean name="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1/apsfc?serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!--创建SqlSessionFactory对象--> <bean name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--注入数据源连接池--> <property name="dataSource" ref="druidDataSource"/> <!--注入Mybatis的主配置文件--> <property name="configLocation" value="classpath:sqlMapConfig.xml"/> <!--配置DAO的映射文件扫描路径--> <property name="mapperLocations" value="classpath:mapper/*.xml"/> </bean> <!--加载DAO接口扫描--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--将SqlSessionFactory对象进行注入--> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <!--配置DAO接口的扫描基础路径--> <property name="basePackage" value="com.example.meal_ordering_system.dao.**"/> </bean> <!--加载事务管理器--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--加载数据源连接池--> <property name="dataSource" ref="druidDataSource"/> </bean> <!--配置事务增强通知--> <!--transaction-manager加载指定的事务管理器--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--事务规则列表--> <tx:attributes> <!--propagation定义动作的规则--> <!--REQUIRED阻断操作--> <!--NOT_SUPPORTED非阻断操作--> <!--对新增数据操作的规则定义--> <tx:method name="insert*" propagation="REQUIRED"/> <tx:method name="add*" propagation="REQUIRED"/> <!--对修改数据操作的规则定义--> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="edit*" propagation="REQUIRED"/> <!--对删除数据操作的规则定义--> <tx:method name="delete*" propagation="REQUIRED"/> <!--对查询数据操作的规则定义--> <tx:method name="get*" propagation="NOT_SUPPORTED"/> <tx:method name="select*" propagation="NOT_SUPPORTED"/> <tx:method name="query*" propagation="NOT_SUPPORTED"/> </tx:attributes> </tx:advice> <!--托管通知工具类--> <bean name="advice" class="com.example.meal_ordering_system.util.AdviceUtil"/> <!--切面的配置--> <aop:config> <!--切面定义在Service层--> <aop:pointcut id="pointCut" expression="execution(* com.example.meal_ordering_system.service..*(..))"/> <!--将事务增强通知与切面进行绑定--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"/> <!--切面织入--> <aop:aspect ref="advice"> <aop:before method="before" pointcut-ref="pointCut"/> <aop:after method="after" pointcut-ref="pointCut"/> <aop:around method="around" pointcut-ref="pointCut"/> <aop:after-throwing method="exception" pointcut-ref="pointCut"/> </aop:aspect> </aop:config> </beans>
### Spring框架事务管理、数据源配置及AOP切面具体实现
#### 一、Spring事务管理的实现
在Spring应用中,可以通过`@EnableTransactionManagement`注解启用事务支持,并通过声明式事务管理简化复杂的事务操作。以下是基于注解方式的事务管理示例:
```java
@Configuration
@EnableTransactionManagement // 启用事务管理
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource); // 配置事务管理器
}
}
```
在此基础上,在服务层方法上添加`@Transactional`注解即可开启事务[^1]。
---
#### 二、多数据源切换的实现
为了实现多数据源切换,可以借助AOP切面技术完成动态数据源的选择。以下是具体的实现步骤和代码示例:
##### 1. 定义数据源切换注解
创建一个自定义注解`@DataSourceSwitch`,用于标记需要切换的数据源名称。
```java
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSwitch {
String value() default "master"; // 默认为主库
}
```
##### 2. 动态数据源路由逻辑
编写一个抽象类`AbstractRoutingDataSource`,负责根据上下文决定当前使用的数据源。
```java
public abstract class AbstractRoutingDataSource extends AbstractDataSource {
private final Map<Object, Object> targetDataSources;
private final Object defaultTargetDataSource;
protected AbstractRoutingDataSource(Map<Object, Object> targetDataSources, Object defaultTargetDataSource) {
this.targetDataSources = Collections.unmodifiableMap(new HashMap<>(targetDataSources));
this.defaultTargetDataSource = defaultTargetDataSource;
}
@Override
public Connection getConnection() throws SQLException {
return determineCurrentLookupKey().getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return determineCurrentLookupKey().getConnection(username, password);
}
protected Object determineCurrentLookupKey() {
String lookupKey = DynamicDataSourceContextHolder.getDataSourceType(); // 获取线程绑定的数据源key
if (lookupKey == null || "".equals(lookupKey)) {
return defaultTargetDataSource; // 返回默认数据源
}
return targetDataSources.get(lookupKey);
}
}
```
##### 3. 创建动态数据源持有者
使用ThreadLocal保存当前线程的数据源类型。
```java
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(String type) {
contextHolder.set(type);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
```
##### 4. 编写AOP切面逻辑
通过AOP拦截带有`@DataSourceSwitch`注解的方法并设置对应的数据源。
```java
@Aspect
@Component
public class DataSourceSwitchAspect {
@Around("@annotation(dataSourceSwitch)")
public Object around(ProceedingJoinPoint joinPoint, DataSourceSwitch dataSourceSwitch) throws Throwable {
String originalDataSource = DynamicDataSourceContextHolder.getDataSourceType(); // 记录原始数据源
try {
DynamicDataSourceContextHolder.setDataSourceType(dataSourceSwitch.value()); // 设置目标数据源
return joinPoint.proceed();
} finally {
DynamicDataSourceContextHolder.setDataSourceType(originalDataSource); // 恢复原始数据源
}
}
}
```
以上代码展示了如何利用AOP切面实现动态数据源切换的功能[^4]。
---
#### 三、AOP切面实现详解
Spring AOP的核心在于通知(Advice)、切入点(Pointcut)和连接点(Join Point)。下面是一个简单的日志记录切面示例:
```java
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))") // 切入点表达式匹配service包下的所有方法
public void logBefore(JoinPoint joinPoint) {
System.out.println("Method Name: " + joinPoint.getSignature().getName());
System.out.println("Arguments: " + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void logAfterReturning(Object result) {
System.out.println("Return Value: " + result);
}
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void logAfterThrowing(Exception ex) {
System.out.println("Exception Occurred: " + ex.getMessage());
}
}
```
此示例展示了如何通过AOP切面对指定方法进行前置、返回值和异常的日志记录[^5]。
---
#### 四、总结
上述代码分别演示了Spring框架下事务管理、多数据源切换以及AOP切面的具体实现方式。这些功能共同构成了Spring强大的企业级解决方案体系。
阅读全文
相关推荐













