1

It's kinda hard to explain... Hope the question isn't vague...

you can look at the code to get the idea...

ClassA.java

public class ClassA {
    @Autowired
    InterA abcd;
    public void dododo() {
        abcd.doit();
    }
}

ClassB.java

@Component
public class ClassB implements InterA {
    @Override
    public void doit() {
        System.out.println("hoo hoo");
    }
}

ClassC.java

@Component("classc")
public class ClassC {
    public void doFromAbove() {
        ClassA cls = new ClassA();
        cls.dododo();
    }
}

Interface InterA.java

public interface InterA {
    public void doit();
}

Configuration ClassConfig.java (on the same package of other java class files)

@Configuration
@ComponentScan
public class ClassConfig {
}

Main Method

public static void main(String[] args) {
    try(AbstractApplicationContext appctx = new AnnotationConfigApplicationContext(ClassConfig.class)) {
        ClassC obj = (ClassC) appctx.getBean("classc");
        obj.doFromAbove();
    }
}

When I execute the main method, the Autowired field "abcd" in ClassA didn't get injected and results in a NullPointerException

It works only when I declare ClassA as a @Component and get it's bean... indirect autowiring is not happening

Should I decouple ClassA from ClassC and make everything loosely coupled?

Is there any simple annotation that I can use to tell Spring auto inject the @Autowired field even when the object is created in a tight coupled fashion?

Note please don't tell me to use ApplicationContext in ClassC to create the bean of ClassA .

Any Spring Geek who could find an answer?

Crystal Paladin
  • 579
  • 5
  • 26
  • 3
    I think Spring 101 may well be appropriate in this case. So lets start there - if you are using `new` **anywhere** in your application your are _doing it wrong_. The **only** place one should see `new` in a Spring application is in the configuration class that explains to Spring how to create your beans. It's called a _Dependency Injection_ framework - you absolutely **must** _inject dependencies_ in order to use it. – Boris the Spider Jul 07 '17 at 06:49
  • 1
    Furthermore anyone who tells you to "_use `ApplicationContext` in `ClassC` to create the bean of `ClassA`_" also does not understand DI. You should never, ever do this either. The correct approach is to inject `Provider` and call `get` when you need one. You then tell Spring that `ClassA` is `PROTOTYPE` scoped so a new instance is created each time. As I say - Spring 101. – Boris the Spider Jul 07 '17 at 06:51
  • @BoristheSpider, I get your drift... The problem is what I'm going through is converting a traditional Java Project to Spring... So I thought maybe we can mix match tightly coupled objects with loosely coupled spring injected objects to finish the Spring Conversion/Migration Process with less code modification... The example I've described has only one tight coupling... in one level... Applying Spring the right way seems to be a big pain for existing projects – Crystal Paladin Jul 07 '17 at 08:00
  • What you can do is to autowire your `InterA` bean in `ClassC` and pass it to the `ClassA` Constructor like `ClassA cls = new ClassA(interA);` - not saying this is a good style but it works. – Jannik Weichert Jul 07 '17 at 08:39
  • @JannikWeichert sometimes one wishes one could downvote comments. Your suggestion works in this toy example - but is entirely useless in the general case. – Boris the Spider Jul 07 '17 at 22:01
  • @BoristheSpider I wouldn't suggest this solution in general - I think I made this clear. But it should be allowed to show all possibilities even if they do not match with your personal meaning. The questioner should be the one to decide what is useful for him. – Jannik Weichert Jul 07 '17 at 22:33
  • @JannikWeichert wiring the dependencies for a downstream dependency into an upstream one to pass them through is an awful idea. Not least because then the scope of the wired dependencies becomes the scope of where they are wired, unless scoped proxies are used. – Boris the Spider Jul 08 '17 at 11:12
  • @BoristheSpider, check out the Accepted Answer for this question and acknowledge if its a right one... hope you don't mind... – Crystal Paladin Jul 13 '17 at 06:01

5 Answers5

1

The issue is in ClassC:

    ClassA cls = new ClassA();

If you invoke the constructor of ClassA like this, Spring won't do its magic. If you need an instance of ClassA with injected fields, ask Spring for an instance (using injection or getBean()).

(To avoid having null fields where injection is assumed, I recommend using constructor injection.)

C-Otto
  • 5,615
  • 3
  • 29
  • 62
  • 1
    Ctor injection doesn't create a new instance each time `doFromAbove` is called, unless you go into the realms of scoped proxies. – Boris the Spider Jul 07 '17 at 06:53
  • Isn't that the same with field injection? – C-Otto Jul 07 '17 at 06:58
  • Yes, but my point is that "_to work around such issues, I recommend using constructor injection_" is not a helpful statement. – Boris the Spider Jul 07 '17 at 06:58
  • This solves the issue of ending up with `null` fields where injection is assumed. – C-Otto Jul 07 '17 at 07:02
  • It does not implement the intent of the OPs code. It will use the same instance of `ClassA` for every invocation. Presumably the reason the OP did not use field/setter/ctor injection (which is used elsewhere in the code) is specifically because a new instance of `ClassA` is required at every invocation. In order for this answer to be helpful, you would need to describe how to solve the problem the OP is trying to solve... – Boris the Spider Jul 07 '17 at 07:03
  • 1
    Actually @BoristheSpider is right... I need fresh instance of ClassA everytime in that line... using a `Prototype` Scope as suggested by @BoristheSpider gets me halfway through – Crystal Paladin Jul 07 '17 at 07:39
1

After intense googling, Spring Documentation Skimming, I'm convinced there are more possible solutions to this dilemma...

