2、Spring之AOP

AOP术语

  • 通知:定义了切面是什么以及何时使用。除了要描述页面要完成的工作,通知还解决了何时执行这个工作的问题。
  • 连接点:连接点是一个物理的存在。这个点可以是调用方法时、抛出异常时、甚至是修改一个字段时。
  • 切点:如果通知定义了切面是”什么“和”何时“,那么切点定义了”何处“。切点的定义会匹配通知所要织入的一个或者多个连接点。通常使用明确的类名和方法名来指定这些切点,或者使用正则表达式定义匹配的类和方法名模式来指定这些切点。
  • 切面:切面是切点和通知的组合。

配置AOP

<!-- 配置切面的Bean -->
    <bean id="loggingAspect" class="com.test.spring.aop.xml.LoggingAspect"></bean>
    <bean id="validationAspect" class="com.test.spring.aop.xml.ValidationAspect"></bean>

    <bean id ="constant" class="com.test.spring.aop.xml.ConstantImpl"></bean>

    <!-- 配置AOP -->
    <aop:config>
        <!-- 配置切点表达式 -->
        <aop:pointcut expression="execution(*  com.test.spring.aop.xml.ArithmeticCalculator.*(..))" id="pointcut"/>
        <!-- 配置切面和通知 -->
        <aop:aspect ref="loggingAspect" order="2">
            <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
            <aop:after-returning method="afterReturning" returning="result" pointcut-ref="pointcut"/>
            <aop:after method="afterMethod" pointcut-ref="pointcut"/>
            <aop:after-throwing method="afterThrowing" throwing="ex" pointcut-ref="pointcut"/>
            <!--<aop:around method="aroundMethod" pointcut-ref="pointcut"/>  -->
        </aop:aspect>
        <aop:aspect ref="validationAspect" order="1">
            <aop:before method="validateArgs" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

LoggingAspect

public class LoggingAspect {

    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        //为通知传递参数,可以在JoinPoint 中拦截调用的目标方法,包括调用的目标方法的名字,出入目标方法的参数。
        Object[] args = joinPoint.getArgs();
        System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
    }

    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends");
    }

    public void afterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends with " + result);
    }

    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " occurs excetion:" + ex);
    }

环绕通知

public Object aroundMethod(ProceedingJoinPoint pjd) {

        Object result = null;
        String methodName = pjd.getSignature().getName();

        try {
            // 前置通知
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
            // 执行目标方法
            result = pjd.proceed();
            // 返回通知
            System.out.println("The method " + methodName + " ends with " + result);
        } catch (Throwable e) {
            // 异常通知
            System.out.println("The method " + methodName + " occurs exception:" + e);
            throw new RuntimeException(e);
        }
        // 后置通知
        System.out.println("The method " + methodName + " ends");
        return result;
    }

通过切面引入新功能

<aop:aspect ref="loggingAspect" order="2">
    <aop:declare-parents types-matching="com.test.spring.aop.xml.ArithmeticCalculator+" 
        implement-interface="com.test.spring.aop.xml.Constant" delegate-ref="constant"/>
</aop:aspect>

说明:通过切面引入新的功能就是为代理实现接口的一个过程。ArithmeticCalculator 是一个代理,通过<aop:declare-parents/> 为此代理实现Constant接口,并指定默认实现delegate-ref="constant" 从而达到扩展基础对象ArithmeticCalculator 的效果。在掉用目标Bean的时候,如果不是目标Bean的方法,则从代理的实现的接口constant 寻找方法的代用,如果是目标Bean的方法,则调用目标Bean的方法。

注解使用

启用注解: <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

/**
 * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
 */
@Order(1)
@Aspect
@Component
public class LoggingAspect {

    /**
     * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
     * 使用 @Pointcut 来声明切入点表达式. 
     * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
     */
    @Pointcut("execution(public int com.spring.aop.ArithmeticCalculator.*(..))")
    public void declareJointPointExpression(){}

    /**
     * 在 com.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
     */
    @Before("declareJointPointExpression()")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();

        System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
    }
    /**
     * 在方法执行之后执行的代码. 无论该方法是否出现异常
     */
    @After("declareJointPointExpression()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends");
    }
    /**
     * 在方法法正常结束受执行的代码
     * 返回通知是可以访问到方法的返回值的!
     */
    @AfterReturning(value="declareJointPointExpression()",
            returning="result")
    public void afterReturning(JoinPoint joinPoint, Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " ends with " + result);
    }

    /**
     * 在目标方法出现异常时会执行的代码.
     * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
     */
    @AfterThrowing(value="declareJointPointExpression()",
            throwing="e")
    public void afterThrowing(JoinPoint joinPoint, Exception e){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " occurs excetion:" + e);
    }

    /**
     * 环绕通知需要携带 ProceedingJoinPoint 类型的参数. 
     * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
     * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
     */
    @Around("execution(public int com.spring.aop.ArithmeticCalculator.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjd){
        Object result = null;
        String methodName = pjd.getSignature().getName();
        try {
            //前置通知
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
            //执行目标方法
            result = pjd.proceed();
            //返回通知
            System.out.println("The method " + methodName + " ends with " + result);
        } catch (Throwable e) {
            //异常通知
            System.out.println("The method " + methodName + " occurs exception:" + e);
            throw new RuntimeException(e);
        }
        //后置通知
        System.out.println("The method " + methodName + " ends");
        return result;
    }
}
posted @ 2017-03-09 23:10  春秋战国灞桥游  阅读(136)  评论(0编辑  收藏  举报