springspring-bootreflectionspring-transactions

Issues with Dynamic method calling after migrating to springboot 3


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 ?


Solution

  • 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);