How to Validate Spring Cloud Dynamic Configs with In‑Memory Java Compilation

This article explains how to build a runtime validator for Spring Cloud dynamic configuration by generating Java classes from .properties files, compiling them in memory with JavaCompiler, loading them via a custom ClassLoader, and integrating the result into the Spring context to catch configuration errors early.

Programmer DD
Programmer DD
Programmer DD
How to Validate Spring Cloud Dynamic Configs with In‑Memory Java Compilation

Problem

When using Spring Cloud for dynamic configuration, values are injected with @Value into beans marked with @RefreshScope. After a change, ContextRefresher creates a new Spring Environment and lazy‑initialized scoped beans are rebuilt with the new settings. This flexibility introduces risks: configuration changes bypass formal QA, can cause obscure bugs, and Spring Cloud lacks a fallback mechanism, so type errors may render a service unavailable.

The author raised an issue with the Spring Cloud project, but the maintainers deemed the change too large to adopt.

To mitigate these risks, a validator is added that checks dynamic configurations before they are applied.

Overall Idea

The plan extracts the configuration, builds an independent Java class, and creates a separate ApplicationContext to instantiate the class. If bean creation fails, the configuration is considered invalid.

Dynamic Compilation

Generating Java Class from Configuration

The .properties file is parsed, and each entry is annotated with a custom comment that declares the Spring EL expression and the target field type. After parsing, the fields are inserted into a class template to produce a Config.java source string.

JavaCompiler

The generated source is compiled at runtime using javax.util.JavaCompiler, avoiding the traditional write‑compile‑load‑cleanup cycle.

JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
JavaFileManager fileManager = javaCompiler.getStandardFileManager(null, null, null);
CompilationTask task = javaCompiler.getTask(out, fileManager, diagnosticListener, options, classes, compilationUnits);
task.call();
FileObject outputFile = fileManager.getFileForOutput(null, null, null, null);
outputFile.getCharContent(true);

The compilation flow uses getTask() to submit a CompilationTask, reads the source via getCharContent(), and writes the bytecode with openOutputStream().

Delegation Pattern

Because the default JavaCompiler implementation works with files, a memory‑only solution is needed. The author extends ForwardingJavaFileManager and SimpleJavaFileObject using the delegation pattern, overriding necessary methods.

Spring Bean Instantiation

To turn the compiled Config class into a Spring bean, a FileSystemXmlApplicationContext is defined in XML, and after compilation the context is created with a custom class loader.

Class Loader

A custom ClassLoader loads the in‑memory bytecode by overriding findClass:

class MemoryClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // compiled bytecode stored in classBytes map
        byte[] buf = classBytes.get(name);
        if (buf == null) {
            return super.findClass(name);
        }
        return defineClass(name, buf, 0, buf.length);
    }
}

Configuration and Execution

The dynamic properties are added to the Spring Environment as a new PropertySource. Then applicationContext.refresh() is called, and the lazy Config bean is retrieved to trigger validation.

FileSystemXmlApplicationContext applicationContext = new FileSystemXmlApplicationContext();
applicationContext.setClassLoader(memoryClassLoader);
applicationContext.setConfigLocation("classpath*:/test.xml");
Map<String, Object> propertyMap = buildDynamicPropertyMap();
MapPropertySource mapPropertySource = new MapPropertySource("validate_source", propertyMap);
applicationContext.getEnvironment().getPropertySources().addFirst(mapPropertySource);
applicationContext.refresh();
applicationContext.getBean("config");

Summary

The prototype revisits many backend concepts—runtime code generation, in‑memory compilation, custom class loading, and Spring bean lifecycle—demonstrating a way to validate Spring Cloud dynamic configurations before they affect production services.

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.

Dynamic ConfigurationvalidationJavaCompilerSpring Cloudruntime compilationspring-bean
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

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.