1

My Config class:

@Configuration
public class TimeOutConfig {

    @Value("${value1}")
    private int value1;

    @Value("${value2}")
    private int value2;

    @Bean()
    User boTimeOut() {
        System.out.println("In BOTimeOutConfig");
        BeanTest b1 = new BeanTest(value1, value2);
        User u1 = new User(b1);
        return u1;
    }


    @Value("${value3}")
    private int value3;

    @Value("${value4}")
    private int value4;

    @Bean(name = "BSETimeout")
    User getBSEUser() {
        System.out.println("In BSETimeOutConfig");
        User u2 = new User( new BeanTest(value3, value4));
        return u2;
    }

}

BeanTest Class:

@Component
public class BeanTest {

    private int v1;
    private int v2;

   BeanWebClient b1;

    public BeanTest(){}

    public BeanTest(int v1, int v2){
        this.v1=v1;
        this.v2=v2;
        beanWebClient();
        
    }
    public BeanWebClient beanWebClient() {
        //System.out.println("In BeanTest class beanWebClient() method execution v1 and v2: "+v1+v2);
        b1 = new BeanWebClient(v1,v2);
        System.out.println("In BeanTest class beanWebClient() method execution v1 and v2: "+b1.getV1()+" "+b1.getV2());
        return b1;
    }

    public void message()
    {
        b1.webClientMessage();
        //System.out.println("V1= "+v1+" V2= "+v2);
    }
}

BeanWebClient Class:

@Component
public class BeanWebClient {

    private int v1;
    private int v2;
    public BeanWebClient(int v1, int v2){

        this.v1=v1;
        this.v2=v2;
        System.out.println("Received from BeanTest "+v1+" "+v2);
    }

    public BeanWebClient(){

    }

    public int getV1() {
        return v1;
    }
    public int getV2() {
        return v2;
    }

    public void webClientMessage(){

        System.out.println("In BeanWebClient class, printing from autowired dependency "+v1+" "+v2);
    }

}

My User class:

@Component
public class User {

    @Autowired
    private BeanTest beanTest;



    public User(BeanTest beanTest){

        this.beanTest=beanTest;
    }

    public void message()
    {

        beanTest.message();
    }


    public User() {

        //System.out.println("user create... hashCode :" + this.hashCode());
    }
}

Finally, My Service class:

@Service
public class BeanService {

    @Autowired
    @Qualifier("boTimeOut")
    private User boUser;

    @Autowired
    @Qualifier("BSETimeout")
    private User bseUser;

    public void printBO()
    {
        boUser.message();

    }
    public void printBSE()
    {
        bseUser.message();
    }

}

I was trying to understand how components and autowiring dependencies work in spring boot. In my User class, the autowired BeanTest class is initialized, during configuration- all the necessary print statements are executed. But when I call the functions of the Service class, The autowired User dependency has null for beanTest and its members. As opposed to this, when I just create the BeanTest object in my User class everything works as expected. This is what I mean:

@Component
public class User {

    BeanTest beanTest;



    public User(BeanTest beanTest){

        this.beanTest=beanTest;
    }

    public void message()
    {

        beanTest.message();
    }


    public User() {

        //System.out.println("user create... hashCode :" + this.hashCode());
    }
}

I sort of understand that The bean autowired in User class is taken from the container is null. Since Im Autowiring specific User dependency in my service class, I don't understand why it's not working. Can someone please explain If and How I can autowire the BeanTest class into User class correctly. Sorry for so many code files.

Outputs This is the Output I get When I create the BeanTest object manually without @Autowire

In BOTimeOutConfig
Received from BeanTest 10 20
In BeanTest class beanWebClient() method execution v1 and v2: 10 20
In BSETimeOutConfig
Received from BeanTest 30 40
In BeanTest class beanWebClient() method execution v1 and v2: 30 40
2021-11-13 14:37:58.161  WARN 404383 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2021-11-13 14:37:58.613  INFO 404383 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-11-13 14:37:58.622  INFO 404383 --- [           main] c.paytmmoney.errordb.ErrordbApplication  : Started ErrordbApplication in 4.04 seconds (JVM running for 4.379)
2021-11-13 14:38:07.768  INFO 404383 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-11-13 14:38:07.768  INFO 404383 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2021-11-13 14:38:07.769  INFO 404383 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
In /test method
In BeanWebClient class, printing from autowired dependency 10 20
In /test method
In BeanWebClient class, printing from autowired dependency 30 40

