0

I have a service which needs to create Agents on the runtime. Agents inherit from a base Agent class. I would like to use the Autowired ability of spring instead of doing my own dependency injections.

But I am running into this issue, even though I am marking the component as scope=prototype, and even @Lazy to prevent anything from happening at compile-time.

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.my.project.AgentType1 required a bean of type 'com.my.project.POJO' that could not be found.

This is the service that tries to create the agents:

@Service
public class ProjectMain {
   @Autowired
   ApplicationContext context;

   List<IAgent> agents = new ArrayList<>();

   void SetupAgents(List<POJO> agentPojos) {
       for(POJO agentPojo: agentPojos) {
          IAgent agent = AgentFactory.CreateAgent(agentPojo, context);
          agents.add(agent);
       }
   }
}

This is the factory class, not marked as @Component etc. It uses the context passed to it to create the child class beans. It tries to pass the constructor argument via the getBean method.

public class AgentFactory {
  public static IAgent CreateAgent(POJO agentPojo, ApplicationContext context) {
         if (agentPojo.type.equals("AgentType1")) {
              return context.getBean(AgentType1.class, agentPojo);
         } else {
              return context.getBean(AgentType2.class, agentPojo);
         }
   }
}

This is a custom annotation which I found is needed for inheritance scenarios.

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Component
@Inherited
@Lazy
@Scope("prototype")
public @interface AgentAnnotation {}

These are the base and child agent classes, which need a custom data structure called POJO to work.

@AgentAnnotation
public class BaseAgent implements IAgent {

   @Autowired
   Environment env;

   public BaseAgent(POJO agentPojo, String someotherdata) {

   }

}

public class AgentType1 extends BaseAgent {

   public AgentType1(POJO agentPojo) {
      super(agentPojo, "mydata1");
      ...
   }
}

public class AgentType2 extends BaseAgent {

   public AgentType2(POJO agentPojo) {
      super(agentPojo, "mydata2");
      ...
   }
}

This is the starter app.

@ComponentScan(basePackages = "com.my.project", includeFilters = @ComponentScan.Filter(AgentAnnotation.class))
@EnableScheduling
@SpringBootApplication
public class MyApplication {

   public static void main(String[] args) {
       SpringApplication.run(MyApplication.class, args);
   }
}

I also tried the configuration approach:

@Configuration
public class BaseAgentConfig {
    @Bean
    @Scope("prototype")
    public AgentType1 agentType1(POJO agentPojo) {
         return new AgentType1(agentPojo);
    }

    @Bean
    @Scope("prototype")
    public AgentType2 agentType2(POJO agentPojo) {
         return new AgentType2(agentPojo);
    }
}

In this case, I removed the @AgentAnnotation from the baseAgent class as we are now instantiating through this config. Also removed the ComponentScan line from the main App.

This time around, the @Autowired doesn't work. All Autowired references in the baseAgent class are null.

Please advise on the best approach to solve this error. Thanks.

Raza Ali
  • 95
  • 2
  • 10

1 Answers1

1

Found the issue and solution.

Basically, I was expecting child classes to inherit @Component and @Scope, which it doesn't.

So essentially, I need to annotate each child class with @Component and @Scope("prototype").

The other problem was that I was expecting Autowired items in the constructor, which was too early. Adding a @PostConstruct addressed that issue.

So I ended up deleting the custom annotation and the configuration class and making the changes I just described.

Raza Ali
  • 95
  • 2
  • 10
  • I also have got similar error. I have implemented custom starter and I am autowiring it in my controller class. If I remove `@ComponentScan`, it gives me "fooCaller required as bean of type ' ' that could not be found." – Amit Feb 20 '18 at 07:30
  • @Amit ComponentScan is ok to keep, and is not related to the above issues I faced. Not sure what your question is but maybe if you clarify further I may be able to pitch in. – Raza Ali Feb 20 '18 at 22:09
  • https://stackoverflow.com/questions/48880240/spring-boot-custom-starter-application-doesnt-find-required-bean is my question. Our user of custom starter doesn't want to care the package they have to scan, so we tried custom starter. – Amit Feb 21 '18 at 00:57
  • I haven't worked with custom starters, but in my experience ComponentScan issues are related to how packages are organized. So if the main App has @ComponentScan on it and everything else is in an inner package hierarchy, it works. – Raza Ali Feb 21 '18 at 20:52