1

Suppose the bean is registered here.

@Configuration
public class configuration{

    @Bean(name = "MyClass")
    public MyClass getMyClass(@Value("avalue") final String s){
        return new MyClass(s);
        }
    }
}

And the class has another class instantiated inside as below (No annotation needed for either?)

public class MyClass{
    MyOtherClass myOtherClass;
    public MyClass(String s){
        this.myOtherClass = new MyOtherClass(s);
    }
}

public class MyOtherClass{
    String s;
    public MyOtherClass(String s){
        this.s = s;
    }
}

How this mechanic works? Is it safe to instantiate a new instance within a bean injection? What's the advantage or disadvantage of doing so?

If we were to avoid using it by making MyOtherClass a bean too, would this be the way? (Mainly concerning: is @Autowired and getMyclass() taking the right place or it's redundant?)

@Configuration
public class configuration{

    @Bean(name = "MyOtherClass")
    public MyOtherClass getMyOtherClass(@Value("avalue") final String s){
        return new MyOtherClass(s);
        }
    }

    @Bean(name = "MyClass")
    public MyClass getMyClass(MyClass myClass){
    //I was told the input should be myClass not myOtherClass,
    //and the return is myClass not new MyClass(myOtherClass);
    //since it's already autowired. Is that correct?
    //What if the configuration.java and MyClass.java are in different project?
        return myClass;
        }
    }
}

@Component
public class MyClass{
    MyOtherClass myOtherClass;

    @Autowired
    public MyClass(MyOtherClass myOtherClass){
        this.myOtherClass = myOtherClass;
    }
}

public class MyOtherClass{
    String s;
    public MyOtherClass(String s){
        this.s = s;
    }
}
user6219476
  • 89
  • 1
  • 11

2 Answers2

1

Is it safe to instantiate a new instance within a bean injection?

Yes. However, that new instance is not managed by Spring. (Sorry if that's obvious.)

But perhaps more importantly and unrelated to Spring, doing this:

public class MyClass{
    MyOtherClass myOtherClass;
    public MyClass(String s){
        this.myOtherClass = new MyOtherClass(s);
    }
}

is difficult to unit test since there is no easy way to mock myOtherClass.

Still unrelated to Spring, refactor to use IoC to make it easy to unit test:

public class MyClass{
    private final MyOtherClass myOtherClass;
    public MyClass(MyOtherClass myOtherClass){
        this.myOtherClass = myOtherClass;
    }
}

And to configure the class as a Spring bean, but without having a MyOtherClass also as a bean:

@Configuration
public class Configuration{

    private MyOtherClass getMyOtherClass(final String s){
        return new MyOtherClass(s);
        }
    }

    @Bean(name = "MyClass")
    public MyClass getMyClass(@Value("avalue") final String s){
        return new MyClass(getMyOtherClass(s));
        }
    }
}

What you posted about @Component and @Autowire would work, but it sounded like you wanted to avoid exposing MyOtherClass as a bean.

Andrew S
  • 2,509
  • 1
  • 12
  • 14
1

If you want not to expose your MyOtherClass and take control over creating its instances, you can use your first approach:

...
public MyClass(String s) {
    this.myOtherClass = new MyOtherClass(s);
}

It is safe, but it is not a dependency injection: MyOtherClass is not managed by Spring, so you won't be able to inject it in other beans using @Autowired or to inject other spring managed classes into MyOtherClass. Also, as mentioned by Andrew, it becomes impossible to mock MyOtherClass.

If registering MyOtherClass as a bean in a Spring context is fine, then the easiest way is to annotate both classes with @Component and let Spring do its work (in this case you should use @ComponentScan to help Spring auto-detect your classes):

@Component
class MyClass {
    MyOtherClass myOtherClass;
    @Autowired
    public MyClass(MyOtherClass myOtherClass){
        this.myOtherClass = myOtherClass;
    }
}

@Component
class MyOtherClass {
    String s;
    public MyOtherClass(@Value("avalue") final String s){
        this.s = s;
    }
}

Or, using @Bean (this will work if Config.java and MyClass.java are in different projects):

@Configuration
public class Config {

    @Value("avalue")
    private String s;

    @Bean(name = "myClass")
    public MyClass getMyClass(){
        return new MyClass(getMyOtherClass());
    }
    @Bean(name = "myOtherClass")
    public MyOtherClass getMyOtherClass() {
        return new MyOtherClass(s);
    }
}

Note: if you have already registered MyClass in a Spring context using

@Component
public class MyClass {
    ...
}

then you don't need to do it again by

@Bean(name = "MyClass")
public MyClass getMyClass(MyClass myClass) {
    return myClass;
}
Kirill Simonov
  • 8,257
  • 3
  • 18
  • 42