Spring中AOP详解和总结

匿名 (未验证) 提交于 2019-12-03 00:32:02

在上一篇中,介绍了使用代理来实现日志的记录,该方法在平时工作中不易于使用,因为要有一定的设计模式的基础。下面就来介绍下Spring的一个非常核心的概念AOP,即面向切面编程。

为了理解AOP,必须先了解AOP的相关术语:

1、通知(Advice):

在AOP中,切面的工作被称为通知。通知定义了切面“是什么”以及“何时”使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。
Spring切面可以应用5种类型的通知:

(1)前置通知(Before):前置通知, 在方法执行之前执行; (2)后置通知(After):后置通知, 在方法执行之后执行 ; (3)返回通知(After-returning):返回通知, 在方法返回结果之后执行; (4)异常通知(After-throwing):异常通知, 在方法抛出异常之后; (5)环绕通知(Around):环绕通知, 围绕着方法执行; 

连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加行为。

如果说通知定义了切面“是什么”和“何时”的话,那么切点就定义了“何处”。比如我想把日志引入到某个具体的方法中,这个方法就是所谓的切点。

切面是通知和切点的结合。通知和切点共同定义了切面的全部内容―――它是什么,在何时和何处完成其功能。

com.springsource.net.sf.cglib-2.2.0.jar com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar spring-aspects-4.0.0.RELEASE.jar 
<!-- 自动扫描的包 --> <context:component-scan base-package="com.scorpios.spring.aop.impl"></context:component-scan>  <!-- 使 AspectJ 的注解起作用 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 

组件扫描(component scanning):Spring 能够从classpath下自动扫描, 侦测和实例化具有特定注解的组件。

特定组件包括:

@Component: 基本注解, 标识了一个受Spring管理的组件 @Respository: 标识持久层组件 @Service: 标识服务层(业务层)组件 @Controller: 标识表现层组件 

对于扫描到的组件, Spring有默认的命名策略: 使用非限定类名, 第一个字母小写。 也可以在注解中通过value属性值标识组件的名称

public interface ArithmeticCalculator {      int add(int i,int j);     int sub(int i,int j);     int mul(int i, int j);     int div(int i, int j); } 
@Component public class ArithmeticCalculatorImpl implements ArithmeticCalculator {      @Override     public int add(int i, int j) {         int result = i + j;         return result;     }      @Override     public int sub(int i, int j) {         int result = i - j;         return result;     }      @Override     public int mul(int i, int j) {         int result = i * j;         return result;     }      @Override     public int div(int i, int j) {         int result = i / j;         return result;     } } 
@Order(2) @Aspect @Component public class LoggingAspect {      /**      * 定义一个方法,用于声明切入点表达式,一般的,该方法中不再需要其他的代码 使用@Pointcut来声明切入点表达式      * 后面的其他通知直接使用方法名来引用当前的切入点表达式。      */     @Pointcut("execution(public * com.scorpios.spring.aop.impl.ArithmeticCalculator.*(..))")     public void declareJoinPointExpression() {     }      // 声明该方法是一个前置通知:在目标方法开始之前执行     @Before("declareJoinPointExpression()")     public void BeforeMethod(JoinPoint joinPoint) {         String methodName = joinPoint.getSignature().getName();         List<Object> args = Arrays.asList(joinPoint.getArgs());         System.out.println("[BeforeMethod] the method " + methodName + " begins with " + args);     }      // 后置通知:在目标方法执行后(无论是否发生异常),执行的通知     // 在后置通知中还不能访问目标方法执行的结果     @After("declareJoinPointExpression()")     public void afterMethod(JoinPoint joinPoint) {         String methodName = joinPoint.getSignature().getName();         System.out.println("[afterMethod] the method " + methodName + " ends.");     }      // 在方法正常结束时执行的代码     // 返回通知时可以访问到方法的返回值的!     @AfterReturning(value = "declareJoinPointExpression()", returning = "result")     public void afterReturing(JoinPoint joinPoint, Object result) {         String methodName = joinPoint.getSignature().getName();         System.out.println("[afterReturing] the method " + methodName + " ends with :" + result);     }      // 在目标方法出现异常时会执行的代码     // 可以访问到异常对象;且可以指定在出现特定异常时在执行通知代码     @AfterThrowing(value = "declareJoinPointExpression()", throwing = "ex")     public void afterThrowing(JoinPoint joinPoint, Exception ex) {         String methodName = joinPoint.getSignature().getName();         System.out.println("[afterThrowing] the method " + methodName + " occurs with :" + ex);     }      // 环绕通知需要携带ProceedingJoinPoint类型的参数     // 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法     // 且环绕通知必须有返回值,返回值即为目标方法的返回值     @Around("declareJoinPointExpression()")     public Object aroundMethod(ProceedingJoinPoint pjp) {          Object result = null;         String methodName = pjp.getSignature().getName();          try {             // 前置通知             System.out.println(                     "[aroundMethod before] the method " + methodName + " begins with " + Arrays.asList(pjp.getArgs()));             result = pjp.proceed();             // 返回通知             System.out.println("[aroundMethod returning] the method ends with " + result);         } catch (Throwable e) {             // 异常通知             System.out.println("[aroundMethod exception] the method " + methodName + "occurs exception:" + e);         }          // 后置通知         System.out.println("[aroundMethod after] the method " + methodName + " ends");         return result;     }   } 
public class Main {      public static void main(String[] args) {          ApplicationContext apx = new ClassPathXmlApplicationContext("applicationContext.xml");          ArithmeticCalculator arithmeticCalculator = apx.getBean(ArithmeticCalculator.class);          int result = arithmeticCalculator.add(2, 8);         System.out.println("-->" + result);     } } 

@Order(1) @Aspect @Component public class ValidateAspect {      // 声明该方法是一个前置通知:在目标方法开始之前执行     @Before("LoggingAspect.declareJoinPointExpression()")     public void ValidateBefore(JoinPoint joinPoint) {         String methodName = joinPoint.getSignature().getName();         List<Object> args = Arrays.asList(joinPoint.getArgs());         System.out.println("**** validate  " + args);     }  } 

1、引入jar包;

2、在Spring的配置文件中加入aop的命名空间。

3、在配置文件中配置自动扫描的包:

<context:component-scan base-package="com.scorpios.spring.aop.*"></context:component-scan> 

4、加入使 AspjectJ 注解起作用的配置:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>为匹配的类自动生成动态代理对象. 

5、编写切面类:

6、配置切面

a.切面必须是IOC中的bean: 实际添加了@Component注解  b.声明是一个切面: 通过添加@Aspect注解声明一个bean是一个切面! c.声明通知: 即额外加入功能对应的方法.      前置通知: @Before("execution(public int com.scorpios.spring.aop.ArithmeticCalculator.*(int, int))")  @Before 表示在目标方法执行之前执行 @Before 标记的方法的方法体. @Before 里面的是切入点表达式: 

7、在通知中访问连接细节: 可以在通知方法中添加 JoinPoint 类型的参数, 从中可以访问到方法的签名和方法的参数.

关于切入点表达式的解释:

如图所示,我们使用execution()指示器选择UserServiceImpl的sayHello方法。方法表达式以“*”号开始,表明了我们不关心方法返回值的类型。然后,我们指定了全限定类名和方法名。对于方法参数列表,我们使用两个点号(..)表明切点要选择任意的sayHello()方法,无论该方法的入参是什么。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!