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.
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.4445Cash‑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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
