dyenums is a dynamic enum library for Java 8+ that provides runtime-extensible enums. Unlike traditional Java enums that are fixed at compile time, dyenums allows you to register, modify, and load enum values at runtime.
- Runtime Registration: Register enum values dynamically at runtime
- Configuration Loading: Load enums from properties files or databases (optional)
- Custom Loaders: Implement
DyEnumsLoaderinterface for any data source - Type-Safe Access: Full type safety with generics
- Thread-Safe: Built with
ConcurrentHashMapand proper synchronization - Minimal Core: Only SLF4J dependency required for core module
- Spring Integration: Optional Spring/Boot support
dyenums
├── dyenums-core # Core module (required) - minimal dependencies
├── dyenums-loader-file # File loader (optional) - properties file support
├── dyenums-loader-db # Database loader (optional) - JDBC support
└── dyenums-spring # Spring integration (optional) - auto-configuration
| Use Case | Required Modules |
|---|---|
| Manual registration only | dyenums-core |
| Custom loader implementation | dyenums-core |
| Load from properties files | dyenums-core + dyenums-loader-file |
| Load from database | dyenums-core + dyenums-loader-db |
| Spring/Boot integration | dyenums-core + dyenums-spring |
Core only (minimal dependencies):
<dependency>
<groupId>cn.itcraft</groupId>
<artifactId>dyenums-core</artifactId>
<version>1.0.0</version>
</dependency>With file loader:
<dependency>
<groupId>cn.itcraft</groupId>
<artifactId>dyenums-loader-file</artifactId>
<version>1.0.0</version>
</dependency>With database loader:
<dependency>
<groupId>cn.itcraft</groupId>
<artifactId>dyenums-loader-db</artifactId>
<version>1.0.0</version>
</dependency>With Spring integration:
<dependency>
<groupId>cn.itcraft</groupId>
<artifactId>dyenums-spring</artifactId>
<version>1.0.0</version>
</dependency>git clone https://github.com/itcraft-cn/dyenums.git
cd dyenums
mvn clean installExtend BaseDyEnum:
import cn.itcraft.dyenums.core.BaseDyEnum;
public class UserStatus extends BaseDyEnum {
public static final UserStatus ACTIVE = new UserStatus("ACTIVE", "Active", "User is active", 1);
public static final UserStatus INACTIVE = new UserStatus("INACTIVE", "Inactive", "User is inactive", 2);
public static final UserStatus LOCKED = new UserStatus("LOCKED", "Locked", "User is locked", 3);
private UserStatus(String code, String name, String description, int order) {
super(code, name, description, order);
}
}import cn.itcraft.dyenums.core.EnumRegistry;
public class Application {
public static void main(String[] args) {
// Register predefined values
EnumRegistry.register(UserStatus.class, UserStatus.ACTIVE);
EnumRegistry.register(UserStatus.class, UserStatus.INACTIVE);
EnumRegistry.register(UserStatus.class, UserStatus.LOCKED);
// Lookup by code
UserStatus status = EnumRegistry.valueOf(UserStatus.class, "ACTIVE")
.orElseThrow(() -> new IllegalArgumentException("Status not found"));
System.out.println(status.getName()); // Output: Active
}
}// Create and register at runtime
UserStatus customStatus = new UserStatus("CUSTOM", "Custom Status", "Custom status", 99);
EnumRegistry.register(UserStatus.class, customStatus);
// Or use EnumRegistry.addEnum with reflection
EnumRegistry.addEnum(UserStatus.class, "VIP", "VIP User", "VIP status", 100);Implement DyEnumsLoader interface to load enums from any source:
import cn.itcraft.dyenums.loader.DyEnumsLoader;
import cn.itcraft.dyenums.core.DyEnum;
import java.util.function.BiFunction;
public class MyCustomLoader<T extends DyEnum> implements DyEnumsLoader<T> {
@Override
public int load(Class<T> enumClass, BiFunction<String, String, T> factory) throws Exception {
// Load from your data source (REST API, Redis, etc.)
List<MyData> dataList = fetchFromMySource();
int count = 0;
for (MyData data : dataList) {
String code = data.getCode();
String valueString = data.getName() + "|" + data.getDesc() + "|" + data.getOrder();
T enumValue = factory.apply(code, valueString);
EnumRegistry.register(enumClass, enumValue);
count++;
}
return count;
}
@Override
public boolean validateSource() {
return isMySourceAccessible();
}
}Extend with language-aware messages:
import cn.itcraft.dyenums.core.BaseDyEnum;
import java.util.Locale;
import java.util.Map;
import java.util.HashMap;
public class ErrorCode extends BaseDyEnum {
private final Map<String, String> messages;
public ErrorCode(String code, String name, int order, Map<String, String> messages) {
super(code, name, null, order);
this.messages = messages;
}
public String getMessage(String lang) {
return messages.getOrDefault(lang, getCode());
}
public String getMessage(Locale locale) {
return getMessage(locale.getLanguage());
}
// Convenience methods
public String getMessageZh() { return getMessage("zh"); }
public String getMessageEn() { return getMessage("en"); }
}
// Usage
Map<String, String> messages = new HashMap<>();
messages.put("zh", "系统错误");
messages.put("en", "System error");
messages.put("pt", "Erro do sistema");
messages.put("ru", "Системная ошибка");
ErrorCode error = new ErrorCode("SYS_001", "System Error", 1, messages);
EnumRegistry.register(ErrorCode.class, error);
System.out.println(error.getMessageZh()); // 系统错误
System.out.println(error.getMessageEn()); // System errorUse dyenums-loader-file module:
# enums.properties
UserStatus.ACTIVE=ACTIVE|Active|User is active|1
UserStatus.INACTIVE=INACTIVE|Inactive|User is inactive|2import cn.itcraft.dyenums.loader.file.FileBasedDyEnumsLoader;
FileBasedDyEnumsLoader<UserStatus> loader = new FileBasedDyEnumsLoader<>("enums.properties");
loader.load(UserStatus.class, UserStatus::fromValueString);Use dyenums-loader-db module:
import cn.itcraft.dyenums.loader.db.DatabaseDyEnumsLoader;
import javax.sql.DataSource;
DatabaseDyEnumsLoader<UserStatus> loader = new DatabaseDyEnumsLoader<>(dataSource);
loader.load(UserStatus.class, UserStatus::fromValueString);Use dyenums-spring module:
import cn.itcraft.dyenums.spring.EnumService;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class UserService {
@Autowired
private EnumService enumService;
public List<UserStatus> getAllStatuses() {
return enumService.getValues(UserStatus.class);
}
public UserStatus getStatus(String code) {
return enumService.getByCode(UserStatus.class, code);
}
}┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────────────────────────────────┤
│ EnumRegistry (Core) - Registration, Lookup, Thread-Safety │
├──────────────┬──────────────┬──────────────┬────────────────┤
│ BaseDyEnum │ DyEnumsLoader│ @EnumDef │ Spring Config │
│ (Core) │ (Core Interface) │ (Optional) │
├──────────────┴──────────────┴──────────────┴────────────────┤
│ Optional Loader Implementations │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐│
│ │ File Loader │ │ DB Loader │ │ Custom Implementation ││
│ │ (Optional) │ │ (Optional) │ │ (Your Own) ││
│ └─────────────┘ └─────────────┘ └─────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
| Component | Module | Description |
|---|---|---|
DyEnum |
core | Interface defining enum contract |
BaseDyEnum |
core | Abstract base implementation |
EnumRegistry |
core | Central registry for all enum instances |
DyEnumsLoader |
core | Interface for custom loaders |
FileBasedDyEnumsLoader |
loader-file | Properties file loader |
PropDyEnumsLoader |
loader-file | In-memory Properties loader |
DatabaseDyEnumsLoader |
loader-db | JDBC database loader |
EnumService |
spring | Spring service for enum access |
EnumConverter |
spring | Spring MVC type converter |
# Run all tests
mvn test
# Run specific module tests
mvn test -pl dyenums-core
mvn test -pl dyenums-loader-file
# Run specific test class
mvn test -Dtest=EnumRegistryTestAll registry operations are thread-safe:
// Safe for concurrent access
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
final int id = i;
executor.submit(() -> {
EnumRegistry.addEnum(UserStatus.class, "STATUS_" + id, "Status " + id, null, id);
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
assertEquals(100, EnumRegistry.getCount(UserStatus.class));- O(1) Lookup: HashMap-based lookup by code
- Concurrent Access:
ConcurrentHashMapfor thread-safe operations - Lazy Initialization: Enums created only when registered
- Memory Efficient: Single shared registry instance
- Register enums during application startup
- Treat enum instances as immutable after creation
- Ensure codes are unique within each enum type
- Implement
DyEnumsLoaderfor custom data sources - Use
EnumServicein Spring applications
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'feat: add AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
- Create an issue on GitHub
- Check the
docdirectory for documentation - Review example implementations in
dyenums-testmodule