利用java反射机制,可以让我们在知道类名的情况下获取该类的各类信息,并且能进行实例化,完成对对象的反射调用。本例是基于一个springboot的整合了dubbo的分布式项目,我们在controller做一些改动,跳过controller直接对service进行反射调用
1.创建一个service.json保存服务列表及调用方法
[
{
"serviceId":200,
"serviceName":"top.cwk.dubbo.api.customer.ICustomerService",
"method":"getCustomerById",
"version":"0.1",
"group":"test"
}
]
2.定义一个servletContextListener监听器,服务启动的时候加载service.json并保存为key-map格式,存放到servletContext
/**
* @ClassName ServiceJsonBeanListener
* @Description servlet监听器,初始化将service.json读取到ServletContext
* @Author 维康
* @Date 2018/7/26 16:27
* @Version 1.0
**/
@Configuration
public class ServiceJsonBeanListener implements ServletContextListener {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static Map<Long,ServiceJsonBean> SERVICE_MAP = new HashMap<>();
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
File file = null;
try {
file = file = ResourceUtils.getFile("classpath:service.json");
String jsonStr = BaseUtils.readFileContent(file);
JSONArray array = JSONArray.fromObject(jsonStr);
ServiceJsonBean[] arrayBean = (ServiceJsonBean[]) JSONArray.toArray(array,ServiceJsonBean.class);
List<ServiceJsonBean> list = CollectionUtils.arrayToList(arrayBean);
try{
//将list转换为以serviceId 为主键的Map
SERVICE_MAP = list.parallelStream().collect(
Collectors.toMap(ServiceJsonBean::getServiceId, bean -> bean)
);
}catch (IllegalStateException e){
logger.error("service.json 配置出错,serviceId不得重复!",e);
}
servletContextEvent.getServletContext().setAttribute("SERVICE_MAP" , SERVICE_MAP);
logger.debug("读取 service.json : "+SERVICE_MAP.toString());
} catch (FileNotFoundException e2) {
logger.error(" service.json 配置出错,找不到文件!",e2);
}
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
3. 在controller添加一个拦截器,指定这个路径的请求全部走我们自定的处理流程(跳过controller)
/**
* @ClassName RouterRequestInterceptor
* @Description RouterRequestInterceptor
* @Author 维康
* @Date 2018/7/26 16:08
* @Version 1.0
**/
public class RouterRequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
return RouterToService.routerService(request,response);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {
}
}
3.1 RouterToService根据serviceId匹配服务,然后进行反射调用服务
/**
* @ClassName RouterToService
* @Description RouterToService 匹配服务,反射调用
* @Author 维康
* @Date 2018/7/28 19:59
* @Version 1.0
**/
public class RouterToService {
private static Map<Long,ServiceJsonBean> SERVICE_MAP = new HashMap<>();
static public final boolean routerService(HttpServletRequest request, HttpServletResponse response) throws IOException,
ClassNotFoundException,
InvocationTargetException,
IllegalAccessException,
InstantiationException {
SERVICE_MAP = (Map<Long, ServiceJsonBean>) request.getServletContext().getAttribute("SERVICE_MAP");
String serviceIdStr = request.getParameter("serviceId");
ServiceJsonBean serviceJsonBean = null;
response.setCharacterEncoding("UTF-8");
PrintWriter printWriter = response.getWriter();
if(serviceIdStr!=null){
Long serviceId = Long.valueOf(serviceIdStr);
serviceJsonBean = SERVICE_MAP.get(serviceId);
if(serviceJsonBean==null){
printWriter.write("ERROR_101 : No interface was found based on serviceId="+serviceId+"!");
return false;
}
}else{
printWriter.write("ERROR_100 : The parameter [serviceId] should not be empty!");
return false;
}
System.out.println(serviceJsonBean.toString());
//反射获取请求的接口
Class clz = Class.forName(serviceJsonBean.getServiceName());
//根据class从spring容器获取对象
Object obj = SpringContextUtil.getBean(clz);
//反射获取该对象的所有方法
Method[] methods = obj.getClass().getDeclaredMethods();
String methodName = serviceJsonBean.getMethod();
boolean findMethod = false;
//遍历方法找到要执行的方法
for (int i=0 ; i<methods.length ;i++){
if(methodName.equals(methods[i].getName())){
Method method = methods[i];
Class[] parameterTypes = method.getParameterTypes();
Object object = "";
if(parameterTypes.length == 0){
//该方法为无参方法
object = method.invoke(obj);
}else{
if(parameterTypes.length == 1){
Class parameterType = parameterTypes[0];
//参数类型的名称
String simpleName = parameterType.getSimpleName();
//当参数为String/long/Integer/Date之类的时候 我们默认取参数名称为data
if(FieldTypeConstants.FIELD_TYPE_MAP.containsKey(simpleName)){
//这里默认一个参数的时候,默认字段为data 只能填 serviceId=100&data=xxx
//因为虚拟机编译后的字节码,参数名称会变成arg0..arg1之类的
String paramVal = request.getParameter("data");
if(paramVal!=null){
object = invokeSimpleParam(obj,method,simpleName,paramVal);
}else{
object = "ERROR_003 : The parameter [data] should not be empty!";
}
}else{
//服务当参数为一个自定义的对象时,反射获取对象,并将request的参数映射到对象的里的字段
Object arg = FieldUtil.setFieldValue(parameterType.getTypeName(),paramMapSet(request,parameterType.getTypeName()));
object = method.invoke(obj ,arg);
}
}else{
System.err.println("暂不支持接口方法多参数调用,如果有多个参数,请封装为BO!");
object = "ERROR_002 : Method parameter too much! Only one parameter is allowed !";
}
}
printResult(object , printWriter);
findMethod = true;
break;
}
}
if(!findMethod){
String result = "ERROR_001 :No matched method ["+serviceJsonBean.getMethod()+"] from service "+serviceJsonBean.getServiceName()+"!";
printWriter.write(result);
}
return false;
}
/**
* 参数为简单类型的时候
*/
static private final Object invokeSimpleParam(Object invObject , Method method , String paramType , String paramValue) {
Object object = null;
try {
if(FieldTypeConstants.TYPE_STRING.equals(paramType)){
object = method.invoke(invObject , paramValue);
}
if(FieldTypeConstants.TYPE_LONG.equals(paramType)){
object = method.invoke(invObject , Long.parseLong(paramValue));
}
if(FieldTypeConstants.TYPE_INT.equals(paramType) || FieldTypeConstants.TYPE_INTEGER.equals(paramType) ){
object = method.invoke(invObject , Integer.parseInt(paramValue));
}
if(FieldTypeConstants.TYPE_BOOLEAN.equals(paramType)){
object = method.invoke(invObject , Boolean.parseBoolean(paramValue));
}
if(FieldTypeConstants.TYPE_DATE.equals(paramType)){
object = method.invoke(invObject , FieldUtil.parseDate(paramValue));
}
if(FieldTypeConstants.TYPE_DOUBLE.equals(paramType)){
object = method.invoke(invObject , Double.parseDouble(paramValue));
}
if(FieldTypeConstants.TYPE_FLOAT.equals(paramType)){
object = method.invoke(invObject , Float.parseFloat(paramValue));
}
} catch (IllegalAccessException e) {
System.err.println("IllegalAccessException:"+e.getMessage());
object = "ERROR_000 : IllegalAccessException:"+e.getMessage();
} catch (InvocationTargetException e) {
System.err.println("InvocationTargetException:"+e.getMessage());
object = "ERROR_000 : InvocationTargetException:"+e.getMessage();
}
return object;
}
/**
* 将request请求的参数封装为Map
* @param request
* @param paramTypeName
* @return
* @throws ClassNotFoundException
*/
static private final Map<String,String> paramMapSet(HttpServletRequest request, String paramTypeName) throws ClassNotFoundException {
Map<String,String> map = new HashMap<>();
Field[] fields = Class.forName(paramTypeName).getDeclaredFields();
for (int i=0 ; i<fields.length ;i++){
String filedName = fields[i].getName();
map.put(filedName,request.getParameter(filedName));
}
return map;
}
/**
* 返回json格式的结果
* @param object
* @param printWriter
*/
static private final void printResult(Object object , PrintWriter printWriter){
String returnTypeName = object.getClass().getSimpleName();
if(FieldTypeConstants.FIELD_TYPE_MAP.containsKey(returnTypeName)){
String resStr = "";
if(FieldTypeConstants.TYPE_DATE.equals(returnTypeName)){
//时间格式需要特殊转换
resStr = FieldUtil.fmtDate((Date)object);
}else {
resStr = object.toString();
}
printWriter.write(resStr);
}else{
JSONObject jsonObject = JSONObject.fromObject(object);
printWriter.write(jsonObject.toString(4));
}
}
}
3.2 SpringContextUtil 从容器获取bean
/**
* @ClassName SpringContextUtil
* @Description SpringContextUtil
* @Author 维康
* @Date 2018/7/26 18:33
* @Version 1.0
**/
@Configuration
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
public static ApplicationContext getCtx() {
return SpringContextUtil.applicationContext;
}
public static T getBean(Class t) {
return SpringContextUtil.applicationContext.getBean(t);
}
}
3.3 FieldUtil工具类,给入参赋值
/**
* @ClassName FieldUtil
* @Description 参数赋值
* @Author 维康
* @Date 2018/7/27 17:38
* @Version 1.0
**/
public class FieldUtil {
/**
* set属性的值到Bean
* @param beanTypeName
* @param valMap
*/
public static Object setFieldValue(String beanTypeName, Map<String, String> valMap) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> cls = Class.forName(beanTypeName);
Object bean = cls.newInstance();
// 取出bean里的所有方法
Method[] methods = cls.getDeclaredMethods();
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
try {
String fieldSetName = parSetName(field.getName());
if (!checkSetMet(methods, fieldSetName)) {
continue;
}
Method fieldSetMet = cls.getMethod(fieldSetName, field.getType());
String value = valMap.get(field.getName());
if (null != value && !"".equals(value)) {
String fieldType = field.getType().getSimpleName();
if (FieldTypeConstants.TYPE_STRING.equals(fieldType)) {
fieldSetMet.invoke(bean, value);
} else if (FieldTypeConstants.TYPE_DATE.equals(fieldType)) {
Date temp = parseDate(value);
fieldSetMet.invoke(bean, temp);
} else if (FieldTypeConstants.TYPE_INTEGER.equals(fieldType) || FieldTypeConstants.TYPE_INT.equals(fieldType)) {
Integer intval = Integer.parseInt(value);
fieldSetMet.invoke(bean, intval);
} else if (FieldTypeConstants.TYPE_LONG.equalsIgnoreCase(fieldType)) {
Long temp = Long.parseLong(value);
fieldSetMet.invoke(bean, temp);
} else if (FieldTypeConstants.TYPE_DOUBLE.equalsIgnoreCase(fieldType)) {
Double temp = Double.parseDouble(value);
fieldSetMet.invoke(bean, temp);
} else if (FieldTypeConstants.TYPE_BOOLEAN.equalsIgnoreCase(fieldType)) {
Boolean temp = Boolean.parseBoolean(value);
fieldSetMet.invoke(bean, temp);
} else if (FieldTypeConstants.TYPE_FLOAT.equalsIgnoreCase(fieldType)) {
Float temp = Float.parseFloat(value);
fieldSetMet.invoke(bean, temp);
} else {
System.err.println("not supper type" + fieldType);
}
}
} catch (Exception e) {
continue;
}
}
return bean;
}
/**
* 判断是否存在某属性的 set方法
* @param methods
* @param fieldSetMet
* @return boolean
*/
private static boolean checkSetMet(Method[] methods, String fieldSetMet) {
for (Method met : methods) {
if (fieldSetMet.equals(met.getName())) {
return true;
}
}
return false;
}
/**
* 判断是否存在某属性的 get方法
* @param methods
* @param fieldGetMet
* @return boolean
*/
private static boolean checkGetMet(Method[] methods, String fieldGetMet) {
for (Method met : methods) {
if (fieldGetMet.equals(met.getName())) {
return true;
}
}
return false;
}
/**
* 拼接某属性的 get方法
* @param fieldName
* @return String
*/
private static String parGetName(String fieldName) {
if (null == fieldName || "".equals(fieldName)) {
return null;
}
return "get" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
}
/**
* 拼接在某属性的 set方法
* @param fieldName
* @return String
*/
private static String parSetName(String fieldName) {
if (null == fieldName || "".equals(fieldName)) {
return null;
}
return "set" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
}
/**
* 格式化string为Date
* @param datestr
* @return date
*/
public static Date parseDate(String datestr) {
if (null == datestr || "".equals(datestr)) {
return null;
}
String fmtstr = null;
if (datestr.indexOf(':') > 0) {
fmtstr = "yyyy-MM-dd HH:mm:ss";
} else {
fmtstr = "yyyy-MM-dd";
}
SimpleDateFormat sdf = new SimpleDateFormat(fmtstr, Locale.UK);
try {
return sdf.parse(datestr);
} catch (ParseException e) {
return null;
}
}
/**
* 日期转化为String
* @param date
* @return date string
*/
public static String fmtDate(Date date) {
if (null == date) {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.SIMPLIFIED_CHINESE);
return sdf.format(date);
}
}
3.4 最后写一个通用的controller
/**
* @ClassName RouterController
* @Description RouterController
* @Author 维康
* @Date 2018/7/27 14:28
* @Version 1.0
**/
@RequestMapping("/router")
@Controller
public class RouterController {
@RequestMapping("/service")
public String service(HttpServletRequest request){
return "index";
}
}
4. 效果展示
此时拦截器拦截的路径为/router/service,那么我们请求这个服务就会按我们的流程处理。
这个时候只要写好了服务,然后在service.json配置好serviceName以及method,然后在请求的时候携带上servceId以及参数就行,不用再去写一个Controller了!
注意 : serviceId是全局唯一的




