Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional injection of bean

I want to have inject a bean based on a String parameter passed from client.

public interface Report {
    generateFile();
}

public class ExcelReport extends Report {
    //implementation for generateFile
}

public class CSVReport extends Report {
    //implementation for generateFile
}

class MyController{
    Report report;
    public HttpResponse getReport() {
    }
}

I want report instance to be injected based on the parameter passed. Any help would be greatly appretiated. Thanks in advance

like image 656
Abhijith Prabhakar Avatar asked Sep 24 '11 07:09

Abhijith Prabhakar


People also ask

What is conditional bean?

Why do we need Conditional Beans? A Spring application context contains an object graph that makes up all the beans that our application needs at runtime. Spring's @Conditional annotation allows us to define conditions under which a certain bean is included into that object graph.

What is the use of conditional annotation?

The @Conditional annotation may be used in any of the following ways: as a type-level annotation on any class directly or indirectly annotated with @Component , including @Configuration classes. as a meta-annotation, for the purpose of composing custom stereotype annotations.

Can we use @qualifier with @bean?

NOTE: if you are creating bean with @Bean, it will be injected byType if there is duplicates then it will injected byName. we no need to mention @Bean(name="bmwDriver") . so you can directly use qualifier("bmwDriver") wherever you need in classes.

How do you use a Spring conditional bean?

Conditions based on a Bean definition are present in Spring Application context. Conditions based on a Bean object are present in Spring Application context. Conditions based on some or all Bean properties values. Conditions based on some Resources are present in current Spring Application Context or not.


1 Answers

Use Factory method pattern:

public enum ReportType {EXCEL, CSV};

@Service
public class ReportFactory {

    @Resource
    private ExcelReport excelReport;

    @Resource
    private CSVReport csvReport

    public Report forType(ReportType type) {
        switch(type) {
            case EXCEL: return excelReport;
            case CSV: return csvReport;
            default:
                throw new IllegalArgumentException(type);
        }
    }
}

The report type enum can be created by Spring when you call your controller with ?type=CSV:

class MyController{

    @Resource
    private ReportFactory reportFactory;

    public HttpResponse getReport(@RequestParam("type") ReportType type){
        reportFactory.forType(type);
    }

}

However ReportFactory is pretty clumsy and requires modification every time you add new report type. If the report types list if fixed it is fine. But if you plan to add more and more types, this is a more robust implementation:

public interface Report {
    void generateFile();
    boolean supports(ReportType type);
}

public class ExcelReport extends Report {
    publiv boolean support(ReportType type) {
        return type == ReportType.EXCEL;
    }
    //...
}

@Service
public class ReportFactory {

    @Resource
    private List<Report> reports;

    public Report forType(ReportType type) {
        for(Report report: reports) {
            if(report.supports(type)) {
                return report;
            }
        }
        throw new IllegalArgumentException("Unsupported type: " + type);
    }
}

With this implementation adding new report type is as simple as adding new bean implementing Report and a new ReportType enum value. You could get away without the enum and using strings (maybe even bean names), however I found strongly typing beneficial.


Last thought: Report name is a bit unfortunate. Report class represents (stateless?) encapsulation of some logic (Strategy pattern), whereas the name suggests it encapsulates value (data). I would suggest ReportGenerator or such.

like image 183
Tomasz Nurkiewicz Avatar answered Sep 30 '22 23:09

Tomasz Nurkiewicz