java:S6809: Spring did not allow that rule

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 via this 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.

Hello there, and welcome to our community!

Would you be able to reduce the scope of this reproducer down to the single FP that you are reporting? By copy-pasting this code snippet I was not able to reproduce the issue, maybe with a simpler example I might have more luck

Hi. I’m not sure exactly how to report down to a single FP, so if you can change it, please do.

If that snippet doesn’t work for you, I’ve modified it with a simpler version

package com.example.self_injection;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class TestService {


    private final TestService testService;


}

Based on that code, Spring boot won’t run, and I get the same response error to the top question.

Please reply if you have any other issues.

This code does not raise any issue on my end, are you sure this last snippet of code you shared is having issues reported on SonarQube?

If so, could you share on what line the issue is?

I cannot really help with SpringBoot-related issues, I can only check if SonarQube issues are correct or not.

Maybe I wasn’t specific in my previous post, but what I would like to have is a simple code snippet that generates the java:S6809 issue you mentioned at the beginning. The first code snipped you shared was hard for me to copy paste as it had a lot of dependencies