vendredi 4 décembre 2015

Guice Injection While Using Reflection to Call Multiple Classes

I have encountered a rather interesting problem when it comes to Injection and using Reflection to call Multiple Classes.

Background

Report Param Definition table contains rows of reports. One of the columns contains the full package and class name.

ReportDef object contains the report parameter definitions

ReportRequest object contains report related information, things like what month do you need a report for, format of the report (pdf, txt etc) and other related information that the data collectors need to make the proper DB queries.

ReportHandler this class is what does reflection calls. Calls the appropriate DataCollector and then runs a report based on the data collected. ReportHandler does have an @Inject constructor.

ReportDataCollector Interface that all DataCollectors implement. It contains the method getReportData.

EligibilityDataCollector Implements the ReportDataCollector. The purpose of this class is to collect all the data relating to the Eligibility report and adds things to an Eligibility object which then the ReportHandler uses to run the reports

XXXXXCalcs Multiple calcs classes that handle calculations. The constructors have injection for various reasons but mainly it's to be able to make the appropriate Database calls (through a DAO layer)

Design

I discussed this in the background section, but to make things clear the flow is like this

ReportHandler > xxxxDataCollector > xxxxxCalcs

Code

ReportHandler

// get class name based on the Report Def object. The String returned
// from getReportImplementationClass is the full package + class name
Class<?> cls = Class.forName(reportDefs.getReportImplementationClass());

// create new instance of that class
Object obj = cls.newInstance();

// Cast it to ReportDataCollector to ensure that getReportData exists
// note this is in a try / catch and will catch any issues that might arise
// here
ReportDataCollector reportObj = (ReportDataCollector) obj;

// Get the report data needed
birtGroupList = reportObj.getReportData(reportRequest, emProvider);

// Run the report with that data returned
if (birtGroupList != null) {
    result = runner.runReport(birtGroupList, reportRequest);
}

EligibilityDataCollector

The specific code is irrelevant, but basically getReportData ends up coordinating all the information needed. Part of that information is to call a calcs class. The Data Collector knows what data it needs so it calls specific calcs classes.

xxxxxCalcs

This class has a injection constructor.

The Question

The report code up until now did not have to call any calcs classes so calling classes through reflection that need injection was not an issue. cls.newInstance() worked so everything went through ReportHandler to xxxxDataCollector with no isuses.

The thing that I want to do is through reflection call the EligibilityDataCollector constructor and inject xxxxCalcs instance so I can perform the calculations that I need.

So I need the ReportHandler to figure out if there is a non empty constructor and then call it providing the injected information it needs.

Alternatively on the EligibilityDataCollector level if I could create or grab the injection data and then call xxxxCalcs that would work as well.

One of the problems is I can't just instantiate the xxxCalcs object with "new" since its constructor also expects injection, those objects also have constructors with injection...so it very quickly becomes very messy. That's not even counting the fact that I would be breaking the overall design that expects injection throughout the project.





Aucun commentaire:

Enregistrer un commentaire