欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring之AOP ----日志切面LoggingAspect

程序员文章站 2022-05-24 09:49:35
...

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;
    }
}