关于反射,我的理解是用某些基本信息(类名、方法名、变量),逆向生成对象并调用其中的函数。有点像用基因技术生成一个新生命。
这里,我用网关API来举例:
我们平时在调用银联、xx银行的系统时,对方往往只给与一个url,具体业务依靠入参[交易代码]来做分流,内部调用不同的service。
我们先创建一个交易代码枚举类ApiEnums
public enum ApiEnums {
CONSUME("CM1101", "consume", "com.szd.service.reflection.ConsumeServiceImpl", "consumeService"), //
RECHARGE("CM1102", "recharge", "com.szd.service.reflection.RechargeServiceImpl", "rechargeService"), //
REFUND("CM1103", "refund", "com.szd.service.reflection.RefundServiceImpl", "refundService"), //
QUERY_DETAIL("CM1201", "queryDetail", "com.szd.service.reflection.QueryServiceImpl", "queryService");
String methodCode;
String methodName;
String ServiceName;
String beanName;
}
service反射分流单元测试
@Test
public void testApi() {
// 验签完毕,除sign外的参数集
Map<String, String> requestBody = new HashMap<>();
requestBody.put("code", "CM1101");
requestBody.put("orderId", "18112711102556458");
requestBody.put("amount", "20");
ApiEnums apiEnum = ApiEnums.matchingEnum(requestBody.get("code"));
if (apiEnum == null) {
System.out.println("交易代码不存在");
return;
}
try {
//根据类全名(com.szd.service.reflection.ConsumeServiceImpl),获取到.class文件,并加载进JVM
Class<?> clazz = Class.forName(apiEnum.getServiceName());
//获得对象实例
Object object = clazz.newInstance();
Method method = clazz.getDeclaredMethod(apiEnum.getMethodName(), JSONObject.class);
method.invoke(object, JSONObject.toJSON(requestBody));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
ConsumeService :
public class ConsumeServiceImpl implements ConsumeService {
private Integer goodsAmount = 100;
@Override
public void consume(JSONObject jsonObject) {
System.out.println(Thread.currentThread().getName() + "商品数量(消费前): " + goodsAmount);
// step.1 将外部参数对象,转化成具体的业务对象
OrderInfo orderInfo = JSONObject.toJavaObject(jsonObject, OrderInfo.class);
// step.2 做参数校验
// step.3 处理业务
System.out.println(Thread.currentThread().getName() +"消费函数-订单号:" + orderInfo.getOrderId() + ",金额:" + orderInfo.getAmount());
goodsAmount = goodsAmount - orderInfo.getAmount();
System.out.println(Thread.currentThread().getName() +"商品数量(消费后): " + goodsAmount);
}
}
这里如果业务Service内部注入了spring管理的bean,那么在调用bean时,会出现空指针。这是因为反射得到的实例对象、spring管理的实例对象,是两种独立的存在。如果想要反射得到的实例对象能够调用spring管理的bean就不能用clazz.newInstance()
// 如果反射对象类中含有spring管理的bean,那么这个对象不能通过clazz.newInstance()来实例化,需要从spring获取
// ApplicationContext applicationContext = new
// ClassPathXmlApplicationContext("classpath:spring/applicationContext-x.xml");
// Object obj = applicationContext.getBean(apiEnum.getBeanName());
JunitTest调用结果
#################
某些接口有特殊校验时,也可以通过注解annotation,在method.invoke()前进行校验
注解的类:
public class QueryServiceImpl {
@NoNeedAuth
public String queryDetail(JSONObject jsonObject) {
return "success";
}
}