webx在启动时通过:
com.alibaba.citrus.webx.servlet.WebxFrameworkFilter.init()==>ServletContext. getAttribute(attrName)
获取WebxComponentsContext。
后续调用过程:
WebxRootControllerImpl. handleRequest()
getComponents().findMatchedComponent(path).getWebxController().service(requestContext)
在service中:
PipelineInvocationHandle. Invoke()==>valve.invoke(this)
valve就是在pipeline中配置的阀门。
由于我们需要在screen层加一层aop:
<aop:config> <aop:aspect ref="requestParamCheck"> <aop:pointcut id="screenPointcut" expression="execution(public * com.x.z.*.module.screen.*.*(..))" /> <aop:before method="check" pointcut-ref="screenPointcut" /> </aop:aspect> </aop:config>
这时,如果screen实现的有接口,则screen所对应的bean应该是通过jdk动态代理创建的。例如:LoginScreen implements ILogin
//空接口 public interface ILogin { }
那边LoginScreen的bean实例:org.springframework.aop.framework.JdkDynamicAopProxy@a634f0e
这时就会出现异常:
Failed to process request /loginScreen/abc.htm, the r oot cause was UnadaptableModuleException: Could not adapt object to module: type=screen, name=LoginS creen, class=class com.sun.proxy.$Proxy8
抛出异常的代码:
// 从factory中装载
Object moduleObject = null;
Module module = null;
for (ModuleFactory factory : factories) {
moduleObject = factory.getModule(moduleType, moduleName);
if (moduleObject != null) {
break;
}
}
//此时 moduleObject 被赋值为LoginScreen的jdk动态代理实例org.springframework.aop.framework.JdkDynamicAopProxy@a634f0e
// 通过适配器转换接口
if (moduleObject != null) {
if (moduleObject instanceof Module) {
module = (Module) moduleObject; // 假如moduleObject直接实现了接口
} else {
for (ModuleAdapterFactory adapter : adapters) {
module = adapter.adapt(moduleType, moduleName, moduleObject);
if (module != null) {
break;
}
}
}
}
if (module == null) {
if (moduleObject != null) {
throw new UnadaptableModuleException("Could not adapt object to module: type=" + moduleType + ", name="
+ moduleName + ", class=" + moduleObject.getClass());
}
}
应该是 adapter.adapt(moduleType, moduleName, moduleObject)出现了问题
public Module adapt(String type, String name, Object moduleObject) {
if (!isAdaptableType(type)) {
return null;
}
Class<?> moduleClass = moduleObject.getClass();
Map<String, Method> handlers = getEventHandlers(moduleClass);
if (handlers == null) {//debug发现,是这里返回了null
return null;
}
ModuleInfo moduleInfo = new ModuleInfo(type, name);
FastClass fc = FastClass.create(moduleClass);
Map<String, MethodInvoker> fastHandlers = createHashMap(handlers.size());
for (Map.Entry<String, Method> entry : handlers.entrySet()) {
FastMethod fm = fc.getMethod(entry.getValue());
fastHandlers.put(entry.getKey(), getMethodInvoker(fm, moduleInfo, isEventHandlerSkippable()));
}
FastMethod preHandler = getFastMethod(fc, PRE_HANDLER);
FastMethod postHanlder = getFastMethod(fc, POST_HANDLER);
AbstractModuleEventAdapter adapter = createAdapter(moduleObject,
fastHandlers,
getMethodInvoker(preHandler, moduleInfo, false),
getMethodInvoker(postHanlder, moduleInfo, false));
try {
// 注入并初始化adapter(不是注入moduleObject,后者取决于factory的设置)
autowireAndInitialize(adapter, context, AbstractBeanDefinition.AUTOWIRE_AUTODETECT, type + "." + name);
} catch (Exception e) {
throw new ModuleLoaderException("Failed to configure module adapter", e);
}
return adapter;
}
if (handlers == null) {//debug发现,是这里返回了null
return null;
}
再看看getEventHandlers():
/** 取得事件处理方法。 */
private Map<String, Method> getEventHandlers(Class<?> moduleClass) {
Map<String, Method> handlers = null;
for (Method method : moduleClass.getMethods()) {
if (checkMethod(method)) {
String methodName = method.getName();
// doXyz()
if (methodName.length() > 2 && methodName.startsWith("do")//对以do开头的方法做map,以处理请求
&& Character.isUpperCase(methodName.charAt(2))) {
String eventName = toCamelCase(methodName.substring(2));
// default handler: doPerform()
if (DEFAULT_EVENT.equals(eventName)) {
eventName = null;
}
if (handlers == null) {
handlers = createHashMap();
}
handlers.put(eventName, method);
}
}
}
return handlers;
}
由于jdk动态代理产生的实例包含的方法来源于其所实现的接口,而ILogin接口中没有任何方法,更不会有以do开头的方法,所以getEventHandlers()返回null,导致异常。。
解决方案:
如果不想对screen中的所有do*()都定义接口,那么可以采用cglib做代理,只需要保证screen没有实现任何接口,spring就会自动使用cglib做代理,cglib是通过创造子类来实现代理的,所以代理子类肯定有do*()方法,getEventHandlers()就会返回do开头的方法map,就能够正常处理请求。