Possible Solutions:

  1. Use JSR 330 Provider<T> with @Autowired
  2. Use FactoryBean<T> with initialization code in getObject() (but the bean returned by the factory is not spring managed and thus any autowired field in the prototype class will return NullPointerException)
  3. Use Lookup Method Injection(include CGLIB library)(I don't prefer this, as it modifies compiled code and sounds like it creates bean objects of Abstract classes)(Java's Purity is violated)
  4. Implement ApplicationContextAware interface and get the context object (not recommended)
  5. Autowire ApplicationContext and use getBean() (not recommended)

Most Subtle approach among the above is JSR330 Provider

ClassA

@Component("classa")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClassA implements InterB {
    private static int counter=0;

    private int objectid = 0;
    @Autowired
    InterA abcd;

    public ClassA() {
        super();
        this.objectid = ++counter;
    }

    @Override
    public void dododo() {
        System.out.println("instance number "+objectid++);
        abcd.doit();
    }
}

ClassB

@Component
public class ClassB implements InterA {
    @Override
    public void doit() {
        System.out.println("hoo hoo");
    }

}

ClassC

@Component("classc")
public class ClassC {

    @Autowired
    Provider<InterB> classAPrototypeobj;

    public void doFromAbove() {
        //you can do a for loop here and get a set of objects for use
        InterB cls = (InterB) classAPrototypeobj.get();
        InterB cls1 = (InterB) classAPrototypeobj.get();
        cls.dododo();
        cls1.dododo();
        System.out.println(cls);
        System.out.println(cls1);
    }
}

Now it works flawlessly and the initialized object is spring managed too...

Note: JSR330 dependency has to be set in maven pom.xml

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
Crystal Paladin
  • 579
  • 5
  • 26
  • Perfect - well done on the research. This is exactly the correct approach. You shouldn't need the casting, as the `Provider` is generic. – Boris the Spider Jul 18 '17 at 14:58
0

The beans declared within Spring container (either through XML or annotations like @Component) are Spring-managed - Spring will take care of them, will make sure they exist when you request them via ApplicationContext.getBean() and that their dependencies are injected as well.

When you create an instance yourself (cls = new ClassA()), that instance is not Spring-managed and Spring will not do anything to it. In fact, Spring won't (and cannot) even know the object exists.

Some confusion may stem from that you annotate the class with the Spring annotations - but it's really objects (instances) that are actually used in Java; even if the class is annotated, the annotations will only apply on instances that are created and managed by Spring.

Jiri Tousek
  • 12,211
  • 5
  • 29
  • 43
0

You could use @Configurable if you enable load time weaving and that would make each new instance of the object a managed spring component, so the new statement you used will work.

Besides this you could create a prototype scoped bean definition and a factory bean that would reference that bean, meaning it will give you a new bean each time, so you would inject the factory and only call the get method for a new instance.

raduone
  • 191
  • 1
  • 10
  • Care to give me a simple example with `@Configurable` and FactoryBeans ? If possible, use the sample code in the question so it'll benefit anyone who stumbles on the same. This might be the answer I've been looking for... But I need to see to make sure... If this works, I'll award +50 bounty – Crystal Paladin Jul 11 '17 at 08:18
  • This is a standalone basic command-line java program with `public static void main` thingy... will LTW even work on it? I assume it works only if its a web app loaded in a container like tomcat... But I could be wrong... enlightenment please... – Crystal Paladin Jul 11 '17 at 08:27
0

Why not use @Lookup annotation? Based on the accepted answer, I assume you require a new instance of ClassA every time in ClassC.

@Component("classA")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClassA {
    @Autowired
    InterA abcd;

    private ObjectA objA;

    private ObjectB objB;

    public ClassA(objA, objB) {
        this.objA = objA;
        this.objB = objB;
    }

    public void dododo() {
        abcd.doit();
    }
}

@Component("classc")
public class ClassC {
    public void doFromAbove() {
        ClassA cls = getNewInstanceOfClassA(objA, objB);
        cls.dododo();
    }

    @Lookup("classA")
    private getNewInstanceOfClassA(ObjectA objA, ObjectB objB) {
        //Spring creates a runtime implementation of this method
        return null;
    }
}

The rest of the class implementations remain the same. I have included objA and objB in the implementation for some clarity on injecting constructor arguments.

So the main method gets a spring bean of classC and calls doFromAbove() method. This in turn calls getNewInstanceOfClassA method which returns a spring bean of type classA with the constructor arguments objA, objB. Since we annotated it as prototype bean, we get a new instance of classA every time this method is invoked. You don't need to implement getNewInstanceOfClassA method. Spring adds it's own code during the runtime.

Essentially your problem boils down to injecting a prototype bean in a singleton bean. Lookup annotation is the best way to solve that issue.

yaswanth
  • 2,349
  • 1
  • 23
  • 33
  • Except that @Lookup (as I've mentioned in my answer point number (3) ) uses CGLib to change the Java Intermediate Bytecodes with a subclass.. (Java's purity is violated) Now you might feel its okay to extend the java class and return a bean which is s subclass of what I intended.. But it feels kinda off – Crystal Paladin Jul 20 '17 at 07:43
  • Spring extends the Bean class you've created with a subclass and overrides the method... It just doesn't seem right... – Crystal Paladin Jul 20 '17 at 07:44
  • I am not sure what you mean by Java's purity is violated. What is the downside you have if CGLib adds byte code? You are able to do what you want (get a prototype bean) in a much cleaner and concise way using Lookup annotation. Spring uses CGLib at many other places. Transactional annotation, spring-data-jpa, Configuration annotation and a lot of other spring features internally use CGLib or JDK Dynamic Proxy. – yaswanth Jul 20 '17 at 08:39