不同于OOP--面向对象编程,提供一系列继承、重写、封装技术,纵向的丰富编程功能。spring AOP为面向横向的切面编程,当工程中很多类都有共同的需求时,可以针对这些类,将共用的方法抽离出来,形成一个切面方法,将他织入到这些类中。每当执行这些类的时候,自动触发织入的切面方法,这样就不用在这些类中写重复的代码。
一 AOP的基本概念
(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
二 Spring AOP
Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。
AOP有两种实现方式,一种基于XML配置,一种基于注解,本篇先写一个基于XML配置的例子,下篇中讲解基于注解的AOP编程。
1、在spring中使用AOP变成,不止要导入spring-aop.jar,还需要导入spring-aspects.jar、aspectjweaver.jar和aopalliance.jar,但是aspectjweaver.jar被spring-aspects.jar依赖,aopalliance.jar被spring-aop.jar依赖,spring-aop.jar又被spring-mvc.jar依赖,所以只要导入spring-mvc.jar和spring-aspects.jar就行了。
pom.xml文件:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.0.5.RELEASE</spring.version> </properties> <dependencies> <!-- springMvc框架,它依赖于spring的其他jar包,会自动导入spring的core、beans、web等jar包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency>
2、编写被增强的类,即被织入切面的类LittleBallImpl.java:
package springAop; public class LittleBallImpl { /* * 需要aop代理的方法 * getLittleBallName() */ public void getLittleBallName(String name){ System.out.println("hello, I am little ball!"); } }
3、编写方法执行前、执行后、执行前后的增强类PrintDate.java、PrintWeather.java、AroundPoint.java,其中PrintDate通过在切入方法入参中添加JointPoint来获取被切方法的入参:
package springAop; import java.util.Date; import org.aspectj.lang.JoinPoint; /** * 切面类 * @author qiaozhong */ public class PrintDate { /** * 织入的具体业务方法,实现业务逻辑 * JointPoint可获得被切的方法的入参 */ public void printDate(JoinPoint joinPoint){ //获取被切的方法的入参 Object[] o=joinPoint.getArgs(); System.out.println(String.valueOf(o[0]) + new Date()); } }
package springAop; public class PrintWeather { /** * 织入的具体业务方法,实现业务逻辑 */ public void printWeather(){ System.out.println("Today is sunny"); } }
package springAop; import org.aspectj.lang.ProceedingJoinPoint; public class AroundPoint { /* * 环绕通知 * */ public void aroundPrint(ProceedingJoinPoint point) throws Throwable { System.out.println("环绕通知前。。。。。"); point.proceed(); System.out.println("环绕通知后。。。。。。"); } }
4、配置切面的配置文件spring-aop.xml,要加入aop相关的约束声明:
<?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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 配置bean --> <bean id="littleBall" class="springAop.LittleBallImpl"></bean> <!-- 配置切面的bean --> <bean id="printDate" class="springAop.PrintDate"></bean> <bean id="printWeather" class="springAop.PrintWeather"></bean> <bean id="aroundPoint" class="springAop.AroundPoint"></bean> <!-- 配置AOP --> <aop:config> <!-- 配置切面和通知 order:优先级,越小优先级越高--> <aop:aspect id="date" ref="printDate" order="1"> <!-- 配置切面表达式 --> <aop:pointcut id="addDate" expression="execution(* springAop.LittleBallImpl.get*(..))" /> <!-- 配置切面方法 --> <aop:before method="printDate" pointcut-ref="addDate"/> <aop:after method="printDate" pointcut-ref="addDate"/> </aop:aspect> <!-- 配置切面和通知 order:优先级,越小优先级越高--> <aop:aspect id="weather" ref="printWeather" order="2"> <!-- 配置切面表达式 --> <aop:pointcut id="addWeather" expression="execution(* springAop.LittleBallImpl.get*(..))" /> <!-- 配置切面方法 --> <aop:before method="printWeather" pointcut-ref="addWeather"/> <aop:after method="printWeather" pointcut-ref="addWeather"/> </aop:aspect> <!-- 配置切面和通知 order:优先级,越小优先级越高--> <aop:aspect id="aroundP" ref="aroundPoint" order="3"> <!-- 配置切面表达式 --> <aop:pointcut id="aPoint" expression="execution(* springAop.LittleBallImpl.get*(..))" /> <!-- 配置切面方法 --> <aop:around method="aroundPrint" pointcut-ref="aPoint"/> </aop:aspect> </aop:config> </beans>
5、编写测试类TestAop.java:
package springAop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAop { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("springConfig/spring-aop.xml"); LittleBallImpl littleBallImpl = (LittleBallImpl)ac.getBean(LittleBallImpl.class); littleBallImpl.getLittleBallName("hello ,I am little ball! Today is "); } }
测试结果:
hello ,I am little ball! Today is Thu Oct 31 14:35:43 CST 2019 Today is sunny 环绕通知前。。。。。 hello, I am little ball! 环绕通知后。。。。。。 Today is sunny hello ,I am little ball! Today is Thu Oct 31 14:35:43 CST 2019
结果分析:
1、先执行了addDate切面方法,通过JointPoint获取了被切方法的入参“hello ,I am little ball! Today is”
2、执行了addWeather切面方法
3、执行AroundPoint切面前置方法
4、执行业务类LittleBallImpl
5、执行AroundPoint切面后置方法
6、执行了addWeather切面方法
7、执行了addDate切面方法,通过JointPoint获取了被切方法的入参“hello ,I am little ball! Today is”