1、什么是AOP
面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式,AOP是OOP的延续。
AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,它将业务逻辑的各个部分进行隔离,使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。
AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。
AOP本质上只是一种代理模式的实现方式
目前最流行的 AOP 框架有两个,分别为 Spring AOP 和 AspectJ
Spring AOP 使用纯 Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。
AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。
2、AOP术语
为了更好地理解 AOP,就需要对 AOP 的相关术语有一些了解,
Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Advice 接口。
Spring 通知按照在目标类方法的连接点位置,可以分为以下五种类型,如下表所示。
3、Spring使用AspectJ实现AOP的方式
首先,我们通过Maven引入Spring对AOP的支持:
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version></dependency>
上述依赖会自动引入AspectJ,使用AspectJ实现AOP比较方便,因为它的定义比较简单。
3.1、方式一:使用原生Spring API接口
接口
publicinterfaceUserService{publicvoidadd();publicvoiddelete();publicvoidupdate();publicvoidquery();}
真实角色
publicclassUserServiceImplimplementsUserService{publicvoidadd(){System.out.println("增加了一个用户");}publicvoiddelete(){System.out.println("删除了一个用户");}publicvoidupdate(){System.out.println("修改了一个用户");}publicvoidquery(){System.out.println("查询了一个用户");}}
日志的实现类,要在不影响原业务代码的情况下执行 原生接口
//前置日志publicclassLogimplementsMethodBeforeAdvice{//method要执行的目标对象的方法//args参数//target目标对象publicvoidbefore(Methodmethod,Object[]args,Objecttarget)throwsThrowable{System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了");}}
//后置日志publicclassAfterLogimplementsAfterReturningAdvice{//returnValue执行后的返回值publicvoidafterReturning(ObjectreturnValue,Methodmethod,Object[]args,Objecttarget)throwsThrowable{System.out.println("执行了"+method.getName()+"方法,返回结果为"+returnValue);}}
applicationContext.xml(相当于代理角色)
<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.3.xsd"><!--注意:配置AOP需要导入约束--><beanid="userService"class="com.cheng.service.UserServiceImpl"/><beanid="log"class="com.cheng.log.Log"/><beanid="afterLog"class="com.cheng.log.AfterLog"/><!--方式一:使用SpringAPI接口--><aop:config><!--切入点expression表达式execution(切入要执行的切入点UserServiceImpl类下的所有方法下的所有参数--><aop:pointcutid="pointcut"expression="execution(*com.cheng.service.UserServiceImpl.*(..))"/><!--日志实现通过Spring这个中间商执行AOP切入,并不干预目标对象程序的执行--><!--执行环绕增加(就是在切入点执行的代码)--><aop:advisoradvice-ref="log"pointcut-ref="pointcut"/><!--把log这个类切入到execution里的方法里面--><aop:advisoradvice-ref="afterLog"pointcut-ref="pointcut"/></aop:config></beans>
测试
publicclassMyTest{publicstaticvoidmain(String[]args){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");//动态代理的是接口,所以这里要返回一个接口UserServiceuserService=context.getBean("userService",UserService.class);userService.delete();}}
3.2、方式二:自定义类实现AOP
自定义日志类
//自定义切入点类publicclassDiyPointCut{publicvoidbefore(){System.out.println("方法执行前");}publicvoidafter(){System.out.println("方法执行后");}}
applicationContext.xml
<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.3.xsd"><!--配置AOP需要导入约束--><beanid="userService"class="com.cheng.service.UserServiceImpl"/><!--方式二:自定义类--><beanid="diy"class="com.cheng.diy.DiyPointCut"/><aop:config><!--自定义一个切面(就是一个类)ref要引用的类--><aop:aspectref="diy"><aop:pointcutid="point"expression="execution(*com.cheng.service.UserServiceImpl.*(..))"/><!--在point切入点执行下面两个方法--><aop:beforemethod="before"pointcut-ref="point"/><aop:aftermethod="after"pointcut-ref="point"/></aop:aspect></aop:config></beans>
测试代码不变
3.3、方式三:使用注解实现AOP
在 Spring 中,尽管使用 XML 配置文件可以实现 AOP 开发,但是如果所有的相关的配置都集中在配置文件中,势必会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。
为此,AspectJ 框架为 AOP 开发提供了另一种开发方式——基于 Annotation 的声明式。AspectJ 允许使用注解定义切面、切入点和增强处理,而 Spring 框架则可以识别并根据这些注解生成 AOP 代理。
用注解定义一个切面类
packagecom.cheng.diy;importorg.aspectj.lang.annotation.After;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;//使用注解方式实现AOP@Aspect//标注这个类是一个切面publicclassAnnotationPointCut{@Before("execution(*com.cheng.service.UserServiceImpl.*(..))")//@Before注解里的内容就是切入点pointcut的内容publicvoidbefore(){System.out.println("=======方法执行前=======");}@After("execution(*com.cheng.service.UserServiceImpl.*(..))")publicvoidafter(){System.out.println("=======方法执行后=======");}}
applicationContext.xml
publicinterfaceUserService{publicvoidadd();publicvoiddelete();publicvoidupdate();publicvoidquery();}0
测试代码不变