什么是AOP
AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。
AOP技它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
实现AOP的技术,主要分为两大类:一是采用动态代理技术(典型代表为Spring AOP),利用截取消息的方式(典型代表为AspectJ-AOP),对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
相关概念
切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。切面用spring的 Advisor或拦截器实现。
连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice
切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上
引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口(使用较少)
目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO
AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
AOP概念的通俗理解
1.通知(Advice): 通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。
2.连接点(Joinpoint): 程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。
3.切入点(Pointcut) :通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,Spring中允许我们方便的用正则表达式来指定
4.切面(Aspect) :通知和切入点共同组成了切面:时间、地点和要发生的“故事”
5.引入(Introduction) :引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)
6.目标(Target) :即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事)
7.代理(proxy) :应用通知的对象,详细内容参见设计模式里面的代理模式
8.织入(Weaving) :把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
---- (1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器
---- (2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码
----(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术
Spring AOP的三种实现方式(基于Spring Boot)
一、基于XML配置的Spring AOP
现在都是spring boot的时代了,因此基于xml配置的例子,本文不做介绍了,有需要的可以自己去找其余博文阅读
二、基于ProxyFactoryBean,编码的方式来实现
导包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
有如下包即可正常工作了
备注:aopalliance.jar这个jar包已经不用单独引入了,因为Spring-AOP包已经把这个jar包内全部的类都已经放进来了(如下图),因此无需重复引入 只需要引入依赖包:org.aspectj:aspectjweaver即可
为何Spring自己实现了AOP,还需要导入org.aspectj:aspectjweaver的包呢?
官网解释的原因如下:
原因一:spring确实有自己的AOP。功能已经基本够用了,除非你的要在接口上动态代理或者方法拦截精确到getter和setter。这些都是写奇葩的需求Spring做不到,但一般不使用。
原因二:1、如果使用xml方式,不需要任何额外的jar包。2、如果使用@Aspect的注解方式。你就可以在类上直接一个@Aspect就搞定,不用费事在xml里配了。但是这需要额外的jar包( aspectjweaver.jar)。因为spring直接使用AspectJ的注解功能,注意只是使用了它 的注解功能而已。并不是核心功能 !!!
注意到文档上还有一句很有意思的话:文档说到 是选择spring AOP还是使用full aspectJ?什么是full aspectJ?如果你使用"full aspectJ"。就是说你可以实现基于接口的动态代理,等等强大的功能。而不仅仅是aspectj的 注-解-功-能 !!!
如果用full AspectJ。比如说Load-Time Weaving的方式 还 需要额外的jar包 spring-instrument.jar。。。现在明白了吧~~~ 具体详情,后面在讲述AspectJ里可以看见~~
基本类如下:
// A类: @Service public class AServiceImpl implements AService { @Override public void sayHelloA() { System.out.println("hello A"); } } // B类: @Service public class BServiceImpl implements BService { @Override public void sayHelloB() { System.out.println("hello B"); } } // C类: @Service public class CServiceImpl implements CService { @Override public void sayHelloC() { System.out.println("hello C"); } }
通过实现接口的方式编写的通知类
/** * 在方法之前、之后 打印输出日志 * * @author fangshixiang@vipkid.com.cn * @description * @date 2018-10-29 17:42 */ @Component //通知组件交给容器管理 public class LogAdvice implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor { //MethodBeforeAdvice的方法 @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("MethodBeforeAdvice...before..."); } //AfterReturningAdvice的方法 @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("AfterReturningAdvice...afterReturning..."); } //MethodInterceptor的方法 @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("MethodInterceptor...invoke...start"); Object proceed = invocation.proceed(); System.out.println("MethodInterceptor...invoke...end"); return proceed; } }
实现接口 MethodBeforeAdvice该拦截器会在调用方法前执行
实现接口 AfterReturningAdvice该拦截器会在调用方法后执行
实现接口 MethodInterceptor该拦截器会在调用方法前后都执行,实现环绕效果
然后就是配置了,其中最重要的类为ProxyFactoryBean、BeanNameAutoProxyCreator、AspectJExpressionPointcutAdvisor等等代理类,能达到强大的效果。这种一般都是spring时代基于xml的书写方式,因此这里不做详细讲解,SpringBoot时代,建议使用优雅的注解的风格编写,但本文提供一个参考博文:
Spring AOP之ProxyFactoryBean与BeanNameAutoProxyCreator