【Java】利用反射执行Spring容器Bean指定的方法,支持多种参数自动调用
使用情景
- 将定时任务录入数据库(这样做的好处是定时任务可视化,也可以动态修改各个任务的执行时间),通过反射执行对应的方法;
- 配合Netty实现简单的HTTP请求处理框架
- 其他需要使用反射执行Spring方法的业务亦可
目的
很多文章都提到了反射,但是对于方法参数处理这一块都是明确了类型,不支持按照实际参数动态转换,而本篇文章提供了一个思路怎么做到方法参数的动态调用。
大家也可以通过利用本文的方法结合自己的业务场景写出复用性更高、可扩展性更好的代码。欢迎各位指出文章中的错误,如果有更好的思路可以在下方评论,我们一起讨论。
欢迎转发,请注明出处。
实现方式
前提:
明确清楚需要执行的类和方法。
思路
- 通过Spring容器获取需要执行的类,注意:从spring容器中获取的类可能是被JDK或CGLIB代理的(取决于你的环境配置);
- 获取执行的Mehod对象;
- 封装方法实际参数List,仅支持基本类型包装类, String,对象,Map等参数类型自动转换
- 执行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);
}
}
源码分享
【Java】利用反射执行Spring容器Bean指定的方法,支持多种参数自动调用的更多相关文章
- JAVA面试题:Spring中bean的生命周期
Spring 中bean 的生命周期短暂吗? 在spring中,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一 ...
- 基于反射启动Spring容器
基于反射启动Spring容器 package com.maple.test; import org.springframework.context.ApplicationContext; import ...
- java利用反射调用类的某个方法
java利用反射机制 可以动态调用某个类的某个方法,在 扩展系统功能或提供对外接口时经常用的到. 代码如下: 打印类Print.java package com.test.reflct; /** * ...
- java利用反射获取类的属性及类型
java利用反射获取类的属性及类型. import java.lang.reflect.Field; import java.math.BigDecimal; import java.util.Map ...
- 获取Spring容器Bean对象工具类
在开发中,总是能碰到用注解注入不了Spring容器里面bean对象的问题.为了解决这个问题,我们需要一个工具类来直接获取Spring容器中的bean.因此就写了这个工具类,在此记录一下,方便后续查阅. ...
- 【转】Java利用反射机制访问私有化构造器
Java利用反射机制访问私有化构造器 博客分类: java 我们都知道,当一个类的构造方法被设为私有的时候(private),在其他类中是无法用new来实例化一个对象的. 但是有一种方法可以把带有 ...
- java利用反射机制判断对象的属性是否为空以及获取和设置该属性的值
1.java利用反射机制判断对象的属性是否为空: Map<String,String> validateMap = new LinkedHashMap<String, String& ...
- java利用反射访问类的私有(private)属性及方法
Java语言中,在一个类中,为了不让外界访问到有的属性和方法,通常将其设置为private,用正常的方式(对象名.属性名,对象名.方法名)将无法访问此属性与方法,但有没有其他方法可以访问呢?答案是有的 ...
- DataTable和DataRow利用反射直接转换为Model对象的扩展方法类
DataTable和DataRow利用反射直接转换为Model对象的扩展方法类 /// <summary> /// 类 说 明:给DataTable和DataRow扩展方法,直接转换为 ...
随机推荐
- java新学者(二)
一.构造方法的特点 创建新的对象 A a =new A (); 二.抽象类和抽象方法的特点是什么? .抽象类使用abstract修饰: .抽象类不能实例化,即不能使用new关键字来实例化对象: .含有 ...
- Java操作XML(1)
XML简介 XML(Extensible Markup Language)是一种非常有用的迷哦书结构化信息的技术.XML工具使处理和转化信息变得十分容易. 在程序中,可以使用属性文件(property ...
- 换个角度学习ASP.NET Core中间件
中间件真面目 关于ASP.NET Core中间件是啥,简单一句话描述就是:用来处理HTTP请求和响应的一段逻辑,并且可以决定是否把请求传递到管道中的下一个中间件! 上面只是概念上的一种文字描述,那问题 ...
- 11_ArrayList集合的方法
class Program { static void Main(string[] args) { //数组:长度不可变,类型单一 //ArrayList集合:长度可以任意改变,类型可以不单一 //创 ...
- LinkedList为什么增删快、查询慢
List家族中共两个常用的对象ArrayList和LinkedList,具有以下基本特征. ArrayList:长于随机访问元素,中间插入和移除元素比较慢,在插入时,必须创建空间并将它的所有引用向前移 ...
- Java线程的启动与中止
一.线程与进程的关系 关于进程与线程,百度百科上是这样描述的: 进程(Process) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础. 在当 ...
- Java中的集合(十五) Iterator 和 ListIterator、Enumeration
Java中的集合(十五) Iterator 和 ListIterator.Enumeration 一.Iterator (一).简介 Iterator 是一个接口,它是集合的迭代器.集合可以通过Ite ...
- ## H5 canvas画图白板踩坑
最近接手了一个小型的H5,最主要的功能大概就是拍照上传和canvas画板了. 主要是记录一下自己菜到像傻子一样的技术. 1.canvas画板隔空打牛!画布越往上部分错位距离越小,越往下距离越大. 2. ...
- C实现进程间通信(管道; 共享内存,信号量)
最近学习了操作系统的并发:以下是关于进程间实现并发,通信的两个方法. 例子: 求100000个浮点数的和.要求: (1)随机生成100000个浮点数(父进程). (2)然后创建4个后代进程,分别求25 ...
- Java Word中的文本、图片替换功能
Word中的替换功能以查找指定文本然后替换为新的文本,可单个替换或全部替换.以下将要介绍的内容,除常见的以文本替换文本外,还将介绍使用不同对象进行替换的方法,具体可包括: 1. 指定字符串内容替换文本 ...