Overview and Usage of JavaMoney (JSR‑354) for Currency and Monetary Amount Handling

JavaMoney (JSR‑354) provides a comprehensive, extensible API for currency units, monetary amounts, conversion, rounding, and formatting, with modular packages, SPI‑based custom implementations, and dynamic data loading, enabling Java applications to handle simple to complex financial and virtual‑currency scenarios.

vivo Internet Technology
vivo Internet Technology
vivo Internet Technology
Overview and Usage of JavaMoney (JSR‑354) for Currency and Monetary Amount Handling

This article introduces JavaMoney, the reference implementation of JSR‑354, which provides a comprehensive API for handling currencies, monetary amounts, conversions, formatting, and related extensions in Java applications.

1. Current Situation – The standard JDK class java.util.Currency only supports ISO‑4217 currency codes and lacks numeric values, amount representation, conversion, and formatting capabilities. JSR‑354 defines a richer set of interfaces to address these gaps.

2. Goals of JSR‑354

Enable extensible currency types to support diverse business scenarios.

Provide APIs for monetary amount calculations.

Support currency conversion and exchange‑rate handling.

Offer parsing and formatting of monetary values.

3. Typical Use Cases

Online stores – price calculation, cart totals, tax and discount computation.

Financial trading platforms – portfolio valuation, historical and projected returns.

Virtual worlds and games – custom in‑game currencies and exchange with real money.

Banking and finance – handling of exchange rates, interest rates, and historical monetary data.

4. Package and Module Structure

JSR‑354 defines four main packages: javax.money – core types such as CurrencyUnit, MonetaryAmount, MonetaryContext, MonetaryOperator, MonetaryQuery, and the singleton accessor Monetary. javax.money.convert – conversion‑related types like ExchangeRate, ExchangeRateProvider, CurrencyConversion, and the singleton MonetaryConversions. javax.money.format – formatting types such as MonetaryAmountFormat and AmountFormatContext, accessed via MonetaryFormats. javax.money.spi – Service Provider Interfaces (SPI) that allow custom implementations for the above functionalities.

The source repository contains modules jsr354-api, jsr354-ri (Moneta implementation), jsr354-tck (technology compatibility kit), and a parent POM javamoney-parent.

5. Core API Details

5.1 CurrencyUnit

The CurrencyUnit interface models a currency’s minimal unit:

public interface CurrencyUnit extends Comparable<CurrencyUnit> {
    String getCurrencyCode();
    int getNumericCode();
    int getDefaultFractionDigits();
    CurrencyContext getContext();
}

Retrieval methods include:

CurrencyUnit cu = Monetary.getCurrency("USD");
CurrencyUnit cuChina = Monetary.getCurrency(Locale.CHINA);
CurrencyQuery query = CurrencyQueryBuilder.of()
    .setCurrencyCodes("CNY")
    .setCountries(Locale.CHINA)
    .setNumericCodes(-1)
    .build();
Collection<CurrencyUnit> cnyUnits = Monetary.getCurrencies(query);
Collection<CurrencyUnit> all = Monetary.getCurrencies();

These methods delegate to MonetaryCurrenciesSingletonSpi implementations, with the default provider chain consisting of JDKCurrencyProvider and ConfigurableCurrencyUnitProvider.

5.2 MonetaryAmount

The MonetaryAmount interface represents a monetary value and extends several supplier interfaces:

public interface MonetaryAmount extends CurrencySupplier, NumberSupplier, Comparable<MonetaryAmount> {
    MonetaryContext getContext();
    default <R> R query(MonetaryQuery<R> query) { return query.queryFrom(this); }
    default MonetaryAmount with(MonetaryOperator operator) { return operator.apply(this); }
    MonetaryAmountFactory<? extends MonetaryAmount> getFactory();
    boolean isGreaterThan(MonetaryAmount amount);
    int signum();
    MonetaryAmount add(MonetaryAmount amount);
    MonetaryAmount stripTrailingZeros();
    // ... other arithmetic methods
}

Three standard implementations are provided: FastMoney – integer‑based, high‑performance. Money – uses java.math.BigDecimal for arbitrary precision. RoundedMoney – applies rounding after each operation.

Creation examples:

FastMoney fm1 = Monetary.getAmountFactory(FastMoney.class).setCurrency("CNY").setNumber(144).create();
FastMoney fm2 = FastMoney.of(144, "CNY");
Money m1 = Monetary.getAmountFactory(Money.class).setCurrency("CNY").setNumber(144).create();
Money m2 = Money.of(144, "CNY");

Custom precision and rounding can be configured via MonetaryContextBuilder:

