Before 前置处理

在一个切面类里使用@Before来修饰一个方法,该方法将作为Before增强处理。使用@Before修饰时,通常需要指定一个value属性值,该属性指定一个切点表达式(既可以是一个已有的切入点,也可以直接定义切入点表达式),用于指定该增强处理将被织入哪些切入点。

下面定义的java类里使用@Before定义了一个Before增强处理:

  1. // 定义一个切面
  2. @Aspect
  3. public class AuthAspect
  4. {
  5. // 匹配com.owen.app.service.impl包下所有类的
  6. // 所有方法的执行作为切入点
  7. @Before("execution(* com.owen.app.service.impl.*.*(..))")
  8. public void authority()
  9. {
  10. System.out.println("模拟执行权限检查");
  11. }
  12. }

在com.owen.app.service.impl下有两个类分别是HelloImple.java和WorldImpl.java的类。

  1. HelloImple.java
  1. @Component("hello")
  2. public class HelloImpl implements Hello
  3. {
  4. // 定义一个简单方法,模拟应用中的业务逻辑方法
  5. public void foo()
  6. {
  7. System.out.println("执行Hello组件的foo()方法");
  8. }
  9. // 定义一个addUser()方法,模拟应用中的添加用户的方法
  10. public int addUser(String name , String pass)
  11. {
  12. System.out.println("执行Hello组件的addUser添加用户:" + name);
  13. return 20;
  14. }
  15. }
  1. WorldImpl.java
  1. @Component("world")
  2. public class WorldImpl implements World
  3. {
  4. // 定义一个简单方法,模拟应用中的业务逻辑方法
  5. public void bar()
  6. {
  7. System.out.println("执行World组件的bar()方法");
  8. }
  9. }

从上面的两个类来看,他们都是普通的类,它丝毫不知道将被谁进行增强,也不知道将被进行怎样的增强。下面,在Spring配置文件中配置自动搜索Bean组件、自动搜索切面类,Spring AOP自动对Bean组件进行增强。下面是Spring配置文件代码。(如果后序的AOP中无特殊说明,则用到的文件与这一份相同)

  1. <?xml version="1.0" encoding="GBK"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context-4.0.xsd
  10. http://www.springframework.org/schema/aop
  11. http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
  12. <!-- 指定自动搜索Bean组件、自动搜索切面类 -->
  13. <context:component-scan base-package="com.owen.app.service
  14. , com.owen.app.aspect">
  15. <context:include-filter type="annotation"
  16. expression="org.aspectj.lang.annotation.Aspect"/>
  17. </context:component-scan>
  18. <!-- 启动@AspectJ支持 -->
  19. <aop:aspectj-autoproxy/>
  20. </beans>

下面写一个测试类。

  1. public class BeanTest
  2. {
  3. public static void main(String[] args)
  4. {
  5. // 创建Spring容器
  6. ApplicationContext ctx = new
  7. ClassPathXmlApplicationContext("beans.xml");
  8. Hello hello = ctx.getBean("hello" , Hello.class);
  9. hello.foo();
  10. hello.addUser("owen" , "7788");
  11. World world = ctx.getBean("world" , World.class);
  12. world.bar();
  13. }
  14. }

运行结果如下。

  1. [java]信息:Loading XML bean definitions from class path resource
  2. [java] 模拟执行权限检查
  3. [java] 执行Hello组件的foo()方法
  4. [java] 模拟执行权限检查
  5. [java] 执行Hello组件的addUser添加用户:owen
  6. [java] 模拟执行权限检查
  7. [java] 执行World组件的bar()方法

总结:

使用Before增强处理只能在目标方法执行之前织入增强,如果Before增强处理没有特殊处理,目标方法总会自动执行,如果Before处需要阻止目标方法的执行,可通过抛出一个异常来实现。Before增强处理执行时,目标方法还未获得执行的机会,所以Before增强处理无法访问目标方法的返回值。

Around 增强处理

@Around 注解用于修饰Around增强处理,Around增强处理是功能比较强大的增强处理,它近似于Before增强处理和AfterReturing增强处理的总结,Around增强处理既可在执行目标方法之前增强动作,也可在执行目标方法之后织入增强的执行。
与Before增强处理、AfterReturning增强处理不同的是,Around增强处理可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标方法的执行。

当定义一个Around增强处理方法时,该方法的第一个形参必须是 ProceedJoinPoint 类型(至少含有一个形参),在增强处理方法体内,调用 ProceedingJoinPoint 参数的 proceed() 方法才会执行目标方法——这就是Around增强处理可以完全控制方法的执行时机、如何执行的关键;如果程序没有调用 ProceedingJoinPoint 参数的 proceed() 方法,则目标方法不会被执行。

