首页>>后端>>Spring->Spring——AOP实现方式

Spring——AOP实现方式

时间:2023-11-29 本站 点击:0

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 的相关术语有一些了解,

名称说明Joinpoint(连接点)指那些被拦截到的点,在 Spring 中,可以被动态代理拦截目标类的方法。Pointcut(切入点)指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。Advice(通知)指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。方法Target(目标)指代理的目标对象。Weaving(植入)指把增强代码应用到目标上,生成代理对象的过程。Proxy(代理)指生成的代理对象。Aspect(切面)切入点和通知的结合。类

Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Advice 接口。

Spring 通知按照在目标类方法的连接点位置,可以分为以下五种类型,如下表所示。

名称说明org.springframework.aop.MethodBeforeAdvice(前置通知)在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。org.springframework.aop.AfterReturningAdvice(后置通知)在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。org.aopalliance.intercept.MethodInterceptor(环绕通知)在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。org.springframework.aop.ThrowsAdvice(异常通知)在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。org.springframework.aop.IntroductionInterceptor(引介通知)在目标类中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。

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 代理。

名称说明@Aspect用于定义一个切面。@Before用于定义前置通知,相当于 BeforeAdvice。@AfterReturning用于定义后置通知,相当于 AfterReturningAdvice。@Around用于定义环绕通知,相当于MethodInterceptor。@AfterThrowing用于定义抛出通知,相当于ThrowAdvice。@After用于定义最终final通知,不管是否异常,该通知都会执行。@DeclareParents用于定义引介通知,相当于IntroductionInterceptor(不要求掌握)。

用注解定义一个切面类

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

测试代码不变


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Spring/317.html