`
bdxjl
  • 浏览: 1376 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

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")
@Aspect
public 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 { // 调用了withMethodToExecute
		Method method = getMethod(name);
		Object obj = method.invoke(this, name); // 此处动态调用了method1
		return obj;
	}

	public Object withMethodToExecute2(String name) throws Exception { // 调用了withMethodToExecute
		method1("");  //直接调用method1
		return 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;
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		try {
			ApplicationContext act = new ClassPathXmlApplicationContext("beans.xml");
			dynamicProxy = (DynamicProxy) act.getBean("dynamicProxy");
		} catch (RuntimeException e) {
			e.printStackTrace();
		}
	}

	@Test
	public void testSay() throws Exception {
		dynamicProxy.withMethodToExecute("method1");  //动态调用method1
		System.out.println("=============================");  
		dynamicProxy.withMethodToExecute2("method1");  //直接调用method1
		System.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的代理实现有关系。
  **头一次发帖!有不对的地方请指正。欢迎拍砖。
分享到:
评论
7 楼 flynofry 2010-03-03  
这点在《spring揭秘》一书中提到了,里面有讲到解决的办法。
6 楼 redhat 2010-03-02  
最简单就是做个web filter或者listener。当然在使用struts时可以使用intercepter,这个要看版本是否支持。
5 楼 YiSingQ 2010-03-02  
AOP拦截不应该设在Action上,应该在业务逻辑层的方法上。
4 楼 linsongbin1 2010-03-02  
在service上进行拦截合理。
3 楼 sundoctor 2010-03-01  
为什么要递归拦截呢,我个人觉得权限不应该在action上拦截,应该拦截到业务层上就可以了。
2 楼 sword.cai 2010-02-28  
要想用到spring增强,必须用spring ioc里的proxy bean引用
而this引用到了具体实现proxy.target里具体bean了,所以得不到增强
1 楼 kingwon 2010-02-27  
其实有更深层次的原因:
AspectJ增强编译的字节码时,是不会修改你的方法实现的,凡是调用this.xxx的仍然是原来的方法;反射亦然

也有一个规避的方法:凡是用到this的地方,用从spring得到的对象代替

相关推荐

    Pro Spring 3

    随后,本书的第6至7章介绍了Spring AOP(面向切面编程)的相关概念,以及如何在Spring中实现和使用面向切面编程,同时强调注解在AOP中的重要性。 第8章到第11章分别介绍了Spring对JDBC、Hibernate、JPA和MyBatis等...

    J2EE企业级项目开发-1期 任务3-1 实训项目单.doc

    同时,需要配置Spring的IoC(Inversion of Control)容器和AOP(Aspect Oriented Programming)支持。 3. **使用构造方法和setter方法进行对象注入**:在Spring框架中,依赖注入是核心特性之一,通过构造器或setter...

    网上书店.rar

    Spring作为核心框架,提供了依赖注入(DI)和面向切面编程(AOP),用于管理对象的生命周期和行为;SpringMVC则是Spring框架的一部分,专门处理Web请求和响应,实现了Model-View-Controller设计模式,使得业务逻辑与...

    java面试评价表

    - **IOC与AOP**:介绍依赖注入(IOC)的概念及其优势,以及面向切面编程(AOP)的实现原理与应用场景。 - **Bean的作用域与生命周期**:讨论Bean的生命周期管理及不同作用域下的行为差异。 - **SpringBoot改进**:...

    MVC详解:了解真正所谓的框架

    3. **控制器(Controller)**:控制器是模型和视图之间的桥梁,它接收用户的输入,调用模型进行处理,然后更新视图。控制器处理业务逻辑的细节,确保用户请求正确地传递到相应的地方。 早期的Web开发中,开发者直接...

    java实战技术 培训资料

    Spring框架是企业级应用的常用选择,它涵盖了依赖注入、AOP(面向切面编程)、事务管理等多个方面,简化了复杂系统的构建。 异常处理是Java程序健壮性的保障。理解何时、如何抛出和捕获异常,能够有效防止程序在...

    java程序设计教程课后答案

    - **Spring框架**:企业级应用开发框架,简化了依赖注入和AOP(面向切面编程)。 课后答案可以帮助你验证对这些概念的理解,找出不足之处,加深对Java编程的掌握。同时,分享和讨论这些答案也能促进团队学习,共同...

    java面试八股文总结.pdf

    - **多态**:子类重写父类的方法,根据对象的实际类型动态决定调用哪个版本的方法。 3. **封装**: - **访问修饰符**:public、protected、private等控制成员的访问级别。 - **getter/setter方法**:提供外部...

    BlackBetJava:这是高级Java课程的所有文件夹和类

    5. **反射机制**:反射是Java动态性的重要体现,它允许程序在运行时检查类的信息,并可以动态创建对象和调用方法。在高级Java课程中,反射常用于配置文件的读取、插件机制的实现等。 6. **设计模式**:设计模式是...

Global site tag (gtag.js) - Google Analytics