利用反射執行Spring方法,支持參數自動轉換


使用情景

  1. 將定時任務錄入數據庫(這樣做的好處是定時任務可視化,也可以動態修改各個任務的執行時間),通過反射執行對應的方法;
  2. 配合Netty實現簡單的HTTP請求處理框架
  3. 其他需要使用反射執行Spring方法的業務亦可

目的

      很多文章都提到了反射,但是對於方法參數處理這一塊都是明確了類型,不支持按照實際參數動態轉換,而本篇文章提供了一個思路怎么做到方法參數的動態調用。
      大家也可以通過利用本文的方法結合自己的業務場景寫出復用性更高、可擴展性更好的代碼。歡迎各位指出文章中的錯誤,如果有更好的思路可以在下方評論,我們一起討論。
      歡迎轉發,請注明出處。

實現方式

前提:

明確清楚需要執行的類和方法。

思路

  1. 通過Spring容器獲取需要執行的類,注意:從spring容器中獲取的類可能是被JDK或CGLIB代理的(取決於你的環境配置);
  2. 獲取執行的Mehod對象;
  3. 封裝方法實際參數List,僅支持基本類型包裝類, String,對象,Map等參數類型自動轉換
  4. 執行Mehod的invoke方法

核心類

@Service
public class ReflectionService {

    @Resource
    private ApplicationContext applicationContext;

    private static final List<Class> WRAP_CLASS = Arrays.asList(Integer.class, Boolean.class, Double.class,Byte.class,Short.class, Long.class, Float.class, Double.class, BigDecimal.class, String.class);


    /**
     * 反射調用spring bean方法的入口
     * @param classz 類名
     * @param methodName 方法名
     * @param paramMap 實際參數
     * @throws Exception
     */
    public void invokeService(String classz, String methodName, Map<String,Object> paramMap) throws Exception {
        if(!applicationContext.containsBean(classz)) {
            throw new RuntimeException("Spring找不到對應的Bean");
        }

        // 從Spring中獲取代理對象(可能被JDK或者CGLIB代理)
        Object proxyObject = applicationContext.getBean(classz);

        // 獲取代理對象執行的方法
        Method method = getMethod(proxyObject.getClass(), methodName);

        // 獲取代理對象中的目標對象
        Class target = AopUtils.getTargetClass(proxyObject);

        // 獲取目標對象的方法,為什么獲取目標對象的方法:只有目標對象才能通過 DefaultParameterNameDiscoverer 獲取參數的方法名,代理對象由於可能被JDK或CGLIB代理導致獲取不到參數名
        Method targetMethod = getMethod(target, methodName);

        if(method == null) {
            throw new RuntimeException(String.format("沒有找到%s方法", methodName));
        }

        // 獲取方法執行的參數
        List<Object> objects = getMethodParamList(targetMethod, paramMap);

        // 執行方法
        method.invoke(proxyObject, objects.toArray());
    }

    /**
     * 獲取方法實際參數,不支持基本類型
     * @param method
     * @param paramMap
     * @return
     */
    private List<Object> getMethodParamList(Method method, Map<String, Object> paramMap) throws Exception {
        List<Object> objectList = new ArrayList<>();

        // 利用Spring提供的類獲取方法形參名
        DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
        String[] param =  nameDiscoverer.getParameterNames(method);

        for (int i = 0; i < method.getParameterTypes().length; i++) {
            Class<?> parameterType = method.getParameterTypes()[i];

            Object object = null;
            // 基本類型不支持,支持包裝類
            if(WRAP_CLASS.contains(parameterType)) {
                if(param != null && paramMap.containsKey(param[i])){
                    object = paramMap.get(param[i]);

                    object = ConvertUtils.convert(object, parameterType);
                }

            }else if (!parameterType.isPrimitive() ) {
                object = getInstance(parameterType);

                // 賦值
                BeanUtils.populate(object, paramMap);
            }

            objectList.add(object);
        }

        return objectList;
    }

    /**
     * 獲取類型實例
     * @param parameterType
     * @return
     * @throws Exception
     */
    private Object getInstance(Class<?> parameterType) throws Exception {
        if(parameterType.isAssignableFrom(List.class)) {
            return  new ArrayList();

        }else if(parameterType.isAssignableFrom(Map.class)) {
            return new HashMap();
        }else if(parameterType.isAssignableFrom(Set.class)) {
            return  new HashSet();
        }
        return parameterType.newInstance();
    }

    /**
     * 獲取目標方法
     * @param proxyObject
     * @param methodStr
     * @return
     */
    private Method getMethod(Class proxyObject, String methodStr) {
        Method[] methods = proxyObject.getMethods();

        for(Method method : methods) {
            if(method.getName().equalsIgnoreCase(methodStr)) {
                return method;
            }
        }

        return null;
    }
}

測試方法

package com.ywqonly.springtest.reflection;

import com.ywqonly.springtest.reflection.service.impl.ReflectionService;
import com.ywqonly.springtest.reflection.vo.CarVO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringReflectionTest {

    @Resource
    private ReflectionService reflectionService;

    @Test
    public void paramTest() throws Exception {
        Map<String, Object>  paramMap = new HashMap<>();

        paramMap.put("carName", "寶馬");
        paramMap.put("speed", "1");
        reflectionService.invokeService("carServiceImpl", "start", paramMap);
    }

    @Test
    public void objectTest() throws Exception {
        Map<String, Object>  paramMap = new HashMap<>();

        paramMap.put("carName", "寶馬");
        paramMap.put("speed", "2");
        reflectionService.invokeService("carServiceImpl", "startByVO", paramMap);
    }

    @Test
    public void mapTest() throws Exception {
        Map<String, Object>  paramMap = new HashMap<>();

        paramMap.put("carName", "寶馬");
        paramMap.put("speed", "3");
        reflectionService.invokeService("carServiceImpl", "startByMap", paramMap);
    }

}

源碼分享

GITHUB源碼地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM