Why Manual JAR Deployment Is Outdated: Embrace Dynamic Hot‑Deploy of Uploaded JARs
The article demonstrates how to replace manual JAR deployment with a dynamic hot‑deployment mechanism that loads user‑provided JARs at runtime, supporting both Spring‑managed and pure‑reflection implementations, and explains bean registration, removal, and testing procedures.
Define a Simple Interface
First, a basic Calculator interface is declared with two methods: int calculate(int a, int b) and int add(int a, int b).
public interface Calculator {
int calculate(int a, int b);
int add(int a, int b);
}Simple Implementation
The implementation class CalculatorImpl shows two ways a user may provide the logic: an annotation‑based method ( calculate) that relies on Spring injection, and a reflection‑based method ( add) that directly creates a CalculatorCore instance.
@Service
public class CalculatorImpl implements Calculator {
@Autowired
CalculatorCore calculatorCore;
// Annotation way
@Override
public int calculate(int a, int b) {
int c = calculatorCore.add(a, b);
return c;
}
// Reflection way
@Override
public int add(int a, int b) {
return new CalculatorCore().add(a, b);
}
}Reflection‑Based Hot Deployment
The user uploads a JAR to a predefined directory ( jarAddress) and provides the fully‑qualified class name of the implementation. The system loads the JAR with a URLClassLoader, obtains the class, creates an instance via reflection, and invokes the method.
private static String jarAddress = "E:/zzq/IDEA_WS/CalculatorTest/lib/Calculator.jar";
private static String jarPath = "file:/" + jarAddress;
public static void hotDeployWithReflect() throws Exception {
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());
Class clazz = urlClassLoader.loadClass("com.nci.cetc15.calculator.impl.CalculatorImpl");
Calculator calculator = (Calculator) clazz.newInstance();
int result = calculator.add(1, 2);
System.out.println(result);
}Annotation‑Based Hot Deployment
If the uploaded JAR contains Spring components, the system scans all classes, identifies those annotated with @Component, @Repository, or @Service, and registers them into the current Spring container using BeanDefinitionBuilder. The loading of the JAR itself uses the same URLClassLoader as the reflection approach.
public static void hotDeployWithSpring() throws Exception {
Set<String> classNameSet = DeployUtils.readJarFile(jarAddress);
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());
for (String className : classNameSet) {
Class clazz = urlClassLoader.loadClass(className);
if (DeployUtils.isSpringBeanClass(clazz)) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
defaultListableBeanFactory.registerBeanDefinition(DeployUtils.transformName(className), beanDefinitionBuilder.getBeanDefinition());
}
}
}Utility Methods (DeployUtils)
DeployUtils.readJarFileiterates over a JAR, collects fully‑qualified class names, and returns them as a Set<String>. isSpringBeanClass checks whether a class is a concrete Spring component by looking for @Component, @Repository, or @Service annotations. transformName converts a class name to a bean name by lower‑casing the first character.
public static Set<String> readJarFile(String jarAddress) throws IOException {
Set<String> classNameSet = new HashSet<>();
JarFile jarFile = new JarFile(jarAddress);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String name = jarEntry.getName();
if (name.endsWith(".class")) {
String className = name.replace(".class", "").replaceAll("/", ".");
classNameSet.add(className);
}
}
return classNameSet;
}
public static boolean isSpringBeanClass(Class<?> cla) {
if (cla == null || cla.isInterface() || Modifier.isAbstract(cla.getModifiers())) {
return false;
}
if (cla.getAnnotation(Component.class) != null) return true;
if (cla.getAnnotation(Repository.class) != null) return true;
if (cla.getAnnotation(Service.class) != null) return true;
return false;
}
public static String transformName(String className) {
String tmp = className.substring(className.lastIndexOf(".") + 1);
return tmp.substring(0,1).toLowerCase() + tmp.substring(1);
}Deleting a JAR and Its Beans
When a JAR is removed or replaced, the previously registered Spring beans must be deregistered. The deletion process mirrors registration: the same URLClassLoader loads the classes, isSpringBeanClass identifies Spring beans, and defaultListableBeanFactory.removeBeanDefinition removes them.
public static void delete() throws Exception {
Set<String> classNameSet = DeployUtils.readJarFile(jarAddress);
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());
for (String className : classNameSet) {
Class clazz = urlClassLoader.loadClass(className);
if (DeployUtils.isSpringBeanClass(clazz)) {
defaultListableBeanFactory.removeBeanDefinition(DeployUtils.transformName(className));
}
}
}Test Harness
A simple test loop repeatedly attempts hot deployment. If the JAR is not yet present, an exception is caught, printed, and the thread sleeps for 10 seconds, allowing the developer to copy the JAR into the target directory manually.
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
while (true) {
try {
hotDeployWithReflect();
// hotDeployWithSpring();
// delete();
} catch (Exception e) {
e.printStackTrace();
Thread.sleep(1000 * 10);
}
}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.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
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.
