Hello, please note that there may be some incorrect translations using the translator.
What language is this for?
Java
Product: SonarQube
Which rule?
java:S6809 [ Methods with Spring proxy should not be called via “this”]
Project source: Java 17, Spring Boot 3.X.X
The resolution of the rule in this SonarCube seems to be incorrect. In How Can I fix It, it is written as follows.
Replace calls to
@Async
or@Transactional
methods viathis
with calls on an instance that was injected by Spring (@Autowired
,@Resource
or@Inject
). The injected instance is a proxy on which the methods can be invoked safely.
However, when I write the corresponding code and run it,
package com.example.self_injection;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class TestService {
private final TestEntityRepository testEntityRepository;
private final TestLoggingEntityRepository testLoggingEntityRepository;
private final TransactionalService transactionalService;
private TestService testService;
@Autowired
public void setTestService(TestService testService) {
this.testService = testService;
}
@Transactional
public void save(Map<String, String> map) {
testEntityRepository.save(TestEntity.builder().name(map.get("name")).build());
}
public void getReadOnlyMethod(Long id){
log.info("Transactional ReadOnly Start");
TestEntity te = testEntityRepository.findById(id).orElse(null);
if(te == null) {
log.info("~~");
return;
}
TestLoggingEntity testLoggingEntity = TestLoggingEntity.builder().id(te.getId())
.name("123").actionName("action").build();
testService.upsertMethod(testLoggingEntity);
log.info("Transactional ReadOnly End");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void upsertMethod(TestLoggingEntity testLoggingEntity) {
log.info("Transactional Start");
testLoggingEntityRepository.findById(testLoggingEntity.getId()).ifPresentOrElse(
entity -> {
entity.updateAction("action1");
testLoggingEntityRepository.save(entity);
},
() -> testLoggingEntityRepository.save(testLoggingEntity)
);
log.info("Transactional end");
}
}
I get the following error.
┌─────┐
| testService
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
In this case, you need to change the option for spring.main.allow-circular-reference to true starting from Spring Boot 2.6.X version.
There is a note about it in the corresponding GitHub commit for your reference…
In practice, you should use self references as a last resort only
In other words, the workaround for that rule seems to be wrong and you need to tweak Spring’s options for that rule, which I believe is the option to create a different side effect.
Rather than this workaround, I think it’s better to handle it the way the Spring team prefers, which is the following way.