我们可以从下面的代码中看到Jarslink扫描Action是从已经加载的ApplicationContext中获取指定的bean中加载,我们可以通过我们的自定义注解的方式加在每个模块需要通信的类上面实现分发请求的功能。
/**
* 请求类注解
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ActionClass {
String value();
String name();
}
/**
* 请求方法注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ActionMethod {
String value();
String name();
String description() default "";
}
对于上面的两个注解,我个人倾向于将他们理解为spring中的@Controller和@XXMapping的关系,接下来我们来看jarslink中是如何扫描到对应注解的
private Map<String, com.alipay.jarslink.api.ActionMethod> scanActions(ApplicationContext applicationContext, Class<ActionClass> type){
Map<String, com.alipay.jarslink.api.ActionMethod> actions = Maps.newHashMap();
//获取Spring容器内的ActionService类
for (Object action : applicationContext.getBeansWithAnnotation(type).values()) {
Object target=null;
//获取代理对象的目标对象
try {
target= AopTargetUtils.getTarget(action);
} catch (Exception e) {
throw new ModuleRuntimeException("JarsLink scanActions actionName is null");
}
//获取url第一层路径
String actionValue = target.getClass().getAnnotation(type).value();
//获取方法名称
String actionName=target.getClass().getAnnotation(type).name();
// if (isBlank(actionValue)) {
// throw new ModuleRuntimeException("JarsLink scanActions value is null");
// }
if (isBlank(actionName)) {
throw new ModuleRuntimeException("JarsLink "+actionValue+" scanActions name is null");
}
//添加actionClassEntity信息(此处忽略存储对应实体信息过程)
//获取ActionClass注解的类中所有方法
Method[] methods=target.getClass().getMethods();
List<ActionMethodEntity> actionMethodEntityList=new ArrayList<>();
for(Method method:methods){
//如果方法使用了ActionMethod添加的注解则需要被暴露出去的接口
Annotation annotation=method.getAnnotation(ActionMethod.class);
if(annotation instanceof ActionMethod){
//通过注解获取method基本信息
String methodValue=((ActionMethod) annotation).value();
String methodName=((ActionMethod)annotation).name();
String methodDescription=((ActionMethod)annotation).description();
if (isBlank(methodValue)) {
throw new ModuleRuntimeException("JarsLink scanActions methodValue is null");
}
if (isBlank(methodName)) {
throw new ModuleRuntimeException("JarsLink "+methodValue+" scanActions methodName is null");
}
//添加ActionMethod信息(此处忽略存储ActionMethod实体过程)
String key;
if(actionValue.equalsIgnoreCase("/")||actionValue.equalsIgnoreCase("")){
key=methodValue.toUpperCase(Locale.CHINESE);
}else{
key=(actionValue+"/"+methodValue).toUpperCase(Locale.CHINESE);
}
//将路由/actionService/actionMethod 映射到类对象,方法对象上,便于反射调用
actions.put(key, actionMethod);
}
}
}
return actions;
}
扫描到对应的类和方法后,将类和方法的value值映射为唯一路由,后期实现通过路由获取对应的Action来进行反射调用。
protected <R, T> T doActionWithinModuleClassLoader(com.alipay.jarslink.api.ActionMethod actionMethod, R actionRequest) {
checkNotNull(actionMethod, "action is null");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
ClassLoader moduleClassLoader = actionMethod.getAction().getClass().getClassLoader();
Thread.currentThread().setContextClassLoader(moduleClassLoader);
if(actionRequest==null){
Method method= BeanUtils.findMethod(actionMethod.getAction().getClass(),actionMethod.getMethod().getName());
return (T) method.invoke(actionMethod.getAction());
}
Method method=null;
if(actionRequest instanceof String){
method= BeanUtils.findMethod(actionMethod.getAction().getClass(),actionMethod.getMethod().getName(),String.class);
}else if(actionRequest instanceof Map){
method= BeanUtils.findMethod(actionMethod.getAction().getClass(),actionMethod.getMethod().getName(),Map.class);
}
return (T) method.invoke(actionMethod.getAction(),actionRequest);
} catch (Exception e) {
LOGGER.error("调用模块出现异常,action=" + actionMethod, e);
throw new ModuleRuntimeException("doActionWithinModuleClassLoader has error,action=" + actionMethod, e);
} finally {
Thread.currentThread().setContextClassLoader(classLoader);
}
}
版权声明:本文为bigdogLIU原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。