一、AOP概念

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码.Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。

在实际开发中,我们会有很多与我们业务逻辑无关但是又不得不写的代码,例如如果我们需要使用jwt签名验证我们需要在很多接口前都要进行签名的校验,根据签名的正确与否来决定是否拥有调用此接口的权利。如果我们在每一个接口前都进行这一系列的复杂校验,不仅会增加程序员的代码量,影响他们写业务逻辑的思路,也会造成代码的冗余,使同一功能代码书写多次。还包括权限检查、性能监控以及事务管理逻辑(都称为横切逻辑)。

二、AOP中常用的术语

  1. 连接点(Joinpoint): 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,也可以理解连接点为:目标类上哪些有可能被增强的方法。
  2. 切点(Pointcut):可以理解为查询条件。一个target(目标类)的所有方法都是连接点,切点可以通过查询条件定位特定的连接点。
  3. 增强(Advice):织入目标类连接点上的一段程序代码。既包含连接点上的执行逻辑(横切逻辑、增强逻辑)又包含定位连接点的方位信息,before、after、around等。增强默认织入目标类的所有方法中。
  4. 目标对象(Target):增强逻辑织入的目标类。
  5. 代理(Proxy):一个类被AOP植入增强后,被产生一个结果代理类。
  6. 织入(Weaving):将通知(增强)应用到连接点上,生成代理的过程。
  7. 切面(Aspect):由切点和增强组成。
  8. 引介(Introduction):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
Spring AOP通过Pointcut指定在哪些类的那些方法上织入横切逻辑,通过Advice描述横切逻辑和方法的具体织入点(方法前、方法后、方法的两端等)。
此外,spring通过Advisor将advice和pointcut组装起来。也可以通过AspectJ描述切面信息,如下图所示

三、常见增强类型

  1. 前置增强(Beforeadvice): 在目标方法执行前实施增强
  2. 后置增强(Afterreturningadvice):在目标方法执行后实施增强
  3. 环绕增强(Aroundadvice):在目标方法执行前后实施增强
  4. 异常抛出增强(Throwsadvice):在方法抛出异常后实施增强
  5. 引介增强(IntroductionInterceptor):在目标类中添加一些新的方法和属性。
  6. final增强:可以看成异常抛出增强和后置增强的混合物,一般用于释放资源,相当于try finally的控制流。 AspectJ有此增强类型,Spring4.0中还没有此增强类型。

四、支撑Spring AOP的底层Java技术:动态代理

spring AOP核心技术就是使用了Java 的动态代理技术, 这里简单的总结下JDK和CGLIB两种动态代理机制.

概念:
当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁--代理对象。按照代理对象的创建时期不同,可以分为两种:
静态代理:程序员事先写好代理对象类,在程序发布前就已经存在了;
动态代理:应用程序发布后,通过动态创建代理对象。
其中动态代理又可分为:JDK/Cglib 动态代理。

静态代理、动态代理等概念以及demo代码可参考:https://www.cnblogs.com/boboxing/p/8126046.html

4.1 JDK动态代理

此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。

代理模式在实际使用时需要指定具体的目标对象,如果为每个类都添加一个代理类的话,会导致类很多,同时如果不知道具体类的话,怎样实现代理模式呢?这就引出动态代理。

JDK动态代理只能针对实现了接口的类生成代理。

代码实例:
UserService.java:

public interface UserService {public void save();public void update();public void delete();public void find();
}

UserServiceImpl.java:

public class UserServiceImpl implements UserService {@Overridepublic void save() {System.out.println("保存用户...");}@Overridepublic void update() {System.out.println("修改用户...");}@Overridepublic void delete() {System.out.println("删除用户...");}@Overridepublic void find() {System.out.println("查询用户...");}}

MyJdbProxy.java:

