Search code examples
javacdiweld-se

Do i need to make all object instantiation via producer in CDI


I just start a CDI project. In this project a Beans2 is injected inside a Beans1. But the Beans2 has a method that create a file. This method instantiates the File Object like this :

new File('myPathFile');

Because this instantiation is not managed by the CDI container, Bean2 is not injected into Beans1. I try to make a producer to inject a File into the Beans2 but do i need to do the same thing for all java base class that i will use?

Is there another solution to simply use class that do not need to be inject?

Bean1 :

@Dependant
public class Bean1 implements Serializable {
    private @Inject @Bean2Producer Bean2 bean2;

    public void someMethod() {
        bean2.foo();
    }
}

Bean2 :

@Dependant
public class Bean2 extends AbstractClass implements Serializable {

    private @Inject @PathDir String pathDir;


    public Bean2(String param1, boolean param2) {
         super(param1, param2);
    }

    public void foo() {
        File file = new File(pathDir);
    }
}

pathDir Producer :

@ApplicationScoped
public class ProjectProducer implements Serializable {
    @Produces
    @PathDir
    public String getPathDir() {
        try {
             return PropsUtils.geetProperties().getProperty(PATH_DIR);
        } catch (Exception e) {
             e.printStackTrace();
        }
    }
}

PathDir annotation :

@Qualifier
@Target({FIELD, METHOD, PARAMETER, CONSTRUCTOR, TYPE})
@Retention(RUNTIME)
@Documented
public @interface PathDir {}

In this example, PathDir is not Inject if new File is invoke in foo() method.


Solution

  • The reason why Bean2 is not injected into Bean1 is that there is no suitable constructor in Bean2 so that CDI can automatically create an instance of it. Beans need to have a constructor with no parameters or a constructor with all parameters injected, see more in the Java EE tutorial

    This is simple the limitation of CDI. If you want to pass arguments to your bean, you need to supply a setter method(s) to pass arguments after bean2 is injected. Or, in case you need to have some values during object creation (because the abstract parent requires them), you need to inject all constructor arguments, like this (@Param1 and @param2 are qualifiers):

    public Bean2(@Inject @Param1 String param1, @Inject @Param2 boolean param2) {
         super(param1, param2);
    }
    

    Alternatively, you do not need to turn every object into a CDI bean, especially if you have requirements on constructor parameters. You may inject all dependencies manually after the bean is created. It means you do use @Inject in Bean2, but provide a setter method, inject into bean1 and set the value in postconstruct method of bean1, example follows:

    Bean1 :

    @Dependant
    public class Bean1 implements Serializable {
    
        private @Inject @PathDir String pathDir; // inject pathDir here instead of in Bean2
    
        private Bean2 bean2; // without inject, created in init()
    
        @PostConstruct
        public void init() {
            bean2 = new Bean2("param1", "param2");
            bean2.setPathDir(pathDir);  // set injected value manually
        }
    
        public void someMethod() {
            bean2.foo(); // here bean2.pathDir should be already initialized via setPathDir in init() method above
        }
    }
    

    Bean2 :

    @Dependant
    public class Bean2 extends AbstractClass implements Serializable {
    
        private String pathDir; 
    
        public Bean2(String param1, boolean param2) {
             super(param1, param2);
        }
    
        public void setPathDir(String pathDir) {
            this.pathDir = pathDir;
        }
    
        public void foo() {
            File file = new File(pathDir);
        }
    }
    

    Or even better, merge setPathDir and Bean2 constructor - it will be clear that pathDir is a required dependency for Bean2.