Demo Example

服务类:

  1. package com.haan.springdemo.annotation.aop;
  2. public class UserService {
  3. public String sayHello(String name){
  4. System.out.println("UserService.sayHello()执行,"+name + ":hello!");
  5. return name;
  6. }
  7. }

切面类:

  1. package com.haan.springdemo.annotation.aop;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. import org.aspectj.lang.Signature;
  5. import org.aspectj.lang.annotation.*;
  6. /**
  7. * @Aspect: 告诉Spring当前类是一个切面类
  8. */
  9. @Aspect
  10. public class LogAspect {
  11. /**
  12. * 抽取公共的切面
  13. * 使用:
  14. * (1)本类中使用:pointCut()
  15. * (2)其他类中使用:com.haan.springdemo.annotation.aop.LogAspect.pointCut()
  16. */
  17. @Pointcut(value = "execution(public * com.haan.springdemo.annotation.aop.UserService.*(..))")
  18. public void pointCut(){
  19. }
  20. /**
  21. * 前置通知 @Before 在目标方法之前切入
  22. * value:切入表达式(指定那个方法切入)
  23. */
  24. @Before("pointCut()")
  25. public void before(JoinPoint joinPoint){
  26. Signature signature = joinPoint.getSignature();
  27. String signatureName = signature.getName();
  28. System.out.println("测试before...,方法名:"+signatureName);
  29. }
  30. /**
  31. * 后置通知 @After 在目标方法之后切入
  32. * value:切入表达式(指定那个方法切入)
  33. */
  34. @After("com.haan.springdemo.annotation.aop.LogAspect.pointCut()")
  35. public void after(JoinPoint joinPoint){
  36. Signature signature = joinPoint.getSignature();
  37. String signatureName = signature.getName();
  38. System.out.println("测试after...,方法名:"+signatureName);
  39. }
  40. /**
  41. * 返回通知 @AfterReturning
  42. * 程序执行无异常,返回结果后切入
  43. */
  44. @AfterReturning(value = "pointCut()",returning = "result")
  45. public void afterReturning(JoinPoint joinPoint,Object result){
  46. Signature signature = joinPoint.getSignature();
  47. String signatureName = signature.getName();
  48. System.out.println("测试after returning...,方法名:"+signatureName+",return:"+ (String) result);
  49. }
  50. /**
  51. * 异常通知 @AfterThrowing
  52. * 程序执行出现异常,切入通知
  53. */
  54. @AfterThrowing(value = "pointCut()",throwing = "exception")
  55. //JoinPoint一定要出现在参数表的第一位
  56. public void afterThrowing(JoinPoint joinPoint,Exception exception){
  57. Signature signature = joinPoint.getSignature();
  58. String signatureName = signature.getName();
  59. System.out.println("测试after throwing...,方法名:"+signatureName);
  60. }
  61. /**
  62. * 环绕通知 @Around
  63. *
  64. */
  65. @Around("pointCut()")
  66. public void around(ProceedingJoinPoint pj) throws Throwable {
  67. String signatureName = pj.getSignature().getName();
  68. System.out.println("测试around...执行前,方法名:"+ signatureName);
  69. pj.proceed();
  70. System.out.println("测试around...执行后,方法名:"+ signatureName);
  71. }
  72. }

主程序:

  1. package com.haan.springdemo.annotation.aop;
  2. import org.aspectj.lang.annotation.Before;
  3. import org.springframework.context.ApplicationContext;
  4. import org.springframework.context.ConfigurableApplicationContext;
  5. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.context.annotation.EnableAspectJAutoProxy;
  9. @Configuration
  10. @EnableAspectJAutoProxy
  11. public class TestAopConfiguration {
  12. @Bean
  13. public LogAspect logAspect(){
  14. return new LogAspect();
  15. }
  16. @Bean
  17. public UserService userService(){
  18. return new UserService();
  19. }
  20. public static void main(String[] args) {
  21. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestAopConfiguration.class);
  22. UserService userService = context.getBean(UserService.class);
  23. userService.sayHello("libai");
  24. }
  25. }

执行结果:

  1. 测试around...执行前,方法名:sayHello
  2. 测试before...,方法名:sayHello
  3. UserService.sayHello()执行,libai:hello!
  4. 测试after returning...,方法名:sayHello,return:libai
  5. 测试after...,方法名:sayHello
  6. 测试around...执行后,方法名:sayHello