You are reading the documentation of sheetsmith 1.0.x.See the latest version (1.0.x)
Converters as Spring beans
In a Spring Boot application, every bean implementing CellConverter is registered as an application converter for the type it handles. This is convenient for types used everywhere, and has consequences that must be understood:
| Situation | What happens |
|---|---|
A converter class annotated with @Component (or declared with @Bean) |
It becomes the application converter for its type: it applies to every column of that type in every sheet class, unless a column declares a field converter. |
| Two converter beans handling the same type | Startup fails: IllegalStateException: converter beans 'a' and 'b' both handle type X; keep only one of them. |
| A converter bean whose handled type cannot be determined (raw type, or a lambda bean declared with a raw return type) | Startup fails: IllegalStateException: cannot resolve the type handled by converter bean 'x'; declare it as a class implementing CellConverter<T>, or as a @Bean method returning CellConverter<T>, instead of a lambda or a raw type. |
| A converter meant for one column only | Do not declare it as a bean. Declare it on the field with @ExcelColumn(converter = X.class). If it needs dependencies, give it a constructor with those dependencies: the Spring factory creates it with injection without registering it globally. |
| A field converter class that is also a bean (exactly one) | The bean is used for the column, and it is also the application converter for its type. |
| A field converter class with several beans | A new instance is created with injection for the field. |
| Field converter vs application converter on the same column | The field converter always wins. |
Declaring converter beans:
@Componentpublic class MoneyConverter implements CellConverter<Money> { @Override public CellValue convert(Money value, ConversionContext context) { return CellValue.number(value.amount().doubleValue()); }}
@Configurationclass ExportConfiguration { @Bean CellConverter<UUID> uuidConverter() { // the generic return type identifies the handled type return (value, context) -> CellValue.text(value.toString()); }}A field converter with dependencies, not a bean:
public class CountryNameConverter implements CellConverter<String> {
private final CountryRegistry countries; // a Spring bean, injected by the factory
public CountryNameConverter(CountryRegistry countries) { this.countries = countries; }
@Override public CellValue convert(String isoCode, ConversionContext context) { return CellValue.text(countries.displayName(isoCode)); }}
@ExcelSheetpublic record ShipmentRow( @ExcelColumn(header = "Country code", order = 10) String country, @ExcelColumn(header = "Country", order = 20, converter = CountryNameConverter.class) String countryName) {}Here CountryNameConverter handles String: as a bean it would turn every string column of the application into a country name. As a field converter it applies to one column.