Money money1 = Monetary.getAmountFactory(Money.class)
    .setCurrency("CNY").setNumber(144)
    .setContext(MonetaryContextBuilder.of().set(MathContext.DECIMAL128).build())
    .create();

Conversion between Money and FastMoney is possible via Money.from(...) and FastMoney.from(...).

5.3 MonetaryAmountFactory and SPI

Factories are obtained through the MonetaryAmountsSingletonSpi implementation ( DefaultMonetaryAmountsSingletonSpi). Custom amount types require implementing MonetaryAmountFactoryProviderSpi and the corresponding abstract factory.

5.4 Currency Conversion

Conversion is performed via CurrencyConversion obtained from MonetaryConversions:

Number amount = 144;
CurrencyUnit cu = Monetary.getCurrency("CNY");
Money money = Money.of(amount, cu);
CurrencyConversion conv = MonetaryConversions.getConversion("ECB");
Money converted = money.with(conv);

Alternatively, an ExchangeRateProvider can be fetched first and then used to create a conversion.

5.5 Rounding

Rounding operators are created via Monetary.getRounding(...) or the default rounding:

MonetaryRounding rounding = Monetary.getRounding(RoundingQueryBuilder.of().setScale(4).set(RoundingMode.HALF_UP).build());
Money m = Money.of(144.44445555, "CNY");
Money rounded = m.with(rounding); // 144.4445

Cash‑rounding for currencies like CHF can be enabled with the cashRounding flag.

5.6 Formatting

Formatting and parsing are handled by MonetaryAmountFormat obtained from MonetaryFormats:

MonetaryAmountFormat fmt = MonetaryFormats.getAmountFormat(Locale.CHINESE);
MonetaryAmount amt = Money.of(144144.44, "VZU");
String str = fmt.format(amt);
MonetaryAmount parsed = fmt.parse(str);

Custom format providers can be added via the MonetaryAmountFormatProviderSpi SPI.

6. Service Provider Interfaces (SPI)

JSR‑354 defines several SPIs to decouple implementations:

Currency handling – CurrencyProviderSpi, MonetaryCurrenciesSingletonSpi Conversion – MonetaryConversionsSingletonSpi Amount creation – MonetaryAmountFactoryProviderSpi, MonetaryAmountsSingletonSpi Rounding – RoundingProviderSpi, MonetaryRoundingsSingletonSpi Formatting – MonetaryAmountFormatProviderSpi, MonetaryFormatsSingletonSpi Service discovery – ServiceProvider The default service provider uses Java's ServiceLoader and orders services alphabetically or by @Priority annotation.

7. Data Loading Mechanism

Moneta supports dynamic loading of currency definitions and exchange‑rate data. Four update policies are available: NEVER, ONSTARTUP, LAZY, and SCHEDULED. Loading can be performed locally, asynchronously, or via remote URLs, with optional proxy configuration.

Example of a scheduled loader using Timer:

timer.scheduleAtFixedRate(createTimerTask(load), delayMS, periodMS);

8. Extension Case Study – Custom Currency Types

The article demonstrates how to add proprietary currencies (e.g., VZU, GLJ) by:

Adding configuration entries in javamoney.properties to define loading policy and resource locations.

Registering a custom CurrencyProviderSpi implementation via META-INF/services/javax.money.spi.CurrencyProviderSpi.

Providing a JSON file with currency definitions.

Implementing the provider class that parses the JSON and registers the new CurrencyUnit instances.

Similarly, a custom ExchangeRateProvider can be added to handle bespoke exchange‑rate data, following the same registration steps.

9. Practical Validation Example

To verify that 100 virtual beans (VBE) equal 1 virtual diamond (VZU) after conversion:

Number rechargeNumber = 100;
CurrencyUnit vbe = Monetary.getCurrency("VBE");
Money recharge = Money.of(rechargeNumber, vbe);
Number payNumber = 1;
CurrencyUnit vzu = Monetary.getCurrency("VZU");
Money pay = Money.of(payNumber, vzu);
CurrencyConversion conv = MonetaryConversions.getConversion("VBE");
Money converted = pay.with(conv);
Assert.assertEquals(converted, recharge);

10. Conclusion

JavaMoney (JSR‑354) offers a robust, extensible framework for monetary operations in financial applications. Its modular design, rich SPI system, and data‑loading capabilities enable developers to adapt the library to a wide range of business requirements, from simple currency handling to complex, dynamic extensions.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaExtensionSPICurrencyJavaMoneyJSR 354Monetary API
vivo Internet Technology
Written by

vivo Internet Technology

Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.