/*** 使用JDK的动态代理实现代理机制**/
public class MyJdbProxy implements InvocationHandler{private UserService userService;public MyJdbProxy(UserService userService){this.userService = userService;}public UserService createProxy(){// 生成UserSErvice的代理:UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), this);return userServiceProxy;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// 判断是否是save方法:if("save".equals(method.getName())){// 增强:System.out.println("权限校验===========");return method.invoke(userService, args);}return method.invoke(userService, args);}}

SpringDemo.java 测试类:

public class SpringDemo1 {@Test// 没有代理的时候的调用方式public void demo1() {// 创建目标对象UserService userService = new UserServiceImpl();userService.save();userService.update();userService.delete();userService.find();}@Test// 使用代理public void demo2() {// 创建目标对象UserService userService = new UserServiceImpl();UserService proxy = new MyJdbProxy(userService).createProxy();proxy.save();proxy.update();proxy.delete();proxy.find();}
}

4.2 CGLib动态代理

CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所以弗雷方法的调用并顺势织入横切逻辑。

如果目标对象没有实现接口,则默认会采用CGLIB代理;

如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

代码实例:
CustomerService.java:

public class CustomerService {public void save(){System.out.println("保存客户...");}public void update(){System.out.println("修改客户...");}public void delete(){System.out.println("删除客户...");}public void find(){System.out.println("查询客户...");}
}

MyCglibProxy.java:

/*** 使用Cglib产生代理**/
public class MyCglibProxy implements MethodInterceptor{private CustomerService customerService;public MyCglibProxy(CustomerService customerService){this.customerService = customerService;}public CustomerService createProxy(){// 创建核心类:Enhancer enhancer = new Enhancer();// 设置父类:
        enhancer.setSuperclass(customerService.getClass());// 设置回调:enhancer.setCallback(this);// 创建代理:CustomerService customerServiceProxy = (CustomerService) enhancer.create();return customerServiceProxy;}@Overridepublic Object intercept(Object proxy, Method method, Object[] arg,MethodProxy methodProxy) throws Throwable {if("delete".equals(method.getName())){Object obj = methodProxy.invokeSuper(proxy, arg);System.out.println("日志记录==========");return obj;}return methodProxy.invokeSuper(proxy, arg);}
}

SpringDemo.java 测试类:

public class SpringDemo2 {@Testpublic void demo1(){CustomerService customerService = new CustomerService();customerService.save();customerService.update();customerService.delete();customerService.find();}@Testpublic void demo2(){CustomerService customerService = new CustomerService();// 产生代理:CustomerService proxy = new MyCglibProxy(customerService).createProxy();proxy.save();proxy.update();proxy.delete();proxy.find();}
}

五、Spring的传统的AOP:基于ProxyFactoryBean的方式的代理与BeanPostProcesser自动代理

5.1 使用ProxyFactoryBean配置代理

需要描述Advice、target、以及代理类的信息(增强、target-ref)

根据Advice代理,采用了JDK动态代理(对接口代理)

<bean id="greetingAdvice" class="com.smart.advice.GreetingBeforeAdvice" />
<bean id="target" class="com.smart.advice.NaiveWaiter" />
<bean id="waiter"class="org.springframework.aop.framework.ProxyFactoryBean"p:proxyInterfaces="com.smart.advice.Waiter"p:target-ref="target"p:interceptorNames="greetingAdvice"/>

根据Advisor代理,proxyTargetClass设置为true,采用了CGLib动态代理技术(创建子类来代理target对象)
<bean id="waiterTarget" class="com.smart.advisor.Waiter" />
<bean id="sellerTarget" class="com.smart.advisor.Seller" />
<bean id="greetingAdvice" class="com.smart.advisor.GreetingBeforeAdvice" />
<!-- 正则表达式方法名匹配切面 -->
<bean id="regexpAdvisor"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"p:advice-ref="greetingAdvice"><property name="patterns"><list><value>.*greet.*</value></list></property>
</bean>
<bean id="waiter1" class="org.springframework.aop.framework.ProxyFactoryBean"p:interceptorNames="regexpAdvisor" p:target-ref="waiterTarget"p:proxyTargetClass="true" />

基于ProxyFactoryBean的方式生成代理的过程中不是特别理想:

* 配置繁琐,不利为维护.

* 需要为每个需要增强的类都配置一个ProxyFactoryBean.

5.2 使用BeanPostProcessor自动创建代理

自动代理基于BeanPostProcessor完成的代理.

* 在类的生成过程中就已经是代理对象.

基于ProxyFactoryBean方式代理:

* 先有目标对象,根据目标对象产生代理.

可通过Advisor定义的切面信息或AspectJ定义的切面信息自动生成代理。不用逐个定义target类,它会将容器中的Advisor自动织入匹配的目标类中。
通过Advisor定义的切面自动生成代理 (在xml文件中定义Advisior):
<bean id="waiter" class="com.smart.advisor.Waiter" />
<bean id="seller" class="com.smart.advisor.Seller" />
<bean id="greetingAdvice" class="com.smart.advisor.GreetingBeforeAdvice" />
<bean id="regexpAdvisor"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"p:patterns=".*greet.*" p:advice-ref="greetingAdvice" />
<beanclass="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" p:proxyTargetClass="true" />

六、AspectJ概述

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

Spring为了简化自身的AOP的开发,将AspectJ拿过来作为Spring自身一个AOP的开发.

AspectJ支持的增强类型在第三部分中已经提到。AspectJ切点表达式函数为该框架的核心部分,在这就不对语法进行详细说明,需要使用的时候现查即可。

如上图所示,首先在一个类上面声明@Aspect 通过该注解将PreGreetingAspect类标识为一个切面类。

@Before部分是增的强类型。后面execution是目标切点表达式。beforeGreeting是增强所使用的横切逻辑函数。

通过AspectJ定义的切面自动生成代理 (在java文件中定义AspectJ)所以说AspectJ是语言级别的AOP实现。
package com.smart.aspectj.example;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class PreGreetingAspect{@Before("execution(* greetTo(..))")public void beforeGreeting(){System.out.println("How are you");}
}

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<aop:aspectj-autoproxy/>
<!--bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/--><bean id="waiter" class="com.smart.NaiveWaiter" /><bean class="com.smart.aspectj.example.PreGreetingAspect" />
</beans>

七、在Spring Boot中使用AOP

在spring boot中使用aop就不用再xml文件中配置了代理bean等信息了,在pom.xml添加好aop的依赖。直接在切面类前声明Configuration或Component如下图:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;/*** Created by beyondLi on 2017/7/31.*/
//证明是一个配置文件(使用@Component也可以,因为点入后会发现@Configuration还是使用了@Component)
@Configuration
//证明是一个切面
@Aspect
public class ControllerAOP {//环绕aop//execution表达式 此表达式表示扫描controller下所有类的所有方法都执行此aop@Around("execution (* com.beyondli.controller..*.*(..))")public Object testAop(ProceedingJoinPoint pro) throws Throwable {//获取request请求提(需要时备用)//HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();//进入方法前执行的代码System.out.println("beginAround");//执行调用的方法Object proceed = pro.proceed();//方法执行完成后执行的方法System.out.println("endAround");return proceed;}}

由AspectJ定义的切面会自动扫描满足切点表达式的目标类并代理。

参考资料:

https://www.cnblogs.com/wang-meng/p/5641549.html

https://blog.csdn.net/liboyang71/article/details/76432538

《精通Spring4.X 企业应用开发实战》陈雄华 林开雄 文建国 编著

转载于:https://www.cnblogs.com/kukri/p/8905901.html

Spring AOP 学习总结相关推荐

