学习完AspectJ有段时间了,总体感觉这个编程语言简单易学、功能强大,但是搜到的关于AspectJ的基础内容比较少,so,笔者感觉有必要整理出这部分内容,若有不当之处,欢迎指正共同学习☺

内容:AspectJ概念,和AOP的区别,@AspectJ的配置,Advice的配置,Introduction的配置

(源码:testSpring)

1 AspectJ简介

百科上这样解释它:AspectJ是一个面向切面的框架,它扩展了Java语言,定义了AOP语法,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

简单的说,它是用另外一种方式实现的AOP,一句话解释:AspectJ是一种编译期的用注解形式实现的AOP。至于AOP策略的作用,要单独学习一下AOP基础,这里不再细说。

2 AOP/Spring AOP/AspectJ的区别

既然说AspectJ是一种AOP框架策略,前面又学习过Spring AOP,那么就说说三者的概念区别关系。

      AOP:是一种面向切面的编程范式,是一种编程思想,旨在通过分离横切关注点,提高模块化,可以跨越对象关注点。Aop的典型应用即spring的事务机制,日志记录。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。主要功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等;主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

     AspectJ和Spring AOP 是AOP的两种实现方案,这一点要搞清楚。

     AspectJ:Aspectj是aop的java实现方案,AspectJ是一种编译期的用注解形式实现的AOP。

(1)AspectJ是一个代码生成工具(Code Generator),其中AspectJ语法就是用来定义代码生成规则的语法。基于自己的语法编译工具,编译的结果是JavaClass文件,运行的时候classpath需要包含AspectJ的一个jar文件(Runtime lib),支持编译时织入切面,即所谓的CTW机制,可以通过一个Ant或Maven任务来完成这个操作。

(2)AspectJ有自己的类装载器,支持在类装载时织入切面,即所谓的LTW机制。使用AspectJ LTW有两个主要步骤,第一,通过JVM的-javaagent参数设置LTW的织入器类包,以代理JVM默认的类加载器;第二,LTW织入器需要一个 aop.xml文件,在该文件中指定切面类和需要进行切面织入的目标类。

(3)AspectJ同样也支持运行时织入,运行时织入是基于动态代理的机制。(默认机制)

Spring AOP

Spring aop是aop实现方案的一种,它支持在运行期基于动态代理的方式将aspect织入目标代码中来实现aop。但是spring aop的切入点支持有限,而且对于static方法和final方法都无法支持aop(因为此类方法无法生成代理类);另外spring aop只支持对于ioc容器管理的bean,其他的普通java类无法支持aop。现在的spring整合了aspectj,在spring体系中可以使用aspectj语法来实现aop。

3 AspectJ的定义和实现

3.1@AspectJ的配置

     配置@AspectJ,是为了以便被Bean容器发现。对@AspectJ的支持可以使用XML文件配置或Java风格的配置,其中,@AspectJ注解风格类似于纯Java注解的普通Java类,

注解形式:

       @Configuration@EnableAspectJAutoProxyPublicclass AppConfig{}

XML配置形式

<aop:aspectj-autoproxy/>

Spring可以使用AspectJ来做切入点解析

AOP的运行时仍旧是纯的Spring AOP,对AspectJ的编译器或者织入无依赖性,但是要使用AspectJ,必须确保aspectjweaver.jar库包含在应用程序的classpath中,并且必须确定其版本在1.6.8或者更高版本。

有几点需要了解:

(1)@Aspect切面使用@Aspect注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用,可以拥有aop配置的一切特点。

(2)用@Aspect注解的类可以有方法和字段,他们也可能包括切入点pointcut,通知Advice和引入Introduction声明。

(3)@Aspect注解不能通过类路径被自动检测发现,需要配合使用@Component注释或在xml文件中配置对应的bean项。

两种实现是:

注解形式:

@Component
@Aspect
public class TerenceAspect {}

配置形式:

<bean id=”myAspect” class=”org.xyz.NotVeryUsefulAspect”><!--configureproperties of aspect here as normal-->
</bean>

需要注意的是,一个类的@Aspect注解只标识该类是一个切面,同时将自己从自动代理中排除出去了,其目的是为了避免出现死循环(例如一个包下包含了业务类和切面类,将自己从自动代理中排除出去,避免了自己代理自己的情况一直寻找自己的循环情况)。

3.2 Pointcut注解

(1)一般pointcut

一个切入点通过一个普通的方法定义来提供,并且切入点表达式使用@Pointcut注解来声明,注解的方法返回类型必须为void型。

@Component
@Aspect
public class TerenceAspect {@Pointcut("execution(*com.terence.aop.aspectjbiz.*Biz.**(..))")public void pointcut(){}@Pointcut("within(com.terence.aop.aspectj.biz.*)")public void bizPointcut(){}
}

上述pointcut切入点的注解:@Pointcut("execution(* com.terence.aop.aspectjbiz.*Biz.**(..))")表示包com.terence.aop.aspectjbiz下任何以Biz结尾的方法都将被执行。

@Pointcut("within(com.terence.aop.aspectj.biz.*)")表示当前com.terence.aop.aspectj.biz包下的任何类都会匹配给这个方法。

(2)组合pointcut

有时候根据实际情况,要建立复杂的切入点表达式,可以通过&&、||和!进行组合,也可以通过名字引用切入点表达式。

   @Pointcut("execution(public* (..))")private void anyPublicOperation(){}@Pointcut("within(com.xyz.someapp.trading..)")private void inTrading(){}@Pointcut("anyPublicOperation&& inTrading()")private void tradingOperation(){}

上述tradingOperation()的组合表达式,表示同时执行@Pointcut("execution(public * (..))")和@Pointcut("within(com.xyz.someapp.trading..)")。

虽然根据场景需要,建立复杂的切入点表达式,但是不建议使用过于复杂的表达式,尽量清晰明了简单,因为使用负责的组合表达式,当任一个切入点表达式修改的时候,都很容易造成一些关联性的问题。

(3) 如何定义一个良好的pointcut

AspectJ本身就是一种编译期的AOP,检查代码并匹配连接点于切入点的代价是昂贵的,所以,定义切入点的时候,切入点表达式尽量清晰明了易读,那么,如何定义一个好的切入点呢?一个好的切入点应该包含以下几个方面:

i.    选择特定类型的连接点:如:execution, get, set, call handler

ii.   确定连接点范围,如within ,withincode

iii.  匹配上下文信息,如:this,target,@annotation

iv.  切入点表达式匹配类型参照下表:

3.3 Advice注解

1 Before Advice:@Before

2.After returning advice:@AfterReturning,可在通知体内得到返回的实际值;

3.After throwing advice:@AfterThrowing

4.After (finally) advice : @After

最终通知必须准备处理正常和异常两种返回情况,它通常用于释放资源。

5.Around advice : @Around

环绕通知使用@Around注解来声明,通知方法的第一个参数必须是ProceedingJoinPoint类型,在通知内部调用ProceedingJoinPoint的Proceed()方法会导致执行真正的方法,传入一个Object[]对象,数组中的值将被作为一个参数传递给方法。上述5种通知的例子:

第一,XML文件配置,添加自动扫描和自动代理两项配置

<?xml version="1.0"encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.terence.aop.aspectj"/><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>

第二,业务类。

packagecom.terence.aop.aspectj.biz;
importorg.springframework.stereotype.Service;
/** 业务类*/
@Service
public class TerenceBiz {@TerenceMethod("TerenceBiz save with TerenceMethod")public Stringsave(String arg){System.out.println("TerenceBiz save:"+arg);//throw newRuntimeException(" Save failed!");return"Save success!";}
}

第三,切面类

切面类的通知advice:

前置通知使用@Before("pointcut()"),表示继承了方法pointcut()的切入表达式:executuion(* com.terence.aop.aspectj.biz.*Biz.*(..))。

返回通知使用@AfterReturning(pointcut="bizPointcut()",returning="returnValue"),表示继承了pointcut()方法的切入表达式,并接受了业务类执行方法的返回参数returnValue。

异常抛出通知:@AfterThrowing(pointcut="pointcut()",throwing="e")和返回通知一样

后置通知使用@After("pointcut()")

环绕通知使用@Around("pointcut()")

最后的@Before("pointcut()&&@annotation(terenceMethod)"),是下面所说的Advice传参的自定义注解的形式,自定义的注解为:

package com.terence.aop.aspectj.biz;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** 定义注解TerenceMethod*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interfaceTerenceMethod {String value();
}

通过@Before("pointcut()&&@annotation(terenceMethod)")的声明,首先集成pointcut()方法的切入表达式,然后用@annotation(terenceMethod)引入自定义的注解,和前面继承的表达式组合起来。

第四,测试类。

package com.terence.test.aop;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import com.terence.aop.aspectj.biz.TerenceBiz;
import com.terence.test.base.UnitTestBase;
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAspectJ extends UnitTestBase {publicTestAspectJ(){super("classpath:spring-aop-aspectj.xml");}@Testpublic voidtest(){TerenceBizbiz=getBean("terenceBiz");biz.save("thisis test");}
}

第五,执行结果

信息: JSR-330 'javax.inject.Inject' annotation found and supported forautowiring

Around 1

Before

beforeWithAnnotation:TerenceBizsave with TerenceMethod

BeforeParam:this is test

TerenceBizsave:this is test

Around 2

After

AfterReturning:Savesuccess!

二月 19, 2017 2:18:02下午 org.springframework.context.support.AbstractApplicationContextdoClose

信息: Closingorg.springframework.context.support.ClassPathXmlApplicationContext@175390b7:startup date [Sun Feb 19 14:18:01 CST 2017]; root of context hierarchy

6. Advice扩展

Advice传参 

方法1:一般形式

方法2:分离形式

方法3:自定义注解

Advice的参数及泛型Spring AOP可以处理泛型类的声明和使用方法的参数:
public interface Sample<T>
{void sampleGenericMethod(T param);void sampleGenericCollectionMethod(Collection<T> param);
}
@Before("execution(*..Sample+.sampleGenericMethod(*))&& args(param)")
public void beforeSampleMethod(Mytypeparam)
{//Advice implementation;
}
@Before("execution(*..Sample+.sampleGenericCollectionMethod(*))&& args(param)")
public void beforeSampleMethod(Collection<Mytype>param)
{//Advice implementation;
}

Advice参数名称

通知和切入点注解有一个额外的argNames属性,他可以用来指定所注解的方法的参数名。

 @Before(value="com.terence.lib.Pointcuts.anyPublicMehtod()&&target(bean)&&@annotation(auditable)",argNames="bean,auditable")public void audit(Object bean,Auditableauditable){Auditable code=auditable.value();//..use code and  bean}

如果第一个参数是JointPoitn,ProceedingJoinPoint,JoinPoint.StaticPart,那么可以忽略它。

@Before(value="com.terence.lib.Pointcuts.anyPublicMehtod()&&target(bean)&&@annotation(auditable)",argNames="bean,auditable")public void audit(JoinPoint jp,Objectbean,Auditable auditable){Auditable code=auditable.value();//..use code, bean and jp}

3.4 Introductions

允许一个切面声明一个通知对象实现指定接口,并且提供了一个接口实现类来代表这些对象,Introduction使用@DeclareParents进行注解,这个注解用来定义匹配的类型有一个新的parent.

例如:给定一个接口UseageTracked,并且该接口拥有DefaultUsageTracked的实现,接下来的切面声明了所有的service接口的实现都实现了UseageTracked接口,使用方式和基于配置的AOP是一样的,区别仅仅是此处用注解声明。

@Aspect
public class TerenceUsageTracking {    @DeclareParents(value="com.terence.aop.aspectj.*+",
defaultImpl=DefaultUsageTracked.class)public static UsageTracked mixin;@Before("com.terence.aop.aspectj.biz.businessService()&&this(usageTracked)")public void recordUsage(UsageTrackedusageTracked)    {usageTracked.incrementUseCount();}
}

另一个点:切面实例化模型

切面实例化模型是个高级的主题,“perthis”切面通过制定@Aspect注解perthis子句实现

每个独立的service对象执行时都会创建一个切面实例

Service对象的每个方法在第一次执行的时候创建切面实例,切面在service对象失效的同时也失效。

@Aspect("perthis(com.terence.aop.SystemArchitecture.businessService())")
public class MyAspect
{private int someState; @Before(com.terence.aop.SystemArchitecture.businessService())public void recordingServiceUseage(){//..}
}

AspectJ 学习笔记相关推荐

  1. AspectJ学习笔记

    介绍 AspectJ是一个基于Java语言的AOP框架 Spring2.0以后新增了对AspectJ切点表达支持 @AspectJ是AspectJ1.5新增功能,通过JDK5注解技术,允许Bean类中 ...

  2. AspectJ 学习笔记:Aspect的生命周期

    默认的情况下,Aspect类只有一个实例存在于虚拟机中,也就是作为单例存在的,对于每个对象来说,方面是共享的.一般这样的方面,不能用来保存相应对象的状态.    对于AspectJ 来说,Aspect ...

  3. 学习笔记:cache 和spring cache 技术(1)

    title: 学习笔记:cache 和spring cache 技术(1) author: Eric liu tags: [] categories: hexo 缓存是实际工作中非常常用的一种提高性能 ...

  4. Spring学习笔记(三) AOP_annotation,AOP_XML

    在学习课程以前,听说AOP有种很神秘的感觉,好像很好深的技术.其实原理很简单,使用动态代理的方式给程序增加逻辑.与此相似的有struts2中的filter拦截器. 再讲AOP之前先把需求说一下: 同S ...

  5. Spring Boot学习笔记-基础(2)

    Spring Boot学习笔记-基础(2) Spring Boot 优点: – 快速创建独立运行的Spring项目以及与主流框架集成 – 使用嵌入式的Servlet容器,应用无需打成WAR包 – st ...

  6. [原创]java WEB学习笔记107:Spring学习---AOP切面的优先级,重用切点表达式

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  7. Spring源码学习笔记:起源发展和核心模块主要职能

    1.博客内容均出自于咕泡学院架构师第三期 2.架构师系列内容:架构师学习笔记(持续更新) 1.Spring 的前世今生 早在 2007 年,一个基于 Java语言的开源框架正式发布,取了一个非常有活力 ...

  8. Spring源码学习笔记:Spring设计模式对比和Spring的OOB,BOP,AOP,IOC,DI/DL

    1.博客内容均出自于咕泡学院架构师第三期 2.架构师系列内容:架构师学习笔记(持续更新) 1.GOF 23总设计模式归纳 分类 设计模式 创建型 工厂方法模式(Factory Method).抽象工厂 ...

  9. 8. SpringBoot基础学习笔记

    SpringBoot基础学习笔记 课程前置知识说明 1 SpringBoot基础篇 1.1 快速上手SpringBoot SpringBoot入门程序制作 1.2 SpringBoot简介 1.2.1 ...

最新文章

  1. Bzoj 2064 分裂 题解
  2. python安装第三方库-Python安装第三方库的3种方法
  3. python处理xml文件_Python解析并修改XML文件
  4. IT资料,重磅来袭!
  5. python如何实现支持中文
  6. Python 命令行解析器argparse及传参数详解
  7. 支付宝将砸十亿支持中国女足发展:她们才是第一女子天团
  8. 【java】统计英文文本中某些字母出现的次数
  9. php调用7天内容,如何使用JS取得最近7天与最近3天日期
  10. atitit software sys 软件技术领域工业体系.docx 目录 1. 技术领域一级大类10大类 2 2. 理论与软件设计方法学 2 2.1. 计算机原理 计算机科学导论 2 2.2.
  11. 快门(曝光时间)、光圈、感光度(ISO)的关系
  12. 前端基础——数组的方法
  13. 为pr视频文件添加字幕
  14. 壁挂炉计算机控制系统,DIY壁挂炉温控器连入米家实现自动和远程控制
  15. np.arry()的用法
  16. 用Python制作简单的小游戏
  17. 网络安全攻防在线学习平台总结
  18. 10G光模块系列:10G 80公里(KM)光模块有哪些-易飞扬
  19. 互联网公司招聘--大众点评--产品岗--2015年笔试题
  20. 中国新四大发明背后的“数据智能”

热门文章

  1. RocketMQ的Quorum Write和自动降级
  2. .java编译成.class 与 .class反编译成.java
  3. 软件工程导论患者监护系统可行性研究
  4. 什么是前向纠错(FEC)?FEC最全解析
  5. ASP.NET中 RadioButtonList(单选按钮组)的使用
  6. 有必要升级到php7,升级到 PHP 7.4
  7. 机器视觉中的像素、分辨率、灰度值等概念
  8. c++学习笔记-二进制文件操作(哔站-黑马程序员c++教学视频)
  9. 面向对象:对象的概念
  10. 主成分分析法(PCA)解析与MATLAB实践