aspect学习(1)before&after&around

7 篇文章 0 订阅
2 篇文章 0 订阅

 面向Aspect的编程,其实就是AOP,什么是AOP,用过学习过spring的同学都晓得。本人不喜欢也不擅长写概念,更喜欢结合例子慢慢讲解。

       下面的例子是在eclipse下写的,需要先准备好环境。


准备环境

         eclipse安装AJDT插件,安装插件的方法有多种,本人更喜欢在线安装。http://www.eclipse.org/ajdt/downloads/,找到和ecilpse版本相合的ajdt版本,复制update site URL连接,然后在eclipse中按照安装插件的方法安装即可。

    (不会安装插件的同学,可百度,别人有贴图详细说明)。

    下载aspect架包,http://www.eclipse.org/aspectj/downloads.PHP

需求一

     有一Point类,有三个私有属性int x,int y , int z ,当调用setX,setY,setZ进行属性值修改前,打印通知日志。

    注意:eclipse新建项目,必须选择aspectJ project


[java]  view plain  copy
  1. package com.fei.bean;  
  2.   
  3. public class Point {  
  4.   
  5.     private int x;  
  6.     private int y;  
  7.     private int z;  
  8.       
  9.     public Point(int x, int y, int z) {  
  10.         super();  
  11.         this.x = x;  
  12.         this.y = y;  
  13.         this.z = z;  
  14.     }  
  15.   
  16.     public int getX() {  
  17.         return x;  
  18.     }  
  19.   
  20.     public void setX(int x) {  
  21.         this.x = x;  
  22.     }  
  23.   
  24.     public int getY() {  
  25.         return y;  
  26.     }  
  27.   
  28.     public void setY(int y) {  
  29.         this.y = y;  
  30.     }  
  31.   
  32.     public int getZ() {  
  33.         return z;  
  34.     }  
  35.   
  36.     public void setZ(int z) {  
  37.         this.z = z;  
  38.     }  
  39.       
  40. }  

      如果是新手,他们的做法就是在3个set方法里面加入日志代码,如果日志代码很复杂呢?如果我还有Pont1、Point2类,也需要对他们的set做相同处理呢?难道你在所有需要的地方都复制粘贴那部分日志代码吗?如果某天日志代码有修改呢,这样一来就需要修改N多个地方的代码了。。。。哪怕你是新手,你都觉得恶心了。

     废话不多说了,下面先直接用aspect来做,实现需求,然后再结合代码讲解。

[java]  view plain  copy
  1. package com.fei.aspect;  
  2.   
  3. import com.fei.bean.*;  
  4. import org.aspectj.lang.Signature;  
  5.   
  6. public aspect LogAspect {  
  7.   
  8.     public pointcut printLog() : call(void Point.set*(int));  
  9.       
  10.     before() : printLog(){  
  11.          Signature signature = thisJoinPoint.getSignature();  
  12.          System.out.println("在调用"+signature.getName());  
  13.     }  
  14. }  



       是不是觉得很神奇,我们用反编译工具来看看编译后的class文件。

       Point.class反编译后得到的源码和原源码可以说是一样的,就不贴了.

      MainTest.class反编译后的源码

[java]  view plain  copy
  1. package com.fei;  
  2.   
  3. import com.fei.aspect.LogAspect;  
  4. import com.fei.bean.Point;  
  5. import org.aspectj.lang.JoinPoint.StaticPart;  
  6. import org.aspectj.runtime.reflect.Factory;  
  7.   
  8. public class MainTest  
  9. {  
  10.   private static final JoinPoint.StaticPart ajc$tjp_0;  
  11.   private static final JoinPoint.StaticPart ajc$tjp_1;  
  12.   
  13.   static{  
  14.         ajc$preClinit(); }   
  15.         private static void ajc$preClinit() {  
  16.           Factory localFactory = new Factory("MainTest.java", MainTest.class);   
  17.           ajc$tjp_0 = localFactory.makeSJP("method-call", localFactory.makeMethodSig("1""setX""com.fei.bean.Point""int""x""""void"), 9);  
  18.           ajc$tjp_1 = localFactory.makeSJP("method-call", localFactory.makeMethodSig("1""setY""com.fei.bean.Point""int""y""""void"), 10);  
  19.       }  
  20.           
  21.   public static void main(String[] args) {  
  22.     Point p = new Point(102030);  
  23.     LogAspect.aspectOf().ajc$before$com_fei_aspect_LogAspect$1$738e898(ajc$tjp_0);   
  24.     p.setX(15);  
  25.       
  26.     LogAspect.aspectOf().ajc$before$com_fei_aspect_LogAspect$1$738e898(ajc$tjp_1);   
  27.     p.setY(25);  
  28.   }  
  29.   
  30.   
  31. }  

   我们发现在执行p.setX(15),p.setY(25)之前,都会先调用LogAspect.aspectOf().ajc$before$com_fei_aspect_LogAspect$1$738e898这个方法,那这个方法是什么东东呢,我们在来看看LogAspect.aj编译后产生的class文件

[java]  view plain  copy
  1. package com.fei.aspect;  
  2.   
  3. import java.io.PrintStream;  
  4. import org.aspectj.lang.JoinPoint.StaticPart;  
  5. import org.aspectj.lang.NoAspectBoundException;  
  6. import org.aspectj.lang.Signature;  
  7. import org.aspectj.lang.annotation.Aspect;  
  8. import org.aspectj.lang.annotation.Before;  
  9.   
  10. @Aspect  
  11. public class LogAspect{  
  12.   static{  
  13.     try{  
  14.       ajc$postClinit();   
  15.     } catch (Throwable localThrowable) {  
  16.         ajc$initFailureCause = localThrowable;  
  17.     }   
  18.   }  
  19.   
  20.   @Before(value="printLog()", argNames="")  
  21.   public void ajc$before$com_fei_aspect_LogAspect$1$738e898(JoinPoint.StaticPart thisJoinPointStaticPart) {  
  22.     Signature signature = thisJoinPointStaticPart.getSignature();  
  23.     System.out.println("在调用" + signature.getName());  
  24.   }  
  25.   
  26.   public static LogAspect aspectOf() {  
  27.     if (ajc$perSingletonInstance == null)  
  28.         throw new NoAspectBoundException("com_fei_aspect_LogAspect", ajc$initFailureCause); return ajc$perSingletonInstance; }   
  29.     public static boolean hasAspect() {   
  30.       return ajc$perSingletonInstance != null;  
  31.   }  
  32. }  

   对于里面的一些不熟悉的方法,我们不需要继续深究,我们只要知道它的处理流程即可。


  我们已经知道执行流程了,那下面我们来学习下aspect的一些语法。语法的使用有很多,这里没法一一详解,需要支持复制操作的,可自行查看官方文档。


pointcut

     pointcut,从字面的意思说的是切面的意思。也就是横切的时候,会有哪些执行点会被识别。只有先识别了,才能执行相应的Advice。

[java]  view plain  copy
  1. public pointcut printLog() : call(void Point.set*(int));  
     这行代码很容易理解,就是凡是符合call里面说写的东东,你都给我执行pritntLog()这个方法,至于是什么时候执行呢?Point.set*之前还是之后呢?那就看printLog()这方法使用befor()修饰呢还是after()修饰呢?我们上面的代码用的是before,所以在main方面里面执行p.setX(15) 之前会先执行printLog().

call

   call里面是可以使用通配符的,组合一些复杂的东东。

     * 表示任何数量的字符,除了(.) 
     .. 表示任何数量的字符包括任何数量的(.) 
     + 描述指定类型的任何子类或者子接口
    同Java一样,提供了一元和二元的条件表达操作符。
    一元操作符:!
    二元操作符:||和&&
    优先权同java

  

   比如上面的call(void Point.set*(int))就是说Point里面方法是以set开头,并且参数类型是int,返回void类型的方法。

   call(* Point.*X(..))就是说Point里面方法名以X结尾,任意参数类型,返回类型也任意的方法都符合,也就是说void setX(int x),int getX()都符合

   call(void Point.set*(int)) 会捕捉到setX,setY,setZ,那如果我要排除setY呢?那可以这样写call(void Point.set*(int)) && !call(void Point.setY(int));

execution

    上面的源码是pointcut  和call连用,call就是方法调用,所以我们用反编译工具看class文件时,发现都是在调用方法(如上面MainTest.main中的p.setX(15),p.setY(25))前会插入一些代码

    pointcut除了和call连用,还可以和execution连用,execution就是方法执行,我们把上面的LogAspect中的 call改为execution看看,有什么不同。

[java]  view plain  copy
  1. package com.fei.aspect;  
  2.   
  3. import com.fei.bean.*;  
  4. import org.aspectj.lang.Signature;  
  5.   
  6. public aspect LogAspect {  
  7.   
  8.     public pointcut printLog() : execution( void Point.set*(int)) ;  
  9.       
  10.     before() : printLog(){  
  11.          Signature signature = thisJoinPoint.getSignature();  
  12.          System.out.println("在调用"+signature.getName());  
  13.     }  
  14.       
  15.       
  16. }  

MainTest.class反编译后的源码

[html]  view plain  copy
  1. package com.fei;  
  2.   
  3. import com.fei.bean.Point;  
  4.   
  5. public class MainTest  
  6. {  
  7.   public static void main(String[] args)  
  8.   {  
  9.     Point p = new Point(10, 20, 30);  
  10.     p.setX(15);  
  11.     p.setY(25);  
  12.   }  
  13. }  

    发现没插入任何额外代码了。那额外的那部分代码哪去了呢?

  Point.class反编译后的源码

[java]  view plain  copy
  1. package com.fei.bean;  
  2.   
  3. import com.fei.aspect.LogAspect;  
  4. import org.aspectj.lang.JoinPoint.StaticPart;  
  5. import org.aspectj.runtime.reflect.Factory;  
  6.   
  7. public class Point  
  8. {  
  9.   private int x;  
  10.   private int y;  
  11.   private int z;  
  12.   private static final JoinPoint.StaticPart ajc$tjp_0;  
  13.   private static final JoinPoint.StaticPart ajc$tjp_1;  
  14.   private static final JoinPoint.StaticPart ajc$tjp_2;  
  15.   
  16.   public Point(int x, int y, int z)  
  17.   {  
  18.     this.x = x;  
  19.     this.y = y;  
  20.     this.z = z;  
  21.   }  
  22.   
  23.   public int getX() {  
  24.     return this.x;  
  25.   }  
  26.   
  27.   public void setX(int x) {  
  28.     LogAspect.aspectOf().ajc$before$com_fei_aspect_LogAspect$1$738e898(ajc$tjp_0); this.x = x;  
  29.   }  
  30.   
  31.   public int getY() {  
  32.     return this.y;  
  33.   }  
  34.   
  35.   public void setY(int y) {  
  36.     LogAspect.aspectOf().ajc$before$com_fei_aspect_LogAspect$1$738e898(ajc$tjp_1); this.y = y;  
  37.   }  
  38.   
  39.   public int getZ() {  
  40.     return this.z;  
  41.   }  
  42.   
  43.   public void setZ(int z) {  
  44.     LogAspect.aspectOf().ajc$before$com_fei_aspect_LogAspect$1$738e898(ajc$tjp_2); this.z = z;  
  45.   }  
  46.   
  47.   static  
  48.   {  
  49.     ajc$preClinit(); }   
  50.   private static void ajc$preClinit() { Factory localFactory = new Factory("Point.java", Point.class); ajc$tjp_0 = localFactory.makeSJP("method-execution", localFactory.makeMethodSig("1""setX""com.fei.bean.Point""int""x""""void"), 20); ajc$tjp_1 = localFactory.makeSJP("method-execution", localFactory.makeMethodSig("1""setY""com.fei.bean.Point""int""y""""void"), 28); ajc$tjp_2 = localFactory.makeSJP("method-execution", localFactory.makeMethodSig("1""setZ""com.fei.bean.Point""int""z""""void"), 36);  
  51.   }  
  52. }  

   呵呵,已经跑到Point里面来了。



通过比较,对call和execution的区别就一目了然了。


需求二

     在需求一的基础上,增加,当调用完setX(int),setY(int),setZ(int)方法后,打印当前时间.


   既然有before那当然就有after啊,简单。

[java]  view plain  copy
  1. package com.fei.aspect;  
  2.   
  3. import java.util.Calendar;  
  4. import java.util.Date;  
  5.   
  6. import com.fei.bean.*;  
  7.   
  8. import org.aspectj.lang.Signature;  
  9.   
  10. public aspect LogAspect {  
  11.   
  12.     public pointcut printLog() : call( void Point.set*(int)) ;  
  13.     public pointcut timeLog() : call(void Point.set*(..));  
  14.       
  15.     before() : printLog(){  
  16.          Signature signature = thisJoinPoint.getSignature();  
  17.          System.out.println("在调用"+signature.getName());  
  18.     }  
  19.       
  20.     after() : timeLog(){  
  21.         Signature signature = thisJoinPoint.getSignature();  
  22.          System.out.println(signature.getName()+"调用完后当前时间:"+new Date().toLocaleString());  
  23.     }  
  24.       
  25. }  
或者

[java]  view plain  copy
  1. package com.fei.aspect;  
  2.   
  3. import java.util.Calendar;  
  4. import java.util.Date;  
  5.   
  6. import com.fei.bean.*;  
  7.   
  8. import org.aspectj.lang.Signature;  
  9.   
  10. public aspect LogAspect {  
  11.   
  12.     public pointcut printLog() : call( void Point.set*(int)) ;  
  13.       
  14.     before() : printLog(){  
  15.          Signature signature = thisJoinPoint.getSignature();  
  16.          System.out.println("在调用"+signature.getName());  
  17.     }  
  18.       
  19.     after() : printLog(){  
  20.         Signature signature = thisJoinPoint.getSignature();  
  21.          System.out.println(signature.getName()+"调用完后当前时间:"+new Date().toLocaleString());  
  22.     }  
  23.       
  24. }  




     这样做,的确满足了需求,但是却需要多写个after.那能否不需这么麻烦吗?可以,这时around就派上用场了,around字面意思包围。

[java]  view plain  copy
  1. package com.fei.aspect;  
  2.   
  3. import java.util.Calendar;  
  4. import java.util.Date;  
  5.   
  6. import com.fei.bean.*;  
  7.   
  8. import org.aspectj.lang.Signature;  
  9.   
  10. public aspect LogAspect {  
  11.   
  12.     public pointcut printLog() : call( void Point.set*(int)) ;  
  13.       
  14.     void around() : printLog(){  
  15.          Signature signature = thisJoinPoint.getSignature();  
  16.          System.out.println("在调用"+signature.getName());  
  17.          proceed();  
  18.          System.out.println("当前时间:"+new Date().toLocaleString());  
  19.     }  
  20.       
  21.       
  22. }  



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
springboot学习笔记 spring基础 Spring概述 Spring的简史 xml配置 注解配置 java配置 Spring概述 Spring的模块 核心容器CoreContainer Spring-Core Spring-Beans Spring-Context Spring-Context-Support Spring-Expression AOP Spring-AOP Spring-Aspects Messaging Spring-Messaging WEB Spring-Web Spring-Webmvc Spring-WebSocket Spring-Webmvc-Portlet 数据访问/集成(DataAccess/Intefration) Spring-JDBC Spring-TX Spring-ORM Spring-OXM Spring-JMS Spring的生态 Spring Boot Spring XD Spring Cloud Spring Data Spring Integration Spring Batch Spring Security Spring HATEOAS Spring Social Spring AMQP Spring Mobile Spring for Android Spring Web Flow Spring Web Services Spring LDAP Spring Session Spring项目快速搭建 Maven简介 Maven安装 Maven的pom.xml dependencies dependency 变量定义 编译插件 Spring项目的搭建 Spring Tool Suite https://spring.io/tools/sts/all IntelliJ IDEA NetBeans https://netbeans.org/downloads/ Spring基础配置 依赖注入 声明Bean的注解 @Component组件,没有明确的角色 @Service在业务逻辑层(service层) @Repository在数据访问层(dao层) @Controller在展现层(MVC→SpringMVC) 注入Bean的注解 @Autowired:Spring提供的注解 @Inject:JSR-330提供的注解 @Resource:JSR-250提供的注解 Java配置 @Configuration声明当前类是一个配置类 @Bean注解在方法上,声明当前方法的返回值为一个Bean AOP @Aspect 声明是一个切面 拦截规则@After @Before @Around PointCut JoinPoint Spring常用配置 Bean的Scope Singleton Prototype Request Session GlobalSession SpringEL和资源调用 注入普通字符 注入操作系统属性 注入表达式云算结果 注入其他Bean的属性 注入文件内容 注入网址内容 注入属性文件 Bean的初始化和销毁 Java配置方式 注解方式 Profile @Profile 通过设定jvm的spring.profiles.active参数 web项目设置在Servlet的context parameter中 事件Application Event 自定义事件,集成ApplicationEvent 定义事件监听器,实现ApplicationListener 使用容器发布事件 Spring高级话题 Spring Aware BeanNameAware BeanFactoryAware
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值