-1

I have a class Wallet that extends a third party class. This class has an autowired service that is null when i try to use. Why can't spring find this service?

@Component
public class Wallet extends TRXService {
    
    @Autowired
    private TransactionsService transactionsService; //should be autowired
    
    private static Logger logger = LoggerFactory.getLogger(Wallet.class.getName());
    
    JSONObject message;
    
    private String trxcode;
    
    private String operation;
    
    public Wallet(String payload) {
        message = new JSONObject(payload);
    }


    //@Override
    public void run() {

        //process payload

        try {

            logger.info("Wallet|" + message);
            trxcode = message.getJSONObject("header").getString("code");
            operation = message.getJSONObject("header").getString("operation");

            logger.info("Executing operation " + operation+ "with code " +trxcode);
           
            if(trxcode.equals("0000")) {
                transactionsService.deposit(message);//<----throws NullPointerException
            }else if (trxcode.equals("00001")) {
                transactionsService.transfer(message);
            }else if (trxcode.equals("0002")) {
                transactionsService.payment(message);
            }else {
                logger.error("transaction code not identified");
            }

            //this.respond(message, "0000", "Success");
        }
        catch (Exception ex) {
            logger.error(ex.toString());
        }

    }
}

class injecting Wallet:

@Component
public class WalletProcessor {

    @Autowired
    private Wallet wallet;


    private static Logger logger = LoggerFactory.getLogger(Wallet.class.getName());

    @Override
    public void afterPropertiesSet() {

        try {
            wallet.init(logger);
        } catch (Exception ex) {
            logger.error(ex.toString());
        }
    }
}
CryptoFool
  • 21,719
  • 5
  • 26
  • 44
user123rd
  • 1
  • 4
  • 1
    Does this answer your question? [Why is my Spring @Autowired field null?](https://stackoverflow.com/questions/19896870/why-is-my-spring-autowired-field-null) – Jeroen Steenbeeke Feb 02 '21 at 07:03
  • 1
    I tried that but it didn't work, thanks. – user123rd Feb 02 '21 at 07:17
  • @JeroenSteenbeeke - I don't think that the question you reference answers this particular problem. The referenced problem involved the autowiring of a class instance that was created with `new` instead of allowing Spring to create the object as a Spring bean. It was this fact that explained why the autowiring wasn't happening. Here, the OP is not calling `new` to create an object, and then expecting fields within it to be autowired, so this is a different problem. – CryptoFool Feb 02 '21 at 07:44
  • @user123rd - per the posted answer, do you know if the package containing the Wallet class is being scanned by Spring? My guess is that it is not, and so that's your problem. – CryptoFool Feb 02 '21 at 07:46
  • @steve I flagged the post as a potential duplicate, the comment was done automatically – Jeroen Steenbeeke Feb 02 '21 at 07:48
  • 1
    Where do you use the WalletProcessor class? Is that object initialized by yourself with the `new` keyword maybe? – dunni Feb 02 '21 at 08:09
  • @Steve - I have logged all the beans that are being created and wallet is amongst. – user123rd Feb 02 '21 at 08:31
  • The thing that troubles me, is the payload in the Wallet object. This only makes sense, if Wallet is an object that's somehow related to a request, message or similar and instantiated in that context. Another possibility is also, that while Spring indeed initializes a bean instance of Wallet (with a random string as payload, there are enough strings in the Spring context to fulfill this dependency), but at the point where you actually use your context related Wallet instance, it's not that bean instance, but rather a new instance, which can also be created by a framework. – dunni Feb 02 '21 at 09:11
  • So to properly answer your question, we need a bit more context, i.e. where is WalletProcessor used, and also, what the class hierarchy of Wallet is (i.e.what's TRXService? Is that your own class, does it come from a framework), what triggers the run method of Wallet? – dunni Feb 02 '21 at 09:12
  • Also, you need to initialize Wallet yourself somewhere, otherwise how do you get the correct payload in it, if it would be initialized by Spring? The code snippets here don't make much sense as it is. – dunni Feb 02 '21 at 09:12
  • @dunni - the TrxService is not my own class, is not even a spring application, pure Java. It is an external service that sends the payload to Wallet via kafka. Once a message/payload is received the run() method in wallet is triggered. I just need to override TrxService run() method in Wallet and put my logic there. – user123rd Feb 02 '21 at 11:01
  • Ah, and there we have it i guess. Most likely then, Kafka or a Kafka listener instantiates Wallet with the payload, and that's why your autowired field is null, because that particular instance is then not managed by Spring. – dunni Feb 02 '21 at 11:02
  • Is there a way to make the autowired field visible? – user123rd Feb 02 '21 at 11:18
  • What do you mean with "visible"? – dunni Feb 02 '21 at 11:19
  • Sorry, how then can I access the autowired field? – user123rd Feb 02 '21 at 11:21
  • Have a look at the linked question from the first comment. Either the @Configurable or the manual lookup approach should work here. You can additionally create a Singleton adapter, which you let manage by Spring, inject the TransactionService there, and use that singleton in your Wallet class to get the transaction service instance. – dunni Feb 02 '21 at 11:23

1 Answers1

0

2 Reasons (1 definite and 1 is a probability)

1- The 1st definite is that you have a non-default constructor in your component

.....
public Wallet(String payload) {
    message = new JSONObject(payload);
}
...

Starting from Spring 4.3 constructors are used to inject components behind the scenes by Spring IOC so unless your constructor arguments are Spring components. so since your constructor here is a String a non-Spring component you must convert that constructor into a method something like

.....
public void init(String payload) {
    message = new JSONObject(payload);
    //... do some code here
}
...

2- Second reason but this is less probable is that your Wallet class does not get scanned by Spring Component-Scan dependency see more details in that post Why is my Spring @Autowired field null?

Youans
  • 4,801
  • 1
  • 31
  • 57
  • I agree that the lack of a default constructor for Wallet is a problem. But my experience has been that Spring would complain about not being able to instantiate a Wallet during app startup, and would fail the startup completely, if it were trying at all to do so. I think that the only way the app can initialize and run is if Spring isn't even trying to create a Wallet bean. And the only way I can see that that can happen is if Spring isn't scanning the definition of Wallet at all. So I think your second reason is very probable. – CryptoFool Feb 02 '21 at 07:47
  • If it wouldn't detect Wallet during component scan, it would throw a NoSuchBeanDefinitionException when initializing WalletProcessor, so that doesn't seem likely to me. – dunni Feb 02 '21 at 08:08
  • It could be, that WalletProcessor is initialized with the new keyword, though. – dunni Feb 02 '21 at 08:08
  • @dunni WalletProcessor is not initialized by the new keyword. – user123rd Feb 02 '21 at 09:02
  • Can you show the code part, where it is initialized or used? – dunni Feb 02 '21 at 09:04