简体   繁体   中英

Springboot @Transactional - How to make spring call super when it creates service proxy?

I have intermediate skills in Springboot and can develop simple to medium complex applications but this one is throwing me off a little bit.

I have a @Service that extends Guava's AbstractIdleService and a @Controller that has instance of the service autowired.

In the service, I have a method, say performTransaction() that is annotated with @Transactional (ofcourse to let Spring take care of transactions) and it does create a proxy of the service class. But when I debug the application I can see that the proxy that is injected in the controller contains all super class (AbstractIdleService) class fields as null meaning super() is not called when the proxy of my service was created. Because of this the application is failing to initialize when calling the super class's (AbstractIdleService) startAsync method. I am not really sure how to make this work.

I have added some code below for illustration. This is my service class:

import com.google.common.util.concurrent.AbstractIdleService;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MyService extends AbstractIdleService {
  @Transactional
  public void performTransaction() {
  }
}

This is the controller that uses the above service:

@Controller
public void MyController {
    private MyService service;
    private EurekaRegistration eurekaRegistration;
    
    @Autowired
    public MyController(MyService service, EurekaRegistration eurekaRegistration) {
        this.service = service;     // This is where the proxy contains all AbstractIdleService fields null
        this.eurekaRegistration = eurekaRegistration;
    }

    @EventListener({ ApplicationPreparedEvent.class })
        public void handleApplicationPreparedEvent() {
        this.service.startAsync();  // This line throws NPE, as delegate in the parent is null
        this.service.awaitRunning();
    }
}

I must use AbstractIdleService as that is the organizational convention and I need to invoke some startup code in the startAsync method. But invoking startAsync method on the service is causing a NullPointerException as it uses a parent field that is null because the proxy initialization never called super.

I'm not 100% sure if this is the actual problem, but try something like this (untested):

Interface for proxy:

import com.google.common.util.concurrent.Service;

public interface MyService extends Service {

  void performTransaction();
}

Implementation:

import com.google.common.util.concurrent.AbstractIdleService;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MyServiceImpl extends AbstractIdleService implements MyService {
  @Transactional
  public void performTransaction() {
  }
}

Controller, declare service interface, not implementation:

@Controller
public void MyController {
    private MyService service;
    private EurekaRegistration eurekaRegistration;
    
    @Autowired
    public MyControlle(MyService service, EurekaRegistration eurekaRegistration) {
        this.service = service;
        this.eurekaRegistration = eurekaRegistration;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM