介绍下AspectJ和AOP和关系
AspectJ是java编程语言的无缝的面向方面的扩展,可以在java代码的字节码中植入切面代码。
AspectJ 是静态代理的增强,所谓的静态代理就是 AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强。
AspectJ 是最早、功能比较强大的 AOP 实现之一,对整套 AOP 机制都有较好的实现,很多其他语言的 AOP 实现,也借鉴或采纳了 AspectJ 中很多设计。在 Java 领域,AspectJ 中的很多语法结构基本上已成为 AOP 领域的标准。
AOP面向切面编程是一种编程思想,是对OOP面向对象编程的一种补充。对于AOP这种编程思想,很多框架都进行了实现。Spring就是其中之一,可以完成面向切面编程。而AspectJ也实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring又将AspectJ对于AOP的实现也引入到了自己的框架中。
AspectJ 作为 AOP 编程的完全解决方案,提供了三种织入时机,分别为
compile-time:编译期织入,在编译的时候一步到位,直接编译出包含织入代码的 .class 文件
post-compile:编译后织入,增强已经编译出来的类,如我们要增强依赖的 jar 包中的某个类的某个方法
load-time:在 JVM 进行类加载的时候进行织入
aspectj静态代理
原理:在编译期织入代码,编译成class文件
优点:可以增强任何类,任何方法,包括(final,static修饰)
缺点:需要使用aspecj提供的Ajc编译器来编译Aj文件。
jdk动态代理
使用jdk的动态代理来增强接口实现类
原理:使用Proxy类的newProxyInstance方法运行期通过反射动态的生成代理对象
优点:不需要修改具体的业务代码,动态的增强方法,降低耦合性。
缺点:代理的对象必须有接口实现。
cglib的动态代理
原理:以创建目标类的子类来生成动态代理对象。
优点:不需要修改具体的业务代码,动态的增强方法,降低耦合性。
缺点:不能对final修饰的类,final修饰的方法或static的方法进行代理
Spring AOP中aspect、advise、pointcut、advisor分别有什么意义?
处理逻辑(Advice):在某个连接点所采用的处理逻辑。
切点(PointCut):一系列连接点的集合,它指明处理方式(Advice)将在何处被触发,可以使用正则表达式表达。
Advisor:是PointCut和Advice的综合体,完整描述了一个advice将会在pointcut所定义的位置被触发。
— 方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。
— 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
— 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。
— 切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式
所以aop:aspect实际上是定义横切逻辑,就是在连接点上做什么,aop:advisor则定义了在哪些连接点上应用什么aop:aspect。Spring这样做的好处就是可以让多个横切逻辑 (即aop:aspect定义的)多次使用,提供可重用性。
1、Adivisor是一种特殊的Aspect,Advisor代表spring中的Aspect
2、advisor只持有一个Pointcut和一个advice,而aspect可以多个pointcut和多个advice
介绍AOP有几种实现方式
1、利用代理模式动态的实现AOP,从具体的技术细节又可以分为静态代理,动态代理;
2、使用预编译的方法静态进行代理;
3、使用自定义加载器的方法动态进行代理。后两种代理都可以代理更多的内容,比如构造函数,静态方法,静态块,私有方法等。
=============================================================================================
1.采用spring的原生API进行实现
先定义通知类:
after通知类型的通知类实现AfterRuturnAdvice接口,实现内部的方法,在方法中加入你要通知的操作
before通知类型的通知类实现MethodBeforeAdvice接口,实现内部的方法,在方法中加入你要通知的操作
之后便是在application配置文件中声明通知类bean、插入类bean,配置切入点、通知
然后,当你运行被切入的类的切入点方法时,前置通知和后置通知都会执行。
2.第二种方法定义切面类,然后在application配置文件中进行配置
先创建一个切面类,再在切面类中创建你要插入的通知方法
<!-- 目标对象--->
<bean id="target" class="com.hzd.aop.Method"/>
<!-- 切面对象-->
<bean id="myAspect" class="com.hzd.aop.Aspect"/>
<!-- 配置织入,告诉spring框架 哪些方法需要进行哪些增强(前置、后置),需要引入aop命名空间-->
<aop:config>
<!-- 声明切面-->
<aop:aspect ref="Aspect">
<!-- 抽取切点表达式-->
<aop:pointcut id="myPonintcut" expression="execution(public void com.hzd.aop.Method.method())"/>
<!-- 切面,切点和通知-->
<aop:before method="before" pointcut-ref="myPonintcut"></aop:before>
<aop:after-returning method="after" pointcut-ref="myPonintcut"></aop:after-returning>
<aop:around method="around" pointcut-ref="myPonintcut"></aop:around>
</aop:aspect>
</aop:config>
- 第三种:用注释配置切面类、通知、切入点
@component 注解:将该类声明进入spring容器中
@Aspect:声明是一个切面类bean,而不是普通bean
@before(切面点)修饰方法:表面这是一个前置方法
@After(切面点)声明这是一个后置方法
@Pointcut 声明这是一个切点表达式方法
简单介绍Spring-AOP的底层原理
AOP concepts(AOP术语)
Aspect/Advisors(切面)
一个关注点的模块化,这个关注点可能会横切多个对象。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现。
Join point(连接点)
在程序执行期间的一点。在Spring AOP中,连接点总是表示方法执行。
Advice(通知)
在切面的某个特定的连接点上执行的动作。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
Pointcut(切入点)
查找连接点的条件。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行。
Introduction(引入)
给一个类型声明额外的方法或属性。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。
Target object(目标对象)
被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
AOP proxy
AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
Weaving(织入)
织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期、类装载期、运行期进行。
Spring框架的AOP的底层实现
1、基于JDK的动态代理
必须是面向接口的,只有实现了具体接口的类才能生成代理对象
2、基于CGLIB动态代理
对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式
Spring的传统AOP中根据类是否实现接口,来采用不同的代理方式
如果实现类接口,使用JDK动态代理完成AOP
如果没有实现接口,采用CGLIB动态代理完成AOP
==========================================================
aop底层采用的是动态代理机制实现的:接口+实现类。
如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy创建代理对象。
没有实现接口的对象,就无法使用JDK Proxy去进行代理,这时候Spring AOP会使用Cglib生成一个被代理对象的子类来作为代理
=================================================================
【1】AOP的设计
在Spring的底层,如果我们配置了代理模式,Spring会为每一个Bean创建一个对应的ProxyFactoryBean的FactoryBean来创建某个对象的代理对象。
每个 Bean 都会被 JDK 或者 Cglib 代理。取决于是否有接口。
每个 Bean 会有多个“方法拦截器”。注意:拦截器分为两层,外层由 Spring 内核控制流程,内层拦截器是用户设置,也就是 AOP。
当代理方法被调用时,先经过外层拦截器,外层拦截器根据方法的各种信息判断该方法应该执行哪些“内层拦截器”。内层拦截器的设计就是职责连的设计。
【2】代理的创建
首先,需要创建代理工厂,代理工厂需要 3 个重要的信息:拦截器数组,目标对象接口数组,目标对象。
创建代理工厂时,默认会在拦截器数组尾部再增加一个默认拦截器 —— 用于最终的调用目标方法。
当调用 getProxy 方法的时候,会根据接口数量大余 0 条件返回一个代理对象(JDK or Cglib)。
注意:创建代理对象时,同时会创建一个外层拦截器,这个拦截器就是 Spring 内核的拦截器。用于控制整个 AOP 的流程。
【3】代理的调用
当对代理对象进行调用时,就会触发外层拦截器。
外层拦截器根据代理配置信息,创建内层拦截器链。创建的过程中,会根据表达式判断当前拦截是否匹配这个拦截器。而这个拦截器链设计模式就是职责链模式。
当整个链条执行到最后时,就会触发创建代理时那个尾部的默认拦截器,从而调用目标方法。最后返回。
调用过程: