鲁春利的工作笔记,好记性不如烂笔头



基于注解方式声明切面

要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间:

Spring学习笔记AOP(二)_第1张图片


引入后AOP命名空间并启动对@AspectJ注解的支持(spring-context-aop-annotation.xml):


    
    


Spring默认不支持@AspectJ风格的切面声明,通过声明Spring就能够自动扫描被@Aspect标注的切面了。
Spring自带了名为AnnotationAwareAspectJAutoProxyCreator的自动代理类,可以在Spring的配置文件中把AnnotationAwareAspectJAutoProxyCreator注册为一个Bean,但是为了简化配置,Spring在aop命名空间提供了该元素配置,用来自动创建AnnotationAwareAspectJAutoProxyCreator类。

wKiom1d-Ekmj_U9LAAEA6JtcRlg156.jpg

AnnotationAwareAspectJAutoProxyCreator会自动代理一些Bean,这些Bean的方法需要与使用@Aspect注解的Bean中所定义的切点想匹配,这些切点是通过@Pointcut注解定义的。


AOP相关的注解位于org.aspectj.lang.annotation包下。


声明切面
@AspectJ风格的声明切面非常简单,使用@Aspect注解进行声明:然后将该切面在配置文件中声明为Bean后,Spring就能自动识别并进行AOP方面的配置(就是一个普通的bean)。

@Aspect
public class LogAdapter {
    // ......
}

声明切入点
@Pointcut修饰一个方法用来标识该方法为切入点方法,方法必须返回void类型。

@Pointcut(value="切入点表达式", argNames="参数名列表")
public void poincutMethod () {
    // ......
}

说明:切入点@Pointcut的声明不是必须的,可以在通知声明时声明切入点表达式(但如果存在多种通知则每个通知上都需要声明切入点表达式),而声明切入点后可以在使用通知的地方引用切入点定义。


Spring支持9个@ApsectJ切点表达式函数:
1、方法切点函数

    execution(方法匹配模式串)
    方法匹配模式串:<注解?> <修饰符?> <返回类型> <方法名> (<参数列表>) <异常列表>
    注解:可选,方法上持有的注解,如@Deprecated;
    修饰符:可选,如public、protected;
    返回值类型:必填,可以是任何类型模式;“*”表示所有类型;
    方法名:必填,可以使用“*”进行模式匹配;
    参数列表:“()”表示方法没有任何参数;“(..)”表示匹配接受任意个参数的方法,“(..,java.lang.String)”表示匹配接受java.lang.String类型的参数结束,且其前边可以接受有任意个参数的方法;“(java.lang.String,..)” 表示匹配接受java.lang.String类型的参数开始,且其后边可以接受任意个参数的方法;“(*,java.lang.String)” 表示匹配接受java.lang.String类型的参数结束,且其前边接受一个任意类型参数的方法;
    异常列表:可选,以“throws 异常全限定名列表”声明,异常全限定名列表如有多个以“,”分割,如throws java.lang.IllegalArgumentException, java.lang.ArrayIndexOutOfBoundsException。
       
    @annotation(方法注解类名)

2、方法入参切点函数
    args(类名)
    @args(类型注解类名)

3、目标类切点函数
    within(类名匹配串)
    @within(类名)
    target(类型注解类名)
    @target(类型注解类名)
4、代理类切点函数
    this(类名)


AspectJ类型匹配的通配符:

*:匹配任何数量字符。

..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。

+:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。

java.lang.String    匹配String类型;
java.*.String       匹配java包下的任何“一级子包”下的String类型;如匹配java.lang.String,但不匹配java.lang.ss.String
java..*             匹配java包及任何子包下的任何类型; 如匹配java.lang.String、java.lang.annotation.Annotation
java.lang.*ing      匹配任何java.lang包下的以ing结尾的类型;
java.lang.Number+   匹配java.lang包下的任何Number的自类型;如匹配java.lang.Integer,也匹配java.math.BigInteger


接口

package com.invicme.apps.aop;

/**
 * 
 * @author lucl
 * 
 * 数学计算接口类
 *
 */
public interface ArithmeticCalculate {
    public int add (int i, int j);
    public int div (int i, int j);
    public String validateNum (String level, int i);
}

实现类

package com.invicme.apps.aop.annotation;

import org.apache.log4j.Logger;

import com.invicme.apps.aop.ArithmeticCalculate;

/**
 * 
 * @author lucl
 * 
 * 数学计算实现类
 *
 */
public class ArithmeticCalculateImpl implements ArithmeticCalculate {
    
    private static final Logger logger = Logger.getLogger(ArithmeticCalculateImpl.class);
    
    private int i = 0;
    private int j = 0;
    
    public ArithmeticCalculateImpl () {
        this(0, 0);
    }
    
    public ArithmeticCalculateImpl (int i, int j) {
        this.i = i;
        this.j = j;
    }
    
    @Override
    public int add(int i, int j) {
        logger.info("The method add was invoke with args [" + i + ", " + j + "]");
        int sum = i + j;
        logger.info("The method add ends with result [" + sum + "]");
        return sum;
    }

    @Override
    public int div(int i, int j) {
        logger.info("The method div was invoke with args [" + i + ", " + j + "]");
        int result = i / j;
        logger.info("The method div ends with result [" + result + "]");
        return result;
    }

