当我们通过类加载器获得Class后,就可以通过常用反射手段,调用类方法了。
反射调用方法的要素:Class类,方法名,方法参数,方法返回值。
- Class类:已经通过前面的类加载器获取到了;
- 方法名:需要调用的类中的methodName,通过clazz.getMethod获取对应的Method;
- 方法参数:方法如果需要接收参数就必须指定传入参数。有因一个类中可能有重载的情况,即存在多个相同方法名但参数不同的情况,所以当存在重载情况就特别需要注意参数的类型以及顺序;
- 方法返回值:当调用完方法后,总是会有返回信息,即使是void(有类型信息,无值)。
代码如下
/**
* 调用类中方法
*
* @param clazz 类
* @param methodName 方法名
* @param args 方法参数
* @return 方法执行结果
*/
public static Object runClassMethod(Class<?> clazz, String methodName, Parameters args)
throws NoSuchMethodException, InstantiationException, IllegalAccessException,
InvocationTargetException {
Object[] values = null;
Object target = null;
Method method;
if (args == null || args.isEmpty()) {
// 如果无参,找类中无参方法
method = clazz.getMethod(methodName);
} else {
// 如果有参,找类中参数顺序相符的方法
List<Parameter> params = args.getParameters();
Class<?>[] clazzes = new Class[params.size()];
values = new Object[params.size()];
for (int i = 0; i < clazzes.length; i++) {
Parameter param = params.get(i);
clazzes[i] = param.getClazz();
values[i] = param.getVaule();
}
method = clazz.getMethod(methodName, clazzes);
}
if (!Modifier.isStatic(method.getModifiers())) {
// 如果方法是不是静态方法,则需要先实例类
target = getClassInstance(clazz);
}
// 调用方法
return method.invoke(target, values);
}
当然同时处理大量数据时,通过反射的方式实例Class或多或少都会有些性能的损失,所以可考虑单例的方式,暂存第一次创建的实例,减少反射实例类的过程。不过频率不大的情况下效率提升不明显,优化最主要的阶段还是再编译阶段,参考编译器篇的文章。
接下来包装一下调用过程以支持单例模式执行方法
/**
* 调用类方法
* @param parameter 封装的前三个要素
* @param useSingleton 是否使用单例
* @return 方法返回值
*/
private Object run(ExecuteParameter<? extends ClassBean> parameter, boolean useSingleton)
throws ExecuteException {
try {
ClassBean classBean = parameter.getClassBean();
Object instance = classBean.getInstance();
if (useSingleton) {
// 单例
if (instance == null) {
AtomicReference<Object> instanceReference = classBean.getInstanceReference();
// 通过反射获取实例,并回写回ClassBean,用ClassBean暂存实例
instanceReference.compareAndSet(null,
ClassUtils.getClassInstance(classBean.getClazz()));
instance = instanceReference.get();
}
} else {
// 非单例
instance = ClassUtils.getClassInstance(classBean.getClazz());
}
return ClassUtils.runInstanceMethod(instance, parameter.getMethodName(),
parameter.getArgs());
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) {
throw new ExecuteException(e);
} catch (InvocationTargetException e) {
throw new ExecuteException(e, e.getCause());
}
}
执行器阶段完毕。