首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Spring的AOP动态调用的深思

2012-10-12 
Spring的AOP动态调用的反思以往做项目的时候,用到了SPRING的AOP来做权限拦截。但项目里用的是Strut1,用的最

Spring的AOP动态调用的反思
  以往做项目的时候,用到了SPRING的AOP来做权限拦截。但项目里用的是Strut1,用的最多的Action是DispatchAction。所配置好一切以后,竟然发现不能有效的拦截DispatchAction里的各个方法。通过对DispatchAction源码的查看,发现原来DispatchAction是通过动态调用来执行各个不同的方法的~
  代码如下:

public abstract class DispatchAction extends BaseAction/*     */ {/*  98 */   protected static Log log = LogFactory.getLog(DispatchAction.class);/*     */   protected Class clazz;/*     */   protected HashMap methods;/*     */   protected Class[] types;/*     */ /*     */   public DispatchAction()/*     */   {/* 105 */     this.clazz = super.getClass();/*     */ /* 113 */     this.methods = new HashMap();/*     */ /* 119 */     this.types = new Class[] { ActionMapping.class, ActionForm.class, HttpServletRequest.class, HttpServletResponse.class };/*     */   }/*     */ /*     */   public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)/*     */     throws Exception/*     */   {    //首先执行这里,程序的入口/* 145 */     if (isCancelled(request)) {/* 146 */       ActionForward af = cancelled(mapping, form, request, response);/*     */ /* 148 */       if (af != null) {/* 149 */         return af;/*     */       }/*     */ /*     */     }/*     */ /* 154 */     String parameter = getParameter(mapping, form, request, response);  //获取strut配置文件中的parameter的参数--通常为method/*     */ /* 157 */     String name = getMethodName(mapping, form, request, response, parameter);/*     */ /* 161 */     if (("execute".equals(name)) || ("perform".equals(name))) {/* 162 */       String message = messages.getMessage("dispatch.recursive", mapping.getPath());/*     */ /* 165 */       log.error(message);/* 166 */       throw new ServletException(message);/*     */     }/*     */ /* 170 */     return dispatchMethod(mapping, form, request, response, name);  //根据method动态调用方法/*     */   }/*     */ /*     */   protected ActionForward unspecified(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)/*     */     throws Exception/*     */   {/* 191 */     String message = messages.getMessage("dispatch.parameter", mapping.getPath(), mapping.getParameter());/*     */ /* 195 */     log.error(message);/*     */ /* 197 */     throw new ServletException(message);/*     */   }/*     */ /*     */   protected ActionForward cancelled(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)/*     */     throws Exception/*     */   {/* 219 */     return null;/*     */   }/*     */ /*     */   protected ActionForward dispatchMethod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String name)/*     */     throws Exception/*     */   {/* 244 */     if (name == null) {/* 245 */       return unspecified(mapping, form, request, response);/*     */     }/*     */ /* 249 */     Method method = null;/*     */     try/*     */     {/* 252 */       method = getMethod(name);/*     */     } catch (NoSuchMethodException e) {/* 254 */       String message = messages.getMessage("dispatch.method", mapping.getPath(), name);/*     */ /* 257 */       log.error(message, e);/*     */ /* 259 */       String userMsg = messages.getMessage("dispatch.method.user", mapping.getPath());/*     */ /* 261 */       throw new NoSuchMethodException(userMsg);/*     */     }/*     */ /* 264 */     ActionForward forward = null;/*     */     try/*     */     {/* 267 */       Object[] args = { mapping, form, request, response };/*     */ /* 269 */       forward = (ActionForward)method.invoke(this, args);  //这里就是通过动态的调用,来执行各个方法的/*     */     } catch (ClassCastException e) {/* 271 */       String message = messages.getMessage("dispatch.return", mapping.getPath(), name);/*     */ /* 274 */       log.error(message, e);/* 275 */       throw e;/*     */     } catch (IllegalAccessException e) {/* 277 */       String message = messages.getMessage("dispatch.error", mapping.getPath(), name);/*     */ /* 280 */       log.error(message, e);/* 281 */       throw e;/*     */     }/*     */     catch (InvocationTargetException e)/*     */     {/* 285 */       Throwable t = e.getTargetException();/*     */ /* 287 */       if (t instanceof Exception) {/* 288 */         throw ((Exception)t);/*     */       }/* 290 */       String message = messages.getMessage("dispatch.error", mapping.getPath(), name);/*     */ /* 294 */       log.error(message, e);/* 295 */       throw new ServletException(t);/*     */     }/*     */ /* 300 */     return forward;/*     */   }/*     */ /*     */   protected String getParameter(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)/*     */     throws Exception/*     */   {/* 318 */     String parameter = mapping.getParameter();/*     */ /* 320 */     if (parameter == null) {/* 321 */       String message = messages.getMessage("dispatch.handler", mapping.getPath());/*     */ /* 324 */       log.error(message);/*     */ /* 326 */       throw new ServletException(message);/*     */     }/*     */ /* 330 */     return parameter;/*     */   }/*     */ /*     */   protected Method getMethod(String name)/*     */     throws NoSuchMethodException/*     */   {/* 344 */     synchronized (this.methods) {/* 345 */       Method method = (Method)this.methods.get(name);/*     */ /* 347 */       if (method == null) {/* 348 */         method = this.clazz.getMethod(name, this.types);/* 349 */         this.methods.put(name, method);/*     */       }/*     */ /* 352 */       return method;/*     */     }/*     */   }/*     */ /*     */   protected String getMethodName(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String parameter)/*     */     throws Exception/*     */   {/* 374 */     return request.getParameter(parameter);/*     */   }/*     */ }

 
  于是乎对spring的AOP进行测试,想看看到底是不是因为动态调用方法,所以才导致aop无法对DispatchAction 进行有效的拦截。
  测试代码如下:
首先定义一个切入点
@Component("aspectDemo")@Aspectpublic class AspectDemo {// 定义切入点表达式private final static String EXP = "execution(* cn.gs..*.*(..))";@Around(EXP)public Object around(ProceedingJoinPoint point) throws Throwable {System.out.println("Around Before " + point.getSignature().getName());// 调用目标对象的方法并获取返回值Object o = point.proceed(point.getArgs());System.out.println("Around After " + point.getSignature().getName());return o;}}

接着是拦截类的方法
@Service("dynamicProxy")public class DynamicProxy {public Object withMethodToExecute(String name) throws Exception { // 调用了withMethodToExecuteMethod method = getMethod(name);Object obj = method.invoke(this, name); // 此处动态调用了method1return obj;}public Object withMethodToExecute2(String name) throws Exception { // 调用了withMethodToExecutemethod1("");  //直接调用method1return null;}public String method1(String methodName) {System.out.println(" execute method1");return methodName;}public Method getMethod(String name) throws NoSuchMethodException {Method method = this.getClass().getMethod(name, String.class);return method;}}

spring配置文件的设置,开启了自动扫描和动态代理
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:aop="http://www.springframework.org/schema/aop"       xmlns:tx="http://www.springframework.org/schema/tx"       xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd           http://www.springframework.org/schema/context           http://www.springframework.org/schema/context/spring-context-2.5.xsd           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">           <aop:aspectj-autoproxy/><context:component-scan base-package="cn.gs"/>  </beans>

Junit的测试类如下
public class ManTest {static DynamicProxy dynamicProxy;@BeforeClasspublic static void setUpBeforeClass() throws Exception {try {ApplicationContext act = new ClassPathXmlApplicationContext("beans.xml");dynamicProxy = (DynamicProxy) act.getBean("dynamicProxy");} catch (RuntimeException e) {e.printStackTrace();}}@Testpublic void testSay() throws Exception {dynamicProxy.withMethodToExecute("method1");  //动态调用method1System.out.println("=============================");  dynamicProxy.withMethodToExecute2("method1");  //直接调用method1System.out.println("ok");  }}

打印结果
Around Before withMethodToExecute
execute method1
Around After withMethodToExecute
=============================
Around Before withMethodToExecute2
execute method1
Around After withMethodToExecute2
ok
结果发现无法是动态调用method1方法还是直接调用,都无法打印两遍,也就是说我所希望的打印结果应该是这样的
Around Before withMethodToExecute
Around Before method1
execute method1
Around After method1
Around After withMethodToExecute
=============================
Around Before withMethodToExecute2
Around Before method1
execute method1
Around After method1
Around After withMethodToExecute2
ok

事实证明,AOP是无法对方法里面的方法进行递归的拦截。也就是如果一个方法里有多个符合AOP所要拦截的方法的话,那么是无法拦截到里面的方法的。这应该跟AOP的代理实现有关系。
  **头一次发帖!有不对的地方请指正。欢迎拍砖。 1 楼 kingwon 2010-02-27   其实有更深层次的原因:
AspectJ增强编译的字节码时,是不会修改你的方法实现的,凡是调用this.xxx的仍然是原来的方法;反射亦然

也有一个规避的方法:凡是用到this的地方,用从spring得到的对象代替
2 楼 sword.cai 2010-02-28   要想用到spring增强,必须用spring ioc里的proxy bean引用
而this引用到了具体实现proxy.target里具体bean了,所以得不到增强
3 楼 sundoctor 2010-03-01   为什么要递归拦截呢,我个人觉得权限不应该在action上拦截,应该拦截到业务层上就可以了。 4 楼 linsongbin1 2010-03-02   在service上进行拦截合理。 5 楼 YiSingQ 2010-03-02   AOP拦截不应该设在Action上,应该在业务逻辑层的方法上。 6 楼 redhat 2010-03-02   最简单就是做个web filter或者listener。当然在使用struts时可以使用intercepter,这个要看版本是否支持。 7 楼 flynofry 2010-03-03   这点在《spring揭秘》一书中提到了,里面有讲到解决的办法。

热点排行