zoukankan      html  css  js  c++  java
  • 简单的aop实现日志打印(切入点表达式)

    Spring中可以使用注解或XML文件配置的方式实现AOP。
    1、导入jar包

    • 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
    • commons-logging-1.1.3. jar
    • spring-aop-4.0.0.RELEASE.jar
    • spring-aspects-4.0.0.RELEASE.jar
    • spring-beans-4.0.0.RELEASE.jar
    • spring-context-4.0.0.RELEASE.jar
    • spring-core-4.0.0.RELEASE.jar
    • spring-expression-4.0.0.RELEASE.jar

    aspectaop相关jar包 ---> 资源目录--->jar包资源--->AOP日志打印相关jar包(切面类) 

    Spring相关jar包 ---> 资源目录--->jar包资源--->Spring相关jar包 

    2、开启基于注解的AOP功能

    在Spring的配置文件中加入

    <context:component-scan base-package="com.bwlu.aop"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    3、声明一个切面类,并把这个切面类加入到IOC容器中

     1 @Component//加入IOC容器
     2 @Aspect//表示这是一个切面类
     3 public class LogAspect{
     4     //value中为切入点表达式
     5     @Before(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//前置通知
     6     public void showBeginLog(){
     7         System.out.println("AOP日志开始");
     8     }
     9     @After(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//后置通知
    10     public void showReturnLog(){
    11         System.out.println("AOP方法结束");
    12     }
    13     @AfterThrowing(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//异常通知
    14     public void showExceptionLog(){
    15         System.out.println("AOP方法异常");
    16     }
    17     @AfterReturning(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//返回通知
    18     public void showAfterLog(){
    19         System.out.println("AOP方法最终返回");
    20     }
    21 }

    4、被代理的对象也需要加入IOC容器

     1 @Component
     2 public class MathCalculator {
     3     public void add(int i, int j) {
     4         int result=i+j;
     5         System.out.println("目标方法add(int)执行了");
     6     }
     7     public void sub(int i, int j) {
     8         int result=i-j;
     9         System.out.println("目标方法sub执行了");
    10     }
    11     public void mult(int i, int j) {
    12         int result=i*j;
    13         System.out.println("目标方法mult执行了");
    14     }
    15     public void div(int i, int j) {
    16         int result=i/j;
    17         System.out.println("目标方法div执行了");
    18     }
    19 }
    MathCalculator

    5、新建Junit测试类进行测试

     1 ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
     2 @Test
     3 public void test() {
     4     //需要进行强转,如果该类实现了一个接口(并且切入点表达式指向这个类),那么获取到的将不是该类的对象,需要强转
     5     //MathCalculatorImpl实现了MathCalculator接口,则
     6     //MathCalculator bean = (MathCalculator)ioc.getBean("mathCalculatorImpl");
     7     MathCalculator bean = (MathCalculator)ioc.getBean("mathCalculator");
     8     bean.add(10, 5);
     9     System.out.println();
    10     bean.add(10.0, 5);
    11     System.out.println();
    12     bean.sub(10, 5);
    13     System.out.println();
    14     bean.mult(10, 5);
    15     System.out.println();
    16     bean.div(10, 0);
    17 }

    6、切入点表达式

    • 切入点表达式的语法格式

    execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))

    • 切入点表达式支持通配符
    • 两种切入表达式
      • 最详细的切入点表达式:

     

    execution(public void com.bwlu.aop.MathCalculator.add(int, int))
      • 最模糊的切入点表达式:
    execution (* *.*(..))

    execution(public void com.bwlu.aop.MathCalculator.add(int, int)):只有add方法加入了4个通知,

    execution(public void com.bwlu.aop.MathCalculator.*(int, int)):任意方法,参数为int,int

    execution(public void com.bwlu.aop.MathCalculator.*(..)):MathCalculator中的任意方法,任意参数列表

    execution(public * com.bwlu.aop.MathCalculator.*(..)):MathCalculator中的任意方法,任意参数列表,任意返回值

    execution( * com.bwlu.aop.MathCalculator.*(..)):MathCalculator中的任意方法,任意参数列表,任意返回值,任意访问修饰符

    execution (* *.*(..)):任意匹配

    需要注意的是:权限是不支持写通配符的,当然你可以写一个*表示所有权限所有返回值!

    7、优化

    用@PointCut注解统一声明,然后在其它通知中引用该统一声明即可!

     1 @Component
     2 @Aspect
     3 public class LogAspect{
     4     @Pointcut(value="execution(* *.*(..))")
     5     public void showLog(){}
     6     @Before(value="showLog()")
     7     public void showBeginLog(){
     8         System.out.println("AOP日志开始");
     9     }
    10     @After(value="showLog()")
    11     public void showReturnLog(){
    12         System.out.println("AOP方法结束");
    13     }
    14     @AfterThrowing(value="showLog()")
    15     public void showExceptionLog(){
    16         System.out.println("AOP方法异常");
    17     }
    18     @AfterReturning(value="showLog()")
    19     public void showAfterLog(){
    20         System.out.println("AOP方法最终返回");
    21     }
    22 }
    LogAspect

    8、通知方法的细节

    (1)在通知中获取目标方法的方法名和参数列表

    • 在通知方法中声明一个JoinPoint类型的形参
    • 调用JoinPoint对象的getSignature()方法获取目标方法的签名
    • 调用JoinPoint对象的getArgs()方法获取目标方法的实际参数列表

    (2)在返回通知中获取方法的返回值

    • 在@AfterReturning注解中添加returning属性:@AfterReturning (value="showLog()", returning= "result")
    • 在返回通知的通知方法中声明一个形参,形参名和returning属性的值一致:showReturnLog(JoinPoint joinPoint, Object result)

    (3)在异常通知中获取异常对象

    • 在@ AfterThrowing注解中添加throwing属性:@AfterThrowing (value="showLog()",throwing= "throwable" )
    • 在异常通知的通知方法中声明一个形参,形参名和throwing属性值一致:showExceptinLog(JoinPoint joinPoint, Throwable throwable)
     1 @Component
     2 @Aspect
     3 public class LogAspect{
     4     @Pointcut(value="execution(* *.*(..))")
     5     public void showLog(){}
     6     @Before(value="showLog()")
     7     public void showBeginLog(JoinPoint jPoint){
     8         Object[] args = jPoint.getArgs();
     9         List<Object> asList = Arrays.asList(args);
    10         Signature signature = jPoint.getSignature();
    11         String name = signature.getName();
    12         System.out.println("AOP日志开始");
    13         System.out.println("目标方法名:"+name+",参数列表:"+asList);
    14     }
    15     @After(value="showLog()")
    16     public void showReturnLog(){
    17         System.out.println("AOP方法结束");
    18     }
    19     @AfterThrowing(value="showLog()",throwing="ex")
    20     public void showExceptionLog(Exception ex){
    21         System.out.println("AOP方法异常");
    22         System.out.println("异常信息:"+ex.getMessage());
    23     }
    24     @AfterReturning(value="showLog()",returning="result")
    25     public void showAfterLog(Object result){
    26         System.out.println("方法返回值:"+result);
    27         System.out.println("AOP方法最终返回");
    28     }
    29 }
    LogAspect

    9、环绕通知@Around

    环绕通知需要在方法的参数中指定JoinPoint的子接口类型ProceedingJoinPoint为参数:

    public void around(ProceedingJoinPoint joinPoint){}

    环绕通知能够替代其他4个通知。
    注意:@Around修饰的方法一定要将方法的返回值返回!本身相当于代理!

     1 @Component
     2 @Aspect
     3 public class LogAspect{
     4     @Around(value="execution(* *.*(..))")
     5     public Object showLog(ProceedingJoinPoint point){
     6         Object[] args = point.getArgs();
     7         List<Object> asList = Arrays.asList(args);
     8         Signature signature = point.getSignature();
     9         String name = signature.getName();
    10         Object result = null;
    11         try {
    12             try {
    13                 //目标方法之前要执行的操作,相当于@before
    14                 System.out.println("[环绕日志]"+name+"开始了,参数为:"+asList);
    15                 //调用目标方法
    16                 result = point.proceed(args);
    17             } finally {
    18                 //方法最终结束时执行的操作,相当于@after
    19                 System.out.println("[环绕日志]"+name+"结束了!");
    20             }
    21             //目标方法正常执行之后的操作,相当于@AfterReturning
    22             System.out.println("[环绕日志]"+name+"返回了,返回值为:"+result);
    23         } catch (Throwable e) {
    24             //目标方法抛出异常信息之后的操作,相当于@AfterThrowing
    25             System.out.println("[环绕日志]"+name+"出异常了,异常对象为:"+e);
    26             throw new RuntimeException(e.getMessage());
    27         }
    28         return result;
    29     }
    30 }
    LogAspect

     10、切面的优先级

    对于同一个代理对象,可以同时有多个切面共同对它进行代理。
    可以在切面类上通过@Order (value=50)注解来进行设置,值越小优先级越高!

    @Aspect

    @Component

    @Order(value=40)

    public class LogAspect {}

  • 相关阅读:
    linux修改主机名
    selinux详解及配置文件
    linux磁盘分区
    识别光纤,模块
    free命令详解
    Linux系统修改网卡名(eth0-3)
    CentOS7的systemctl使用
    ELK集群
    ELK故障:elk在运行一段时间后,没有数据。
    superviosrd进程管理
  • 原文地址:https://www.cnblogs.com/lixiang1993/p/7447853.html
Copyright © 2011-2022 走看看