springmvc方法解释器(MultiActionController)使用aop无效
springmvc的MultiActionController类是一个年代比较久远的类了,现在几乎没人用了。
但公司里有些老项目是这样做的,现在要增加一个日志记录的功能,很自然的想到用aop来做。但是不管是用aspectj还是实现advice还是其他的什么方法都不能生效,而且是既不报错也不生效,很头疼,只能自己找源码分析。
通过查看源码发现,MultiActionController的一个最重要的方法是handleRequestInternal,代码如下:
/**
* Determine a handler method and invoke it.
* @see MethodNameResolver#getHandlerMethodName
* @see #invokeNamedMethod
* @see #handleNoSuchRequestHandlingMethod
*/
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
try {
String methodName = this.methodNameResolver.getHandlerMethodName(request);
return invokeNamedMethod(methodName, request, response);
}
catch (NoSuchRequestHandlingMethodException ex) {
return handleNoSuchRequestHandlingMethod(ex, request, response);
}
}
看起来invokeNamedMethod方法中会有些线索:
/**
* Invokes the named method.
* <p>Uses a custom exception handler if possible; otherwise, throw an
* unchecked exception; wrap a checked exception or Throwable.
*/
protected final ModelAndView invokeNamedMethod(
String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception {
Method method = this.handlerMethodMap.get(methodName);
if (method == null) {
throw new NoSuchRequestHandlingMethodException(methodName, getClass());
}
try {
Class<?>[] paramTypes = method.getParameterTypes();
List<Object> params = new ArrayList<Object>(4);
params.add(request);
params.add(response);
if (paramTypes.length >= 3 && paramTypes[2].equals(HttpSession.class)) {
HttpSession session = request.getSession(false);
if (session == null) {
throw new HttpSessionRequiredException(
"Pre-existing session required for handler method '" + methodName + "'");
}
params.add(session);
}
// If last parameter isn't of HttpSession type, it's a command.
if (paramTypes.length >= 3 &&
!paramTypes[paramTypes.length - 1].equals(HttpSession.class)) {
Object command = newCommandObject(paramTypes[paramTypes.length - 1]);
params.add(command);
bind(request, command);
}
Object returnValue = method.invoke(this.delegate, params.toArray(new Object[params.size()]));
return massageReturnValueIfNecessary(returnValue);
}
catch (InvocationTargetException ex) {
// The handler method threw an exception.
return handleException(request, response, ex.getTargetException());
}
catch (Exception ex) {
// The binding process threw an exception.
return handleException(request, response, ex);
}
}
聪明的小伙伴一眼就能看到method对象是从handlerMethodMap中获取的,并且直接拿来被调用了。看起来十有八九问题就出在这个handlerMethodMap上了。他是在哪里注入的呢?ctrl+f找一下就能发现是在构造器里注入的了。
/**
* Constructor for {@code MultiActionController} that looks for
* handler methods in the present subclass.
*/
public MultiActionController() {
this.delegate = this;
registerHandlerMethods(this.delegate);
}
/**
* Registers all handlers methods on the delegate object.
*/
private void registerHandlerMethods(Object delegate) {
this.handlerMethodMap.clear();
this.lastModifiedMethodMap.clear();
this.exceptionHandlerMap.clear();
// Look at all methods in the subclass, trying to find
// methods that are validators according to our criteria
Method[] methods = delegate.getClass().getMethods();
for (Method method : methods) {
// We're looking for methods with given parameters.
if (isExceptionHandlerMethod(method)) {
registerExceptionHandlerMethod(method);
}
else if (isHandlerMethod(method)) {
registerHandlerMethod(method);
registerLastModifiedMethodIfExists(delegate, method);
}
}
}
registerHandlerMethods方法是直接分析委托类(参数delegate),对委托类反射分析,把方法和方法名放进handlerMethodMap里。
但是构造函数里大家看到了,调用registerHandlerMethods方法的时候传递的是this,而不是经过aop增强的代理类,这就是对controller进行增强既不报错又不生效的原因了。
然而MultiActionController还有一个有参构造器,大家可以看一下这个构造器。
public MultiActionController(Object delegate) {
setDelegate(delegate);
}
public final void setDelegate(Object delegate) {
Assert.notNull(delegate, "Delegate must not be null");
this.delegate = delegate;
registerHandlerMethods(this.delegate);
// There must be SOME handler methods.
if (this.handlerMethodMap.isEmpty()) {
throw new IllegalStateException("No handler methods in class [" + this.delegate.getClass() + "]");
}
}
有参构造器直接调用了setDelegate,在setDelegate方法也调用了registerHandlerMethods方法。
看到这里就明白了,无参构造器不支持aop,有参构造器支持aop。
有参构造器的使用方法如下:
<bean id="menuController" name="/menus.php" class="com.lsp.controller.MenuController"></bean>
<bean class="org.springframework.web.servlet.mvc.multiaction.MultiActionController">
<constructor-arg index="0" ref="menuController"></constructor-arg>
</bean>
这样就可以正常使用aop了。
但是这样不太方便,原本写一个bean,现在我要写两个,这里我提供给大家我的解决方案:
1、首先继承MultiActionController类
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
public class BaseController extends MultiActionController implements BeanFactoryAware{
public BaseController() {
super();
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
//从spring容器中获取controller
//因为是从容器中获取的,所以获取到的是AOP增强过的类(如果没有使用AOP当然就是原始类了)
BaseController controller = beanFactory.getBean(this.getClass());
//把委托类设置为从容器中获取的controller
setDelegate(controller);
}
}
2、让原本继承MultiActionController的类改为继承BaseController
public class UsersController extends BaseController {
...
}
3、设置aop的属性proxy-target-class为true,如果没有设置为true会报错,设置这个属性的意思是使用cglib来做代理,而不是jdk的proxy。xml配置如下:
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.lsp.controller.*UsersController.*(..))" id="logPoint"/>
<aop:advisor advice-ref="logAop" pointcut-ref="logPoint"/>
</aop:config>
其他的部分是正常来做就行。如果帮到你了还请点个赞哟!
本文地址:https://blog.csdn.net/Hpluvalbe/article/details/107159441
下一篇: MySQL查询+事务