本文共 10598 字,大约阅读时间需要 35 分钟。
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class BeforeExample { @Before("execution(* com.xyz.myapp.dao.*.*(..))") public void doAccessCheck() { // ... }}b)后置通知(After returning advice) 返回后通知通常在一个匹配的方法返回的时候执行。使用 @AfterReturning 注解来声明:
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterReturning;@Aspectpublic class AfterReturningExample { @AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doAccessCheck() { // ... }}说明:你可以在相同的切面里定义多个通知,或者其他成员。 我们只是在展示如何定义一个简单的通知。这些例子主要的侧重点是正在讨论的问题。 有时候你需要在通知体内得到返回的值。你可以使用@AfterReturning 接口的形式来绑定返回值:
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterReturning;@Aspectpublic class AfterReturningExample { @AfterReturning( pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", returning="retVal") public void doAccessCheck(Object retVal) { // ... } }在 returning属性中使用的名字必须对应于通知方法内的一个参数名。 当一个方法执行返回后,返回值作为相应的参数值传入通知方法。 一个returning子句也限制了只能匹配到返回指定类型值的方法。 (在本例子中,返回值是Object类,也就是说返回任意类型都会匹配) 请注意当使用后置通知时不允许返回一个完全不同的引用。 c)异常通知(After throwing advice) 抛出异常通知在一个方法抛出异常后执行。使用@AfterThrowing注解来声明:
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterThrowing;@Aspectpublic class AfterThrowingExample { @AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doRecoveryActions() { // ... }}你通常会想要限制通知只在某种特殊的异常被抛出的时候匹配,你还希望可以在通知体内得到被抛出的异常。 使用throwing属性不仅可以限制匹配的异常类型(如果你不想限制,请使用 Throwable作为异常类型),还可以将抛出的异常绑定到通知的一个参数上。
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterThrowing;@Aspectpublic class AfterThrowingExample { @AfterThrowing( pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", throwing="ex") public void doRecoveryActions(DataAccessException ex) { // ... }}在throwing属性中使用的名字必须与通知方法内的一个参数对应。 当一个方法因抛出一个异常而中止后,这个异常将会作为那个对应的参数送至通知方法。 throwing 子句也限制了只能匹配到抛出指定异常类型的方法 (上面的示例为DataAccessException)。 d)最终通知(After (finally) advice) 不论一个方法是如何结束的,最终通知都会运行。使用@After 注解来声明。最终通知必须准备处理正常返回和异常返回两种情况。通常用它来释放资源。
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.After;@Aspectpublic class AfterFinallyExample { @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()") public void doReleaseLock() { // ... }}e)环绕通知 最后一种通知是环绕通知。环绕通知在一个方法执行之前和之后执行。它使得通知有机会 在一个方法执行之前和执行之后运行。而且它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。 环绕通知经常在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。 请尽量使用最简单的满足你需求的通知。(比如如果简单的前置通知也可以适用的情况下不要使用环绕通知)。 环绕通知使用@Around注解来声明。通知的第一个参数必须是 ProceedingJoinPoint类型。在通知体内,调用 ProceedingJoinPoint的proceed()方法会导致 后台的连接点方法执行。proceed 方法也可能会被调用并且传入一个 Object[]对象-该数组中的值将被作为方法执行时的参数。 当传入一个Object[]对象的时候,处理的方法与通过AspectJ编译器处理环绕通知略有不同。 对于使用传统AspectJ语言写的环绕通知来说,传入参数的数量必须和传递给环绕通知的参数数量匹配 (不是后台的连接点接受的参数数量),并且特定顺序的传入参数代替了将要绑定给连接点的原始值 (如果你看不懂不用担心)。Spring采用的方法更加简单并且能更好匹配它基于代理(proxy-based)的执行语法, 如果你使用AspectJ的编译器和编织器来编译为Spring而写的@AspectJ切面和处理参数,你只需要知道这一区别即可。 有一种方法可以让你写出100%兼容Spring AOP和AspectJ的表达式,我们将会在后续的通知参数的章节中讨论它。
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.ProceedingJoinPoint;@Aspectpublic class AroundExample { @Around("com.xyz.myapp.SystemArchitecture.businessService()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed();//环绕完了继续环绕给其他的用 // stop stopwatch return retVal; }}方法的调用者得到的返回值就是环绕通知返回的值。 例如:一个简单的缓存切面,如果缓存中有值,就返回该值,否则调用proceed()方法。 请注意proceed可能在通知体内部被调用一次,许多次,或者根本不被调用,所有这些都是合法的。 测试@Before和@AfterReturning:
package cn.edu.hpu.aop;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;@Aspect@Componentpublic class LogInterceptor { //在方法执行之前先执行这个方法 //execution是织入点语法 @Before("execution(public * cn.edu.hpu.dao..*.*(..))") public void before(){ System.out.println("method start"); } @AfterReturning("execution(public * cn.edu.hpu.dao..*.*(..))") public void afterReturning(){ System.out.println("method after ruturning"); } }测试:
package cn.edu.hpu.service;import org.junit.Test;import org.springframework.beans.factory.BeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.edu.hpu.dao.UserDao;import cn.edu.hpu.model.User;public class UserServiceTest { @Test public void testAdd() throws Exception{ ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); UserService userService=(UserService)ctx.getBean("userService"); //System.out.println(userService.getUserDao()); User u=new User(); u.setUsername("u1"); u.setPassword("p1"); userService.add(u); ctx.destroy(); }}结果: method start add success!! method after ruturning 在每一个方法上都要写一个织入点语法,有点麻烦,如果每个织入点语法都是一样的话,我们可以这样写:
package cn.edu.hpu.aop;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;@Aspect@Componentpublic class LogInterceptor { @Pointcut("execution(public * cn.edu.hpu.dao..*.*(..))") public void myMethod(){} //在方法执行之前先执行这个方法 //execution是织入点语法 @Before("myMethod()") public void before(){ System.out.println("method start"); } @AfterReturning("myMethod()") public void afterReturning(){ System.out.println("method after ruturning"); } }下面测试@AfterThrowing: 现在UserDaoImpl中的save方法中添加异常
package cn.edu.hpu.dao.Impl;import java.util.List;import java.util.Map;import java.util.Set;import org.springframework.stereotype.Component;import cn.edu.hpu.dao.UserDao;import cn.edu.hpu.model.User;@Component("u")public class UserDaoImpl implements UserDao{ public void save(User u) { System.out.println("add success!!"); throw new RuntimeException("exception"); }}
package cn.edu.hpu.aop;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;@Aspect@Componentpublic class LogInterceptor { @AfterThrowing("execution(public * cn.edu.hpu.dao..*.*(..))") public void afterThrowing(){ System.out.println("method after throwing"); } }测试:
package cn.edu.hpu.service;import org.junit.Test;import org.springframework.beans.factory.BeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.edu.hpu.dao.UserDao;import cn.edu.hpu.model.User;public class UserServiceTest { @Test public void testAdd() throws Exception{ ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); UserService userService=(UserService)ctx.getBean("userService"); User u=new User(); u.setUsername("u1"); u.setPassword("p1"); userService.add(u); ctx.destroy(); }}测试结果: add success!! method after throwing 编译器也报了异常 我们以后就可以实现"声明式"异常管理了,但实际上都是交给struts2来处理的。 下面测试@Around:
package cn.edu.hpu.aop;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;@Aspect@Componentpublic class LogInterceptor { @Pointcut("execution(public * cn.edu.hpu.dao..*.*(..))") public void myMethod(){} @Before("myMethod()") public void before(){ System.out.println("method start"); } @AfterReturning("myMethod()") public void afterReturning(){ System.out.println("method after ruturning"); } @Around("myMethod()") public void AroundMtethod(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("method around start"); pjp.proceed(); System.out.println("method around end"); } }测试:
package cn.edu.hpu.service;import org.junit.Test;import org.springframework.beans.factory.BeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.edu.hpu.dao.UserDao;import cn.edu.hpu.model.User;public class UserServiceTest { @Test public void testAdd() throws Exception{ ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); UserService userService=(UserService)ctx.getBean("userService"); User u=new User(); u.setUsername("u1"); u.setPassword("p1"); userService.add(u); ctx.destroy(); }}结果: method start method around start add success!! method after ruturning method around end 虽然可以看出一些顺序,但写业务逻辑的时候不要依赖于这个顺序。可以按顺序写好方法再加进去。 注意一些问题: 如果一个类没有继承接口的话,就不能使用动态代理了,此时需要加cglibrary的jar包cglib-nodep-2.1.3.jar。 原因是: 当一个类实现了接口之后,就会使用JDK自带的proxy和InvocationHandler来帮你自动产生代理,没有实现接口的话,它会用直接操作二进制码的这种类库,也就是cglibrary,来帮你产生代理的代码。 最后,我们验证一下我们使用的是代理,而不是原来的类 测试代码:
package cn.edu.hpu.service;import org.junit.Test;import org.springframework.beans.factory.BeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import cn.edu.hpu.dao.UserDao;import cn.edu.hpu.model.User;public class UserServiceTest { @Test public void testAdd() throws Exception{ ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); UserService userService=(UserService)ctx.getBean("userService"); System.out.println(userService.getClass()); ctx.destroy(); }}结果: class cn.edu.hpu.service.UserService$ $ EnhancerByCGLIB $ $c0951629(CSDN对“$”符号很敏感,所以加空格了) 说明使用的是代理。 总结:
整个的Annotation实现AOP编程是属于那种不太重要的东西。脑子里留印象,使用到回来查就可以。但是AOP的原理一定要懂。
转载请注明出处: