AOP的实现
AOP基于xml配置方式实现
Spring基于xml开发AOP
- 定义目标类(接口及实现类)
1 /** 2 * 目标类 3 */ 4 public interface UserService { 5 //业务方法 6 public void getById(); 7 public void add(); 8 public void delete(); 9 public String update(); 10 public void batch(); 11 }
1 public class UserServiceImpl implements UserService { 2 @Override 3 public void getById() { 4 System.out.println("UserServiceImpl类的getById方法被调用..."); 5 //抛出异常 6 System.out.println(1/0); 7 } 8 @Override 9 public void add() { 10 System.out.println("UserServiceImpl类的add方法被调用..."); 11 } 12 @Override 13 public void delete() { 14 System.out.println("UserServiceImpl类的delete方法被调用..."); 15 // System.out.println(1/0); 16 } 17 @Override 18 public String update() { 19 System.out.println("UserServiceImpl类的update方法被调用..."); 20 // System.out.println(1/0); 21 return "update的返回值"; 22 } 23 @Override 24 public void batch() { 25 System.out.println("UserServiceImpl类的batch方法被调用..."); 26 } 27 }
- 定义切面类
1 /** 2 * 切面 3 */ 4 public class UserAspect { 5 //前置通知 6 public void beforeAdvice(JoinPoint jp){ 7 //获取切入点的方法的名称 8 String name = jp.getSignature().getName(); 9 System.out.println("前置通知切入点的方法的名称:"+name); 10 System.out.println("前置通知"); 11 } 12 13 //后置通知 14 public void afterAdvice(JoinPoint jp){ 15 //获取切入点的方法的名称 16 String name = jp.getSignature().getName(); 17 System.out.println("后置通知切入点的方法的名称:"+name); 18 System.out.println("后置通知"); 19 } 20 21 //返回通知 22 //可以获取object类型的返回值 23 //注意:result名称必须和配置文件中的returning的名称保持一致 24 public void afterReturningAdvice(JoinPoint jp,Object result){ 25 //获取切入点的方法的名称 26 String name = jp.getSignature().getName(); 27 System.out.println("返回通知切入点的方法的名称:"+name); 28 //打印返回值 29 System.out.println(result); 30 System.out.println("返回通知"); 31 } 32 33 //异常通知 34 public void exceptionAdvice(JoinPoint jp,Exception ex){ 35 //获取切入点的方法的名称 36 String name = jp.getSignature().getName(); 37 System.out.println("异常通知切入点的方法的名称:"+name); 38 //打印异常信息 39 System.out.println(ex.getMessage()); 40 System.out.println("异常通知"); 41 } 42 43 //环绕通知 44 //必须有一个参数:ProceedingJoinPoint 45 //有返回值:Object 46 public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{ 47 System.out.println("环绕通知前"); 48 //执行业务逻辑方法 49 Object result = pjp.proceed(); 50 //获取切入点的方法的名称 51 String name = pjp.getSignature().getName(); 52 System.out.println("环绕通知切入点的方法的名称:"+name); 53 System.out.println("环绕通知后"); 54 return result; 55 } 56 }
- 配置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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <!-- 把切面和业务对象放到容器中 --> <bean id="userAspect" class="spring.aop.xml.UserAspect"></bean> <bean id="userServiceImpl" class="spring.aop.xml.UserServiceImpl"></bean> <!-- 配置aop的实现 --> <aop:config> <!-- 切入点的配置 expression(切入点表达式):指定切哪个方法 --> <aop:pointcut expression="execution(* *.add*(..))" id="pointCut01"/> <aop:pointcut expression="execution(* *.delete*(..))" id="pointCut02"/> <aop:pointcut expression="execution(* *.update*(..))" id="pointCut03"/> <aop:pointcut expression="execution(* *.getById*(..))" id="pointCut04"/> <aop:pointcut expression="execution(* *.batch*(..))" id="pointCut05"/> <!-- 切面的配置 --> <aop:aspect ref="userAspect"> <!-- 前置通知的配置:在pointCut01切入点的位置添加beforeAdvice的通知 --> <aop:before method="beforeAdvice" pointcut-ref="pointCut01"/> <!-- 后置通知的配置:在pointCut02切入点的位置添加afterAdvice的通知 --> <aop:after method="afterAdvice" pointcut-ref="pointCut02"/> <!-- 返回通知的配置:在pointCut03切入点的位置添加afterReturningAdvice的通知 returning:返回值的参数名称 --> <aop:after-returning method="afterReturningAdvice" pointcut-ref="pointCut03" returning="result"/> <!-- 异常通知的配置:在pointCut04切入点的位置添加exceptionAdvice的通知 throwing:抛出异常的名称,必须和通知上的形参名称一致 --> <aop:after-throwing method="exceptionAdvice" pointcut-ref="pointCut04" throwing="ex"/> <!-- 环绕通知的配置:在pointCut05切入点的位置添加aroundAdvice的通知 --> <aop:around method="aroundAdvice" pointcut-ref="pointCut05"/> </aop:aspect> </aop:config> </beans>
- 测试
1 //使用xml方式配置AOP 2 @RunWith(SpringJUnit4ClassRunner.class) 3 @ContextConfiguration("classpath:applicationContext-aop-xml.xml") 4 public class XmlAopTest { 5 //注入业务对象 6 @Autowired 7 private UserService userService; 8 9 //测试AOP,对切入点进行增强 10 @Test 11 public void test(){ 12 //前置通知的测试 13 // userService.add(); 14 15 //后置通知的测试(无论有没有异常都会执行) 16 // userService.delete(); 17 18 //返回通知的测试(只有业务方法正常执行时,才会执行的通知) 19 // userService.update(); 20 21 //异常通知的测试 22 // userService.getById(); 23 24 //环绕通知的测试 25 userService.batch(); 26 } 27 }
AOP的切入点表达式execution
表达式的写法:修饰关键词 返回值类型 包名.类名.方法名(..)
修饰关键词:protected , public , private ….一般都省略不写
返回值类型:一般返回值类型用 * 号代替,表示任意的返回值都可以
方法名中的两个点:表示所带的参数的个数。
举例如下:
任意以public修饰的方法都可以 execution(public * *(..))
任何以set开头的方法都可以 execution(* set*(..))
任何在AccountService 这个接口下面的方法都可以 execution(* com.xyz.service.AccountService.*(..))
任何在com.xyz.service 这个包下面的所有类的所有方法都可以 execution(* com.xyz.service.*.*(..))
任何在com.xyz.service 这个包以及整个包下面的所有子包的所有类的所有方法都可以 execution(* com.xyz.service..*.*(..)) ..代表这个包及这个包下面的所有子包
|
Spring中的5种通知类型及参数
-
-
-
前置通知:在目标方法开始之前进行执行的通知
参数:JoinPoint
-
后置通知:在目标方法执行之后,无论是否发生异常,都进行执行的通知
参数:JoinPoint
-
返回通知:在目标方法正常结束时,才执行的通知
参数:JoinPoint,Object
-
异常通知:在目标方法出现异常时才会进行执行的通知
参数:JoinPoint,Exception
-
环绕通知:在目标方法执行之前、 之后都会执行的通知
参数:ProceedingJoinPoint(必须有)
-
-
AOP基于注解方式的实现
xml配置中开启spring注解扫描及开启aop注解的自动代理
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <!-- 开启spring的注解驱动 --> <context:component-scan base-package="spring.aop.annotation"></context:component-scan> <!-- 开启aop的自动代理:使用注解实现AOP,这个必须配置 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
定义接口及接口实现类
此处省略,同xml配置方式相同
通过注解的方式定义切面类
1 /** 2 * 切面 3 */ 4 @Component 5 @Aspect 6 public class UserAspect { 7 //前置通知 8 @Before(value = "execution(* *.add*(..))") 9 public void beforeAdvice(JoinPoint jp){ 10 String name = jp.getSignature().getName(); 11 System.out.println("前置通知的切入点的方法名:" + name); 12 System.out.println("前置通知..."); 13 } 14 15 //后置通知 16 @After(value = "execution(* *.delete*(..))") 17 public void afterAdvice(JoinPoint jp){ 18 String name = jp.getSignature().getName(); 19 System.out.println("后置通知的切入点的方法名:" + name); 20 System.out.println("后置通知..."); 21 } 22 23 //返回通知 24 @AfterReturning(value = "execution(* *.update*(..))",returning="result") 25 public void afterReturningAdvice(JoinPoint jp,Object result){ 26 String name = jp.getSignature().getName(); 27 System.out.println("返回通知的切入点的方法名:" + name); 28 System.out.println(result); 29 System.out.println("返回通知..."); 30 } 31 32 //异常通知 33 @AfterThrowing(value = "execution(* *.getById*(..))",throwing="ex") 34 public void exceptionAdvice(JoinPoint jp,Exception ex){ 35 String name = jp.getSignature().getName(); 36 System.out.println("异常通知的切入点的方法名:" + name); 37 System.out.println(ex.getMessage()); 38 System.out.println("异常通知..."); 39 } 40 41 //环绕通知 42 @Around(value = "execution(* *.batch*(..))") 43 public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{ 44 System.out.println("环绕通知前..."); 45 Object result = pjp.proceed(); 46 String name = pjp.getSignature().getName(); 47 System.out.println("环绕通知的切入点的方法名:" + name); 48 System.out.println("环绕通知后..."); 49 return result; 50 } 51 }
测试
1 /** 2 * 注解形式的AOP的单元测试类 3 */ 4 @RunWith(SpringJUnit4ClassRunner.class) 5 @ContextConfiguration("classpath:applicationContext-aop-annotation.xml") 6 public class AnnotationAopTest { 7 @Autowired 8 private UserService userService; 9 10 @Test 11 public void test(){ 12 //注解:前置通知的测试 13 // userService.add(); 14 15 //后置通知的测试 16 // userService.delete(); 17 18 //返回通知 19 // userService.update(); 20 21 //异常通知 22 // userService.getById(); 23 24 //环绕通知 25 userService.batch(); 26 } 27 }