介绍aop
Spring AOP 是 Spring 框架中的一个模块,它是基于面向切面编程 (AOP) 的一个实现。它可以让开发者在不改变原有代码的情况下,通过横向切面的方式,实现对系统的增强。Spring AOP 提供了很多常用的切面,如日志、事务、安全等,同时也支持自定义切面。Spring AOP 采用了动态代理的方式,在程序运行时才会生成代理对象,从而实现对目标对象的增强。
AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。
AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。
目前最流行的 AOP 框架有两个,分别为 Spring AOP 和 AspectJ。
Spring AOP 使用纯 Java 实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。
AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。
名称 | 说明 |
Joinpoint(连接点) | 指那些被拦截到的点,在 Spring 中,可以被动态代理拦截目标类的方法。 |
Pointcut(切入点) | 指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。 |
Advice(通知) | 指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。 |
Target(目标) | 指代理的目标对象。 |
Weaving(植入) | 指把增强代码应用到目标上,生成代理对象的过程。 |
Proxy(代理) | 指生成的代理对象。 |
Aspect(切面) | 切入点和通知的结合。 |
AOP的概念和作用
AOP是一种编程范式,旨在解决横切关注点的代码重复问题。
AOP通过将横切关注点从主业务逻辑中分离出来,实现了更好的代码模块化和可维护性。
AOP可以在不改变原有代码的情况下,通过织入(Weaving)切面(Aspect)来实现对横切关注点的处理。
Spring AOP的基本原理和实现方式
Spring AOP基于动态代理(Dynamic Proxy)实现。
Spring AOP提供了两种代理方式:JDK动态代理和CGLIB代理。
JDK动态代理适用于基于接口的代理,而CGLIB代理适用于类级别的代理。
Spring AOP通过代理对象将切面织入到目标对象的方法调用中,实现对横切关注点的处理。
AOP中的主要概念
切面(Aspect):横切关注点的模块化单元,它包含了通知和切点。
连接点(Join Point):在程序执行过程中能够被切面织入的特定点。
切点(Pointcut):用于定义连接点的表达式,指定了哪些连接点将被切面织入。
通知(Advice):切面在连接点上执行的动作,包括前置通知、后置通知、异常通知、环绕通知和引入通知。
织入(Weaving):将切面应用到目标对象中的过程,可以在编译时、类加载时或运行时进行。
总结:Spring AOP通过代理和织入的方式,实现了对横切关注点的处理。它的基本原理是通过动态代理来生成代理对象,并将切面织入到目标对象的方法调用中。在AOP中,切面、连接点、切点、通知和织入是重要的概念,它们共同构成了Spring AOP的基本框架。
后置通知
实现org.springframework.aop.AfterReturningAdvice接口
买书返利(存在bug)
环绕通知
org.aopalliance.intercept.MethodInterceptor
类似拦截器,会包括切入点,目标类前后都会执行代码。
异常通知
org.springframework.aop.ThrowsAdvice
出现异常执行系统提示,然后进行处理。价格异常为例
过滤通知(适配器)
org.springframework.aop.support.RegexpMethodPointcutAdvisor
处理买书返利的bug
Spring AOP的应用场景
日志记录:可以使用AOP拦截方法,记录方法的执行时间、参数、返回值等信息,实现日志的自动记录。
安全控制:可以使用AOP拦截访问操作,实现对用户访问权限的控制,例如检查用户是否有权限访问某些资源。
性能监控:可以使用AOP拦截方法,监控方法的执行时间、调用次数等信息,实现对系统性能的监控和优化。
缓存管理:可以使用AOP拦截方法,实现缓存管理,例如缓存对象的读取、写入、删除等操作。
事务管理:可以使用AOP拦截方法,实现事务管理,例如对数据库操作进行事务控制。
异常处理:可以使用AOP拦截方法,实现异常处理,例如捕获异常并记录日志或者发送通知。
优点:
1. 降低了代码的重复性:通过将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,可以减少代码的重复性,提高代码的可维护性和可读性。
2. 提高了代码的模块化和可重用性:将横切关注点封装成切面,可以将其应用到多个目标对象中,提高了代码的模块化和可重用性。
3. 简化了业务逻辑的编写:通过使用切面,可以将与业务逻辑无关的代码(如事务管理、异常处理等)从业务逻辑中抽离出来,使业务逻辑更加清晰和简洁。
4. 提供了更好的代码结构和可扩展性:通过使用切面,可以将不同关注点的代码分离,使代码结构更加清晰,并且可以灵活地添加、修改或删除切面,以满足不同的需求。
缺点:
1. 仅支持方法级别的切面:Spring AOP只能对方法进行切面织入,对于其他级别的切面(如字段访问、对象创建等),需要使用其他的AOP框架。
2. 无法拦截私有方法和静态方法:由于Spring AOP基于动态代理实现,无法拦截私有方法和静态方法。
3. 对性能有一定的影响:由于Spring AOP使用动态代理来生成代理对象,并在方法调用时进行切面织入,会增加一定的性能开销。
4. 需要依赖Spring框架:使用Spring AOP需要依赖Spring框架,对于不使用Spring框架的项目来说,引入Spring框架可能会增加项目的复杂性。
前置通知
前置通知(Before advice):在目标方法执行前,执行的通知。可以用来在方法执行前进行一些准备或检查操作。
spring-context.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" default-autowire="byType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--目标对象--> <bean class="com.aop.biz.impl.BookBizImpl" name="bookBiz"></bean> <!--通知--> <bean class="com.aop.biz.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean> <!--代理--> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy"> <!-- 配置目标对象--> <property name="target" ref="bookBiz"></property> <!-- 配置代理的接口--> <property name="proxyInterfaces"> <list> <value>com.aop.biz.IBookBiz</value> </list> </property> <!--配置通知--> <property name="interceptorNames"> <list> <value>methodBeforeAdvice</value> </list> </property> </bean> </beans>
package com.aop.biz.advice; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; import java.util.Arrays; /** * 买书、评论前加系统日志 * @author Administrator * */ public class MyMethodBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { // 在这里,可以获取到目标类的全路径及方法及方法参数,然后就可以将他们写到日志表里去 String target = arg2.getClass().getName(); String methodName = arg0.getName(); String args = Arrays.toString(arg1); System.out.println("【前置通知:系统日志】:"+target+"."+methodName+"("+args+")被调用了"); } }
后置通知
后置通知(After advice):在目标方法执行后,执行的通知。可以用来在方法执行后进行一些清理操作,例如释放资源或记录执行结果。
package com.aop.biz.advice; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; import java.util.Arrays; /** * 买书返利 * @author Administrator * */ public class MyAfterReturningAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { String target = arg3.getClass().getName(); String methodName = arg1.getName(); String args = Arrays.toString(arg2); System.out.println("【后置通知:买书返利】:"+target+"."+methodName+"("+args+")被调用了,"+"该方法被调用后的返回值为:"+arg0); } }
环绕通知
环绕通知(Around advice):在目标方法执行前和执行后,执行的通知。可以用来在目标方法执行前后做一些预处理和后处理操作。
package com.zking.aop.advice; import java.util.Arrays; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; /** * 环绕通知 * 包含了前置和后置通知 * * @author Administrator * */ public class MyMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation arg0) throws Throwable { String target = arg0.getThis().getClass().getName(); String methodName = arg0.getMethod().getName(); String args = Arrays.toString(arg0.getArguments()); System.out.println("【环绕通知调用前:】:"+target+"."+methodName+"("+args+")被调用了"); // arg0.proceed()就是目标对象的方法 Object proceed = arg0.proceed(); System.out.println("【环绕通知调用后:】:该方法被调用后的返回值为:"+proceed); return proceed; } }
异常通知
异常通知(After throwing advice):在目标方法抛出异常时,执行的通知。可以用来做一些异常处理操作。
package com.aop.biz.advice; import org.springframework.aop.ThrowsAdvice; import com.aop.biz.Exception.PriceException; /** * 出现异常执行系统提示,然后进行处理。价格异常为例 * @author Administrator * */ public class MyThrowsAdvice implements ThrowsAdvice { public void afterThrowing(PriceException ex) { System.out.println("【异常通知】:当价格发生异常,那么执行此处代码块!!!"); } }
过滤通知
过滤通知(After returning advice):在目标方法执行成功并正常返回时,执行的通知。可以用来做一些返回结果的处理。
所有用到的bean <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" default-autowire="byType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--目标对象--> <bean class="com.aop.biz.impl.BookBizImpl" name="bookBiz"></bean> <!--通知--> <bean class="com.aop.biz.advice.MyMethodBeforeAdvice" id="methodBeforeAdvice"></bean> <bean class="com.aop.biz.advice.MyAfterReturningAdvice" id="myAfterReturningAdvice"></bean> <bean class="com.aop.biz.advice.MyMethodInterceptor" id="myMethodInterceptor"></bean> <bean class="com.aop.biz.advice.MyThrowsAdvice" id="myThrowsAdvice"></bean> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="regexpMethodPointcutAdvisor"> <property name="advice" ref="myAfterReturningAdvice"></property> <property name="pattern" value=".*buy"></property> </bean> <!--代理--> <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="bookProxy"> <!-- 配置目标对象--> <property name="target" ref="bookBiz"></property> <!-- 配置代理的接口--> <property name="proxyInterfaces"> <list> <value>com.aop.biz.IBookBiz</value> </list> </property> <!--配置通知--> <property name="interceptorNames"> <list> <value>methodBeforeAdvice</value> <!-- <value>myAfterReturningAdvice</value>--> <value>regexpMethodPointcutAdvisor</value> <value>myMethodInterceptor</value> <value>myThrowsAdvice</value> </list> </property> </bean> </beans>
总结:
Spring的AOP(面向切面编程)是通过动态代理实现的,它可以将横切关注点(如日志、事务、安全等)从业务逻辑中分离出来,以模块化的方式进行维护和复用,提高了程序的可维护性和扩展性。
在Spring中,AOP的实现主要依靠两个核心概念:切面和切点。切面是对一个或多个切点进行通知的模块化功能,它定义了在什么时候、在哪个地方、以何种方式进行通知,在Spring中,切面可以使用注解或XML配置来声明。切点是程序中用于插入切面的标记点,通过表达式或正则表达式定义,用于确定哪些方法需要被通知。
Spring的AOP通知包括前置通知、后置通知、返回通知、异常通知和环绕通知,每种通知都有不同的作用和执行顺序。为实现这些通知,Spring提供了不同的切面注解和方法,如@Before、@After、@AfterReturning、@AfterThrowing、@Around等。
总之,Spring的AOP是一种非常强大的编程范式,它可以在不修改业务代码的情况下,通过插入切面实现对程序行为的改变,提高程序的健壮性和可维护性。