Usage scenarios
- Input the scheduled task into the database (the advantage of this is the visualization of the scheduled task, and the execution time of each task can be modified dynamically), and execute the corresponding method through reflection;
- Implement a simple HTTP request processing framework with Netty
- Other businesses that need to use reflection to execute Spring methods can also
objective
Reflection is mentioned in many articles, but for the processing of method parameters, the type is clear, and dynamic conversion according to actual parameters is not supported. This article provides a way to realize dynamic call of method parameters.
You can also write more reusable and extensible code by using the method in this paper and combining your own business scenarios. You are welcome to point out the mistakes in the article. If you have better ideas, you can comment on them below. Let's discuss them together.
Welcome to forward, please indicate the source.
Implementation mode
Premise:
Be clear about the classes and methods that need to be implemented.
thinking
- Obtain the classes to be executed through the spring container. Note: the classes obtained from the spring container may be proxied by JDK or CGLIB (depending on your environment configuration);
- Get the executed method object;
- The actual parameter List of encapsulation method only supports automatic conversion of basic type wrapper class, String, object, Map and other parameter types
- Executing the invoke method of method
Core class
@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); /** * Reflecting the entry to call spring bean methods * @param classz Class name * @param methodName Method name * @param paramMap Actual parameters * @throws Exception */ public void invokeService(String classz, String methodName, Map<String,Object> paramMap) throws Exception { if(!applicationContext.containsBean(classz)) { throw new RuntimeException("Spring No corresponding Bean"); } // Get proxy object from Spring (may be proxied by JDK or CGLIB) Object proxyObject = applicationContext.getBean(classz); // Get the method of proxy object execution Method method = getMethod(proxyObject.getClass(), methodName); // Get target object in proxy object Class target = AopUtils.getTargetClass(proxyObject); // Get the method of the target object, why get the method of the target object: only the target object can get the method name of the parameter through DefaultParameterNameDiscoverer, and the proxy object can't get the parameter name due to the possibility of being proxied by JDK or CGLIB Method targetMethod = getMethod(target, methodName); if(method == null) { throw new RuntimeException(String.format("Can't find%s method", methodName)); } // Get parameters of method execution List<Object> objects = getMethodParamList(targetMethod, paramMap); // Execution method method.invoke(proxyObject, objects.toArray()); } /** * Get method actual parameter, basic type not supported * @param method * @param paramMap * @return */ private List<Object> getMethodParamList(Method method, Map<String, Object> paramMap) throws Exception { List<Object> objectList = new ArrayList<>(); // Using the class provided by Spring to get the method parameter name 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; // The basic type is not supported. The wrapper class is supported 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); // assignment BeanUtils.populate(object, paramMap); } objectList.add(object); } return objectList; } /** * Get type instance * @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(); } /** * Get target method * @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; } }
test method
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", "bmw"); paramMap.put("speed", "1"); reflectionService.invokeService("carServiceImpl", "start", paramMap); } @Test public void objectTest() throws Exception { Map<String, Object> paramMap = new HashMap<>(); paramMap.put("carName", "bmw"); paramMap.put("speed", "2"); reflectionService.invokeService("carServiceImpl", "startByVO", paramMap); } @Test public void mapTest() throws Exception { Map<String, Object> paramMap = new HashMap<>(); paramMap.put("carName", "bmw"); paramMap.put("speed", "3"); reflectionService.invokeService("carServiceImpl", "startByMap", paramMap); } }