We are currently in the process of migrating our services from Spring Boot 2.x to Spring Boot 3.2.1 and java 21 version, The following code was functioning well in Spring Boot 2.x but encountered problems after the migration.
Working Code in Spring Boot 2.x:
protected Map<String, Object> returnSavedObject(String packageName, String className, Map<String, Object> requestDto, String moduleName, Long pkId) {
try {
// Dynamically creates module object
Class moduleClass = Class.forName(packageName + className + "Dto");
Object object = returnObjectFromRequest(requestDto, moduleClass, moduleName, pkId);
// Loads bean for the given Module serviceImpl class
Object serviceObject = applicationContext.getBean(className.substring(0, 1).toLowerCase() + className.substring(1) + "ServiceImpl");
// Invokes the save method on serviceImpl class
Object savedObject = serviceObject.getClass().getMethod("save", moduleClass).invoke(serviceObject, object);
log.info("savedObject {}", savedObject);
return fieldHelper.objectToMapConverter(savedObject);
} catch (Exception e) {
throw new NotFoundException(MessageCode.error(ApplicationErrorCode.MODULE_CLASS_NOT_FOUND.getKey(), ApplicationErrorCode.MODULE_CLASS_NOT_FOUND.getValue()));
}
}
Issue After Migrating to Spring Boot 3.2.1:
The code encountered a 'java.lang.NoSuchMethodException' exception, and during debugging, we observed 'java.lang.NoSuchMethodException:jdk.proxy2.$Proxy292.save(dto)'. It appears that the getBean method in the application context is returning a proxy bean instead of the actual bean.
To Address the above Issue, We added below code to get actual bean object.
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(serviceObject);
protected Map<String, Object> returnSavedObject(String packageName, String className, Map<String, Object> requestDto, String moduleName, Long pkId) {
try {
// Dynamically creates module object
Class moduleClass = Class.forName(packageName + className + "Dto");
Object object = returnObjectFromRequest(requestDto, moduleClass, moduleName, pkId);
// Resolves the actual target class from the proxy
String beanClassName = className.substring(0, 1).toLowerCase() + className.substring(1) + "ServiceImpl";
Object serviceObject = applicationContext.getBean(beanClassName);
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(serviceObject);
// Invokes the save method on the target class
Object savedObject = targetClass.getMethod("save", moduleClass).invoke(targetClass, object);
log.info("savedObject {}", savedObject);
return fieldHelper.objectToMapConverter(savedObject);
} catch (Exception e) {
throw new NotFoundException(MessageCode.error(ApplicationErrorCode.MODULE_CLASS_NOT_FOUND.getKey(), ApplicationErrorCode.MODULE_CLASS_NOT_FOUND.getValue()));
}
}
New Issue Encountered:
The updated code now throws a 'java.lang.IllegalArgumentException': "Object is not an instance of declaring class."
Could you help me resolve this error ?
Below code helped me to solve the above problem
Object serviceBean = applicationContext.getBean(beanClassName);
if (AopUtils.isAopProxy(serviceBean) && serviceBean instanceof Advised) {
serviceBean = ((Advised) serviceBean).getTargetSource().getTarget();
}
Object savedObject = serviceBean.getClass().getMethod("save", moduleClass).invoke(serviceBean, object);