    @Override
    public String validateNum(String level, int i) {
        logger.info("The method validateNum was invoke with args [" + level + ", " + i + "]");
        String result = this.getMsg(i);
        logger.info("The method validateNum ends with result [" + result + "]");
        return result;
    }

    private String getMsg (int i) {
        if (i > 0) {
            return "正数";
        }
        return "负数";
    }
}

日志切面

package com.invicme.apps.aop.annotation;

import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 
 * @author lucl
 * 
 */
@Aspect
public class LogAdapter {
    private static final Logger logger = Logger.getLogger(LogAdapter.class);
     
    /**
     * Pointcut
     * 定义Pointcut,Pointcut的名称为poincutMethod(),此方法没有返回值和参数
     * 该方法就是一个标识,不进行调用
     */
    @Pointcut(value="execution(public * com.invicme.apps.aop.annotation.ArithmeticCalculateImpl.*(..))")
    public void poincutMethod () {
        // ......
    }
    
    /**
     * 前置通知,在目标方法执行之前被调用(JoinPoint参数不是必须的,传入是为了获取目标对象的相关属性)
     */
    @Before(value="poincutMethod ()")
    public void beforeAdvice (JoinPoint joinPoint) {
        // 目标对象
        Object target = joinPoint.getTarget();
        // 目标方法
        String methodName = joinPoint.getSignature().getName();
        // 方法参数
        List asList = Arrays.asList(joinPoint.getArgs());
        /**
         * target.getClass().getName() : 获取的是全路径名(包名+类名)
         * target.getClass().getSimpleName() : 获取类名
         */
        logger.info("[@Before]" + target.getClass().getSimpleName() + "@" + methodName + " was invoke with args " + asList + ".");
    }
    
    /**
     * 后置通知(在目标方法执行之后被执行,无论该方法是否抛出异常)
     */
    @After(value="poincutMethod ()")
    public void afterAdvice (JoinPoint joinPoint) {
        // 目标对象
        Object target = joinPoint.getTarget();
        // 目标方法
        String methodName = joinPoint.getSignature().getName();
        logger.info("[@After]" + target.getClass().getSimpleName() + "@" + methodName + " ends.");
    }
    
    /**
     * 返回通知(在方法正常执行后执行,若出现异常不会被执行)
     * 返回通知可以获取到目标方法的返回值
     */
    @AfterReturning (value="poincutMethod ()", returning="result")
    public void afterReturningAdvice (JoinPoint joinPoint, Object result) {
        // 目标对象
        Object target = joinPoint.getTarget();
        // 目标方法
        String methodName = joinPoint.getSignature().getName();
        logger.info("[@AfterReturning]" + target.getClass().getSimpleName() + "@" + methodName + " ends with result " + result + ".");
    }
    
    /**
     * 异常通知(当目标方法出现异常时会被执行,可以访问到异常,也可以通过指定异常类型,
     * 如Exception ex,也可以为NullPointerException ex则只有空指针异常才会被执行)
     */
    @AfterThrowing (value="poincutMethod ()", throwing="ex")
    public void afterThrowingAdvice (JoinPoint joinPoint, Exception ex) {
        // 目标对象
        Object target = joinPoint.getTarget();
        // 目标方法
        String methodName = joinPoint.getSignature().getName();
        logger.info("[@AfterThrowing]" + target.getClass().getSimpleName() + "@" + methodName + " occurs exception : " + ex + ".");
    }
    
    /**
     * 环绕通知
     * 说明:
     *         环绕通知需要携带ProceedingJoinPoint类型的参数
     *         环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型参数可以决定是否执行目标方法
     *         环绕通知必须有返回值,且返回值为目标方法的返回值
     */
    @Around (value="poincutMethod ()")
    public Object aroundAdvice (ProceedingJoinPoint joinPoint) {
        // 目标对象
        Object target = joinPoint.getTarget();
        // 目标方法
        String methodName = joinPoint.getSignature().getName();
        // 参数
        List asList = Arrays.asList(joinPoint.getArgs());
        // 
        Object result = null;
        // 执行目标方法
        try {
            // 前置通知
            result = joinPoint.proceed();
            // 返回通知
        } catch (Throwable e) {
            throw new RuntimeException(e);
            // 异常通知
            // 或者
            // 直接throw出去(否则程序会执行到最后的return result,而result为null,目标方法可能需要类型转换,当试图将null转化为特定类型时,出错)
        }
        // 后置通知
        logger.info("[@Around]" + target.getClass().getSimpleName() + "@" + methodName + " was invoke with args " + asList + ", ends with " + result + " .");
        // 注意这里的result实际上是目标方法的返回值,如果出现问题返回值不匹配会出现错误
        return result;
    }
} 
  

配置文件spring-context-aop-annotation.xml




    
    
    
    
    
    
    
    

测试类

package com.test.apps.spring.aop;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.invicme.apps.aop.ArithmeticCalculate;

/**
 * 
 * @author lucl
 *
 */
public class TestSpringAopByAnnotation {
    @Test
    public void testProxyFactoryBean() {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-context-aop-annotation.xml");
        ArithmeticCalculate calculate = context.getBean("calculate", ArithmeticCalculate.class);
        calculate.add(1, 2);
        System.out.println("----------------------------------------------------------------");
        try {
            Thread.sleep(1 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        calculate.div(1, 0);
    }
}

运行结果

Spring学习笔记AOP(二)_第2张图片


你可能感兴趣的:(AspectJ,JavaWeb)