В приложении My Spring Boot реализован пример TenantStore
для хранения данных в ThreadLocalTargetSource
, подробно описано в по этой ссылке
@Bean(destroyMethod = "destroy")
public ThreadLocalTargetSource threadLocalTenantStore() {
ThreadLocalTargetSource result = new ThreadLocalTargetSource();
result.setTargetBeanName("tenantStore");
return result;
}
Рабочий пример позволяет объекту TenantStore
быть установленным и введенным Spring Framework. Моя версия класса TenantFilter
, описанная в этой статье, устанавливает свойства объекта TenantStore
всякий раз, когда выполняется запрос сервлета
@Autowired
private TenantStore tenantStore;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
String token = (String) request.getAttribute(ACCESS_TOKEN_VALUE);
if (token != null) {
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
if (oAuth2AccessToken.getAdditionalInformation() != null) {
String tenantName = (String) oAuth2AccessToken.getAdditionalInformation().get("tenant");
storeTenantInThread(tenantName);
}
}
}
chain.doFilter(request, response);
} catch (ResourceNotFoundException e) {
log.error(e.getMessage());
} finally {
clearTenant();
}
}
private void storeTenantInThread(String tenantName) {
tenantStore.setName(tenantName);
}
private void clearTenant() {
tenantStore.clear();
}
У меня тогда есть ряд служб, в которых TenantStore
автоматически подключен и в каждой из этих служб TenantStore
содержит информацию, которая была заполнена методом doFilter()
. За исключением одного класса. По некоторым причинам свойства TenantStore
в этом классе все еще нулевые. Имя затронутого класса - MyCacheService
, а архитектура выглядит следующим образом:
@RestController
@RequestMapping("/here")
public class MyController {
@Autowired
private MyService myService
@GetMapping
public ResponseEntity myGetMethod(@RequestParam("text") String text) {
myService.myMethod(text);
return new ResponseEntity(Http.OK);
}
}
@Service
public class MyService {
@Autowired
private TenantStore tenantStore;
@Autowired
private MyOtherService myOtherService;
public void myMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
myOtherService.myOtherMethod(text);
}
}
@Service
public class MyOtherService {
@Autowired
private TenantStore tenantStore;
@Autowired
private Map<String, MyComponent> myComponents;
public void myOtherMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
MyComponent useThisComponent = myComponents.get("componentName");
useThisComponent.myComponentMethod(text);
}
}
@Component("componentName")
public class MyComponent {
@Autowired
private TenantStore tenantStore;
@Autowired
private MyCacheService myCacheService;
public void myComponentMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
entityAliasCacheService.myCacheMethod(String text);
}
}
@Service
public class MyCacheService {
@Autowired
private TenantStore tenantStore;
public void myCacheMethod(String text) {
System.out.println(tenantStore.getName()); //DOES NOT WORK - tenantStore object is not null but the name property is
}
}
Из того, что я могу догадаться, по какой-то причине TenantStore
в MyCacheService
заполняется в другом потоке хотя я понятия не имею почему.