  1. Spring——AOP学习(静态代理和动态代理模式)

    Spring--AOP学习(静态代理和动态代理模式) 一.代理模式 我们知道学习Spring最重要的两个知识点就是IOC和AOP,AOP的主要思想就是动态代理模式.在了解AOP之前我们必须学习动态代理 ...

  2. 在Intellij上面导入项目 AOP示例项目 AspectJ学习 Spring AoP学习

    为了学习这篇文章里面下载的代码:http://www.cnblogs.com/charlesblc/p/6083687.html 需要用Intellij导入一个已有工程.源文件原始内容也可见:link ...

  3. Spring AOP 学习笔记

    Spring 的AOP AOP 专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在Java EE应用中,常常通过AOP 来处理一些具有横切性质的系统级服务,如事务管理.安全检查.缓存 ...

  4. Spring AOP学习

    什么是AOP Spring AOP 面向切面编程,采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视.事务管理.安全检查.缓存) 使用纯Java实现,不需要专门的编译过程和类加载器,在运行期 ...

  5. spring AoP学习 -----AoP织入器ProxyFactory剖析

    spring AOP框架内使用AopFactory对使用的不同的代理实现机制进行适度的抽象, 只对不同的代理实现机制提供相应的实现,spring Aop框架内提供了JDK的动态代理和Cglib的两种机 ...

  6. Spring AOP学习笔记

    需明确的几个概念: l         通知(Advice):用于告知系统将有哪些新的行为. l         切入点(Pointcut):定义了通知应该在应用到那些连接点. l         目 ...

  7. spring AoP学习 -----AoP的基本概念

    Aop的发展经理过两个阶段,第一个是静态Aop阶段,第二个是动态Aop阶段. 静态Aop阶段:作为第一代的Aop,以最初的AspectJ为代表,特点是以相应的横切关注点以Aspect形式实现之后,会通 ...

  8. (转)spring aop(下)

    昨天记录了Spring AOP学习的一部分(http://www.cnblogs.com/yanbincn/archive/2012/08/13/2635413.html),本来是想一口气梳理完的.但 ...

  9. 转载:Spring AOP (下)

    昨天记录了Spring AOP学习的一部分(http://www.cnblogs.com/yanbincn/archive/2012/08/13/2635413.html),本来是想一口气梳理完的.但 ...

最新文章

  1. 软件性能测试vu脚本录制,利用LR插件完成性能测试脚本
  2. 银行的双重生物识别实验,是双重麻烦还是双重安全?
  3. 在Python中调用C++,使用SWIG
  4. Python学习笔记--2--面向对象编程
  5. 125. Leetcode 91. 解码方法 (动态规划- 字符串系列)
  6. Razer Synapse 0 day漏洞可获得Windows 10管理员权限
  7. Python中的三引号的用法
  8. PHP易混淆函数的区分方法及意义
  9. atm取款机的简单程序代码_LeNet:一个简单的卷积神经网络PyTorch实现
  10. 分享时刻第二期:砍掉成本(1) 课后总结
  11. CentOS7配置JAVA环境变量
  12. CKEditor、UEditor富文本编辑器原理(CSDN编辑器原理)
  13. seo伪原创/百度链接推送/原创检测工具,python超简单POST案例
  14. Revit二次开发 —— 点到面的投影
  15. PDCA 原则与Smart原则
  16. 星梦邮轮世界梦号推出深圳母港特别航次
  17. Apache Pulsar 社区发起 Pulsar Women 项目,促进女性参与开源社区
  18. mysql的to char data_数据库中的to char
  19. iOS 打开本地或下载excel文件
  20. GAPIT 3.0:全基因组关联分析与预测软件最新版发布

热门文章

  1. login.keyring的问题
  2. 【微信小程序】接口生成自定义首页二维码
  3. java caller_java中callee获取caller
  4. 武汉大学计算机学院期末考试时间,【通知公告】关于2018-2019学年第二学期期末考试工作安排的通知...
  5. Windows 10企业批量部署实战之WDS配置
  6. 银河麒麟系统QtCreator不能切换中文输入法问题解决
  7. C# 扫描并读取图片中的文字
  8. 十二星座----射手
  9. rmmod命令卸载驱动后重启后为什么驱动还在? 安排!
  10. 如何利用卫星遥感探测浒苔(绿藻)?