使用情景

  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源码地址

【Java】利用反射执行Spring容器Bean指定的方法,支持多种参数自动调用的更多相关文章

  1. JAVA面试题:Spring中bean的生命周期

    Spring 中bean 的生命周期短暂吗? 在spring中,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一 ...

  2. 基于反射启动Spring容器

    基于反射启动Spring容器 package com.maple.test; import org.springframework.context.ApplicationContext; import ...

  3. java利用反射调用类的某个方法

    java利用反射机制 可以动态调用某个类的某个方法,在 扩展系统功能或提供对外接口时经常用的到. 代码如下: 打印类Print.java package com.test.reflct; /** * ...

  4. java利用反射获取类的属性及类型

    java利用反射获取类的属性及类型. import java.lang.reflect.Field; import java.math.BigDecimal; import java.util.Map ...

  5. 获取Spring容器Bean对象工具类

    在开发中,总是能碰到用注解注入不了Spring容器里面bean对象的问题.为了解决这个问题,我们需要一个工具类来直接获取Spring容器中的bean.因此就写了这个工具类,在此记录一下,方便后续查阅. ...

  6. 【转】Java利用反射机制访问私有化构造器

    Java利用反射机制访问私有化构造器 博客分类: java   我们都知道,当一个类的构造方法被设为私有的时候(private),在其他类中是无法用new来实例化一个对象的. 但是有一种方法可以把带有 ...

  7. java利用反射机制判断对象的属性是否为空以及获取和设置该属性的值

    1.java利用反射机制判断对象的属性是否为空: Map<String,String> validateMap = new LinkedHashMap<String, String& ...

  8. java利用反射访问类的私有(private)属性及方法

    Java语言中,在一个类中,为了不让外界访问到有的属性和方法,通常将其设置为private,用正常的方式(对象名.属性名,对象名.方法名)将无法访问此属性与方法,但有没有其他方法可以访问呢?答案是有的 ...

  9. DataTable和DataRow利用反射直接转换为Model对象的扩展方法类

    DataTable和DataRow利用反射直接转换为Model对象的扩展方法类   /// <summary> /// 类 说 明:给DataTable和DataRow扩展方法,直接转换为 ...

随机推荐

  1. 重学 Java 设计模式:实战工厂方法模式

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!

  2. jquery 滚轴滚动 导航定位和锚点定位

    自己写的,只测试了ie9+, firefox,chrome 以下js更好 var fixbar={ init:function(){ "use strict"; // 滚轴 导航位 ...

  3. 王艳 201771010127《面向对象程序设计(java)》第十周学习总结

    一:理论部分. 1.泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用. 1)泛型(参数化类型):在定义类.接口和方法时,通过类型参数指示将要处理的对象类型.如ArrayList类是一个泛型程 ...

  4. poj3683 2-SAT 同上一道

    Priest John's Busiest Day Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 10151   Accep ...

  5. POJ3735

    题目链接:http://poj.org/problem?id=3735 解题思路: 先构造一个(n+1)*(n+1)的单位矩阵E,在此基础上进行操作: 1.g i     -------------& ...

  6. LOL源代码娜美皮肤免费领取

    领取地址 http://t.cn/EyOY8zp 截图

  7. 工业互联网可视化系统风格的抉择:线框模式之 3D 数据中心机房的实现

    前言 3D 可视化,就是把复杂抽象的数据信息,以合适的视觉元素及视角去呈现,方便系统的展示.维护和管理.而在可视化系统的搭建选择上,所呈现的风格样式效果多种多样,各自所突出的适用场合也不尽相同.对于科 ...

  8. Java 对象的继承,抽象类,接口

    子父级继承 关键字 extends 首先创建一个父类 class Fu { String name; int a=1; public void word() { System.out.println( ...

  9. RabbitMq和ZeroMq

    RabbitMQ和ZeroMQ都是极好的消息中间件,下我会对这两个消息中间件做一个比較,个人理解不喜勿喷. RabbitMQ是AMQP协议率先的一个实现,它实现了代理(Broker)架构,意味着消息在 ...

  10. [SD心灵鸡汤]001.每月一则 - 2015.05

    1.既然我的父母不能带给我荣耀,那我要做的就只是带给我的子女荣耀,而不是无聊的嫉妒眼红别人. 2.就人生游戏讲,男人是女人的玩物,女人是魔鬼的玩物.就爱情而言,女人是专业的,男人是业余的. 3.快乐使 ...