Spring框架自诞生之日就拯救我等程序员于水火之中,它有两大法宝,一个是IoC控制反转,另外一个即是AOP面向切面编程。今日咱们就来破一下它的AOP法宝,以便之后也能自由使出一手AOP大法。程序员
AOP全名Aspect-oriented programming面向切面编程大法,它有不少兄弟,分别是常常见的面向对象编程,朴素的面向过程编程和神秘的函数式编程等。所谓AOP的具体解释,以及和OOP的区别不清楚的同窗能够自行去了解。express
AOP实现的关键在于AOP框架自动建立的AOP代理,AOP代理主要分为静态代理和动态代理。本文就主要讲解AOP的基本术语,而后用一个例子让你们完全搞懂这些名词,最后介绍一下AOP的两种代理方式:编程
切面是一个横切关注点的模块化,一个切面可以包含同一个类型的不一样加强方法,好比说事务处理和日志处理能够理解为两个切面。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。 Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的链接点中。app
@Component @Aspect public class LogAspect { }
能够简单地认为, 使用 @Aspect 注解的类就是切面框架
目标对象指将要被加强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。模块化
程序执行过程当中明确的点,如方法的调用或特定的异常被抛出。链接点由两个信息肯定:函数式编程
简单来讲,链接点就是被拦截到的程序执行点,由于Spring只支持方法类型的链接点,因此在Spring中链接点就是被拦截到的方法。函数
@Before("pointcut()") public void log(JoinPoint joinPoint) { //这个JoinPoint参数就是链接点 }
切入点是对链接点进行拦截的条件定义。切入点表达式如何和链接点匹配是AOP的核心,Spring缺省使用AspectJ切入点语法。
通常认为,全部的方法均可以认为是链接点,可是咱们并不但愿在全部的方法上都添加通知,而切入点的做用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配链接点,给知足规则的链接点添加通知。源码分析
@Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))") public void pointcut() { }
上边切入点的匹配规则是com.remcarpediem.test.aop.service
包下的全部类的全部函数。ui
通知是指拦截到链接点以后要执行的代码,包括了“around”、“before”和“after”等不一样类型的通知。Spring AOP框架以拦截器来实现通知模型,并维护一个以链接点为中心的拦截器链。
// @Before说明这是一个前置通知,log函数中是要前置执行的代码,JoinPoint是链接点, @Before("pointcut()") public void log(JoinPoint joinPoint) { }
织入是将切面和业务逻辑对象链接起来, 并建立通知代理的过程。织入能够在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。
Advisor是切面的另一种实现,可以将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。Advisor由切入点和Advice组成。
Advisor这个概念来自于Spring对AOP的支撑,在AspectJ中是没有等价的概念的。Advisor就像是一个小的自包含的切面,这个切面只有一个通知。切面自身经过一个Bean表示,而且必须实现一个默认接口。
// AbstractPointcutAdvisor是默认接口 public class LogAdvisor extends AbstractPointcutAdvisor { private Advice advice; // Advice private Pointcut pointcut; // 切入点 @PostConstruct public void init() { // AnnotationMatchingPointcut是依据修饰类和方法的注解进行拦截的切入点。 this.pointcut = new AnnotationMatchingPointcut((Class) null, Log.class); // 通知 this.advice = new LogMethodInterceptor(); } }
看完了上面的理论部分知识, 我相信仍是会有很多朋友感受AOP 的概念仍是很模糊, 对 AOP 的术语理解的还不是很透彻。如今咱们就找一个具体的案例来讲明一下。
简单来说,整个 aspect 能够描述为: 知足 pointcut 规则的 joinpoint 会被添加相应的 advice 操做。咱们来看下边这个例子。
@Component @Aspect // 切面 public class LogAspect { private final static Logger LOGGER = LoggerFactory.getLogger(LogAspect.class.getName()); // 切入点,表达式是指com.remcarpediem.test.aop.service // 包下的全部类的全部方法 @Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))") public void aspect() {} // 通知,在符合aspect切入点的方法前插入以下代码,而且将链接点做为参数传递 @Before("aspect()") public void log(JoinPoint joinPoint) { //链接点做为参数传入 if (LOGGER.isInfoEnabled()) { // 得到类名,方法名,参数和参数名称。 Signature signature = joinPoint.getSignature(); String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String[] argumentNames = methodSignature.getParameterNames(); StringBuilder sb = new StringBuilder(className + "." + methodName + "("); for (int i = 0; i< arguments.length; i++) { Object argument = arguments[i]; sb.append(argumentNames[i] + "->"); sb.append(argument != null ? argument.toString() : "null "); } sb.append(")"); LOGGER.info(sb.toString()); } } }
上边这段代码是一个简单的日志相关的切面,依次定义了切入点和通知,而链接点做为log的参数传入进来,进行必定的操做,好比说获取链接点函数的名称,参数等。
所谓静态代理就是AOP框架会在编译阶段生成AOP代理类,所以也称为编译时加强。ApsectJ是静态代理的实现之一,也是最为流行的。静态代理因为在编译时就生成了代理类,效率相比动态代理要高一些。AspectJ能够单独使用,也能够和Spring结合使用。
与静态代理不一样,动态代理就是说AOP框架不会去修改编译时生成的字节码,而是在运行时在内存中生成一个AOP代理对象,这个AOP对象包含了目标对象的所有方法,而且在特定的切点作了加强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式:JDK动态代理和CGLIB动态代理。
JDK代理经过反射来处理被代理的类,而且要求被代理类必须实现一个接口。核心类是 InvocationHandler接口 和 Proxy类。
而当目标类没有实现接口时,Spring AOP框架会使用CGLIB来动态代理目标类。
CGLIB(Code Generation Library),是一个代码生成的类库,能够在运行时动态的生成某个类的子类。CGLIB是经过继承的方式作的动态代理,所以若是某个类被标记为final,那么它是没法使用CGLIB作动态代理的。核心类是 MethodInterceptor 接口和Enhancer 类
AOP的基础知识都比较枯燥,本人也不擅长概念性的文章,不过下一篇文章就是AOP源码分析了,但愿你们能够继续关注。