When I autowire BeanTest Class this is the Output:

In BOTimeOutConfig
Received from BeanTest 10 20
In BeanTest class beanWebClient() method execution v1 and v2: 10 20
In BSETimeOutConfig
Received from BeanTest 30 40
In BeanTest class beanWebClient() method execution v1 and v2: 30 40
2021-11-13 14:40:36.072  WARN 404724 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2021-11-13 14:40:36.585  INFO 404724 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-11-13 14:40:36.596  INFO 404724 --- [           main] c.paytmmoney.errordb.ErrordbApplication  : Started ErrordbApplication in 4.353 seconds (JVM running for 4.75)
2021-11-13 14:40:45.081  INFO 404724 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-11-13 14:40:45.081  INFO 404724 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2021-11-13 14:40:45.082  INFO 404724 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
In /test method
2021-11-13 14:40:45.204 ERROR 404724 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
    at com.paytmmoney.errordb.repository.BeanTest.message(BeanTest.java:42) ~[main/:na]
    at com.paytmmoney.errordb.repository.User.message(User.java:22) ~[main/:na]
    at com.paytmmoney.errordb.service.BeanService.printBO(BeanService.java:21) ~[main/:na]

I'm using a rest controller to call functions in the Service class.
THANK YOU!!!

Aditya K
  • 135
  • 2
  • 4
  • 10
  • 2
    You are mixing field injection (@Autowired) and constructor injection. You project looks messy. Can you please provide a minimal reproducible example on Github? – Simon Martinelli Nov 13 '21 at 10:04
  • @SimonMartinelli I've added the code here - https://github.com/Aditya149349/StackOverflow Really appreciate your help! – Aditya K Nov 13 '21 at 10:32
  • 2
    There are a lot of issue. Why are you using Qualifiers in the service? – Simon Martinelli Nov 13 '21 at 11:11
  • I have to agree with @SimonMartinelli, your code is hard to follow. You create `User` beans in your `TimeOutConfig` file, but then you also annotate the `User` class with `@Component` (actually "creating" a bean). Additionally, the issue does not seem to be that your `beanTest` is null in your `User` object. The problem seems to be in the `BeanTest.message()` method. Most probably `b1` is null. Have you tried removing `@Component` annotation from `BeanTest`, `BeanWebClient` and `User` classes as they are irrelevant for what it seems you are trying to do. – João Dias Nov 13 '21 at 12:26
  • @JoãoDias If I remove the `@Component` annotation then I won't be able to inject them using `@Autowired ` right? – Aditya K Nov 13 '21 at 12:55
  • Yes, but you are not injecting them using `@Autowired`. You are creating all the objects in `User boTimeOut()` and `User getBSEUser()` methods in `TimeOutConfig` class. – João Dias Nov 13 '21 at 12:58
  • The reason It's so messy is that I'm trying to replicate the code flow of another file. I'm supposed to make changes in another code repository and it has the similar code flow. I just made another project and sort created these files to understand what's happening. The print statements help me track when the `@Configuration` class gets executed during application startup. – Aditya K Nov 13 '21 at 12:58
  • @JoãoDias right okay. So I face an issue only when I inject BeanTest through `@Autowied` else, as shown in the last code example, everything works fine. But I feel they expect me to use inject all my dependencies through spring – Aditya K Nov 13 '21 at 13:01
  • But if that is the case you can't have multiple `User` beans with annotations only. – João Dias Nov 13 '21 at 13:03
  • @JoãoDias can you suggest any alternative to this? – Aditya K Nov 13 '21 at 13:05
  • 1
    Sorry, but no, because I don't know what is expected since you gave no context about your code. – João Dias Nov 13 '21 at 13:08
  • @SimonMartinelli I want each BeanTest class, that will be an object in User, to have different values for its member variables. But the flow should access these variables only through `User` class and its functions. Therefore created two `Beans` of `User` class each taking different set of variables. – Aditya K Nov 13 '21 at 13:08
  • Just a note: https://stackoverflow.com/questions/52355132/spring-autowired-on-a-class-new-instance#comment91655379_52355132 This thread is sort of similar to my problem. But I'm still not sure what to do – Aditya K Nov 13 '21 at 13:24
  • I think the issue is that I'm autowiring BeanTest and expecting to have initialized member variables without actually creating a bean of type BeanTest – Aditya K Nov 13 '21 at 13:58

1 Answers1

0

Given that you are creating BeanTest and User instances on your own in TimeOutConfig, please remove all @Component and @Autowired annotations from BeanTest, BeanWebClient and User as follows:

public class BeanTest {

    private int v1;
    private int v2;
    BeanWebClient b1;

    public BeanTest(){}

    public BeanTest(int v1, int v2){
        this.v1=v1;
        this.v2=v2;
        beanWebClient();
    }
    
    public BeanWebClient beanWebClient() {
        //System.out.println("In BeanTest class beanWebClient() method execution v1 and v2: "+v1+v2);
        b1 = new BeanWebClient(v1,v2);
        System.out.println("In BeanTest class beanWebClient() method execution v1 and v2: "+b1.getV1()+" "+b1.getV2());
        return b1;
    }

    public void message() {
        b1.webClientMessage();
        //System.out.println("V1= "+v1+" V2= "+v2);
    }
}
public class BeanWebClient {

    private int v1;
    private int v2;
    
    public BeanWebClient(int v1, int v2) {}
        this.v1=v1;
        this.v2=v2;
        System.out.println("Received from BeanTest "+v1+" "+v2);
    }

    public BeanWebClient(){ }

    public int getV1() {
        return v1;
    }
    
    public int getV2() {
        return v2;
    }

    public void webClientMessage(){
        System.out.println("In BeanWebClient class, printing from autowired dependency "+v1+" "+v2);
    }
}
public class User {

    private BeanTest beanTest;

    public User(BeanTest beanTest) {
        this.beanTest=beanTest;
    }
    
    public User() {
        //System.out.println("user create... hashCode :" + this.hashCode());
    }

    public void message() {
        beanTest.message();
    }
}
João Dias
  • 16,277
  • 6
  • 33
  • 45
  • But then I won't be able to inject `BeanTest` dependecy into `User` using `@Autowired` right? I'll have to manually create the object as you've shown, in your answer, the `User` class – Aditya K Nov 13 '21 at 15:44
  • Sure, but isn't exactly that what you are doing in your `TimeOutConfig` class? You are creating all the instances on your own and registering two `User` instances as Spring-managed Beans that can be later injected in your `BeanService`. – João Dias Nov 13 '21 at 15:45
  • Yes. But I thought I at the time of configuration itself, I could initialize an instance of `BeanTest` into `User` class. That works when I create the member function in `User` as `private BeanTest beanTest` but **NOT** when I use `@Autowired private BeanTest beanTest`. Now my question is, at the time of configuration, If I use @Autowired for to inject BeanTest in User, it gets initialized But in Service class when I check the `User` class's member functions, `beanTest` is null. I'm wondering why it gets initialized during configuration but shows null when `User` is used in the service class – Aditya K Nov 13 '21 at 17:00
  • But that is the thing, it is not that `beanTest` is null. What is null is `BeanWebClient b1`, because in that case the `BeanTest` instances are created using the default no-args constructor that does not call `beanWebClient()`. Additionally, as I said before, you can't register two `User` instances using only `@Component` on `User` class and thus you must do it as my answer shows in the `TimeOutConfig` class. – João Dias Nov 13 '21 at 17:24
  • right, I'm starting to understand the point when you mentioned default no-args constructor. Yeah I realise there is no point using @Component to User class. Thanks for the help. Is there any way I can use @Autowired to inject and Instantiate `BeanTest` in `User`. Cause for the use-case I'll need to use `BeanWebClient` functions using `User` through `beanTest` and as mentioned I have two different set of values, each `User` Bean deals with one set of values. **Thanks again for being patient and replying to everything** – Aditya K Nov 13 '21 at 17:42
  • If you need to have multiple `User`s you will never be able to use `@Autowire` to inject an instance of `BeanTest`. You will always have to instantiate them "manually" and register them as my answer shows in `TimeOutConfig`. I am not really understanding the problem of this approach since it actually works. If my answer has been so for useful you might consider upvoating it ;) – João Dias Nov 13 '21 at 17:48
  • Alright. Thanks for the clarification. There’s no problem as such, as you said “manually” creating and using the object works. I just always assumed that it’s bad practice when using spring boot to deal with objects this way. – Aditya K Nov 13 '21 at 17:53
  • No, it is not a bad practice and in your case, you have really no other option, because you want to have two different Spring-managed beans of the `User` class. If my answer helped you consider upvoting it and even accepting it as the answer so that it can be ”closed” and others can benefit from it and easily understand which could be a possible solution for similar questions ;) Thanks! – João Dias Nov 13 '21 at 17:59