面向方面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP)。OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面。方面实现了跨越多种类型和对象的关注点(例如事务管理)的模块化。(这些担忧在AOP文献中通常被称为“横切”问题。)
Spring的一个关键组件是AOP框架。虽然Spring IoC容器不依赖于AOP(意味着不需要使用AOP),但AOP补充了Spring IoC以提供非常强大的中间件解决方案。
AOP概念
AOP概念和术语
-
切面(Aspect):跨越多个类别的关注点的模块化。事务管理是企业Java应用程序中横切关注点的一个很好的例子。在Spring AOP中,方面是通过使用常规类(基于模式的方法)或使用
@Aspect
注释(@AspectJ样式)注释的常规类来实现的 。 -
加入点(Join point):程序执行期间的一个点,例如执行方法或处理异常。在Spring AOP中,连接点始终表示方法执行。
-
通知(Advice):特定连接点的某个方面采取的操作。不同类型的通知包括“周围”,“之前”和“之后”通知。(通知类型将在后面讨论。)许多AOP框架(包括Spring)将通知建模为拦截器并在连接点周围维护一系列拦截器。
-
切入点(Pointcut):匹配连接点的谓词。通知与切入点表达式相关联,并在切入点匹配的任何连接点处运行(例如,执行具有特定名称的方法)。由切入点表达式匹配的连接点的概念是AOP的核心,Spring默认使用AspectJ切入点表达式语言。
-
引入(Introduction):代表类型声明其他方法或字段。Spring AOP允许向任何通知的对象引入新接口(以及相应的实现)。例如,可以使用简介使bean实现
IsModified
接口,以简化缓存。(介绍被称为AspectJ社区中的类型间声明。) -
目标对象(Target object):由一个或多个方面通知的对象。也称为“通知对象”。由于Spring AOP是使用运行时代理实现的,因此该对象始终是代理对象。
-
AOP代理(AOP proxy):由AOP框架创建的对象,用于实现方面契约(通知方法执行等)。在Spring Framework中,AOP代理是JDK动态代理或CGLIB代理。
-
编织(Weaving):将方面与其他应用程序类型或对象链接以创建通知对象。这可以在编译时(例如,使用AspectJ编译器),加载时间或在运行时完成。与其他纯Java AOP框架一样,Spring AOP在运行时执行编织。
Spring AOP包括以下类型的通知:
-
之前通知(Before advice):在连接点之前运行但无法阻止执行流程进入连接点的通知(除非它抛出异常)。
-
返回后通知(After returning advice:):在连接点正常完成后运行的通知(例如,如果方法返回而不抛出异常)。
-
抛出后通知(After throwing advice):如果方法通过抛出异常退出,则执行通知。
-
之后(最终)通知(After (finally) advice):无论连接点退出的方式(正常或异常返回),都要执行通知。
-
围绕通知(Around advice):围绕连接点的通知,例如方法调用。这是最有力的通知。around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来快速通知的方法执行。
AOP代理
Spring AOP默认使用AOP代理的标准JDK动态代理。这使得任何接口(或接口集)都可以被代理。
Spring AOP也可以使用CGLIB代理。这是代理类而不是接口所必需的。默认情况下,如果业务对象未实现接口,则使用CGLIB。由于优化的做法是编程接口而不是类,业务类通常实现一个或多个业务接口。可以 强制使用CGLIB,在那些需要建议未在接口上声明的方法或需要将代理对象作为具体类型传递给方法的情况下
AOP示例
1、新建一个目标对象类,AspectBiz.class
1 package com.test.spring.aop.schema.advice.biz; 2 3 public class AspectBiz { 4 5 public void biz() { 6 System.out.println("AspectBiz biz ...."); 7 // throw new RuntimeException("runtime exception ..."); 8 } 9 10 public void init(String bizName, int times) { 11 System.out.println("AspectBiz init: " + bizName + " --- " + times); 12 } 13 }
2、新建一个通知类,CustomAspect.class
1 package com.test.spring.aop.schema.advice; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 5 public class CustomAspect { 6 7 public void before(){ 8 System.out.println("CustomAspect before ..."); 9 } 10 11 public void afterReturning(){ 12 System.out.println("CustomAspect afterReturning ..."); 13 } 14 15 public void afterThrowing(){ 16 System.out.println("CustomAspect afterThrowing ..."); 17 } 18 19 public void after(){ 20 System.out.println("CustomAspect after ..."); 21 } 22 23 public Object around(ProceedingJoinPoint pjp) throws Throwable{ 24 25 Object obj = null; 26 System.out.println("CustomAspect around1 ..."); 27 obj = pjp.proceed(); 28 System.out.println("CustomAspect around2 ..."); 29 30 return obj; 31 } 32 33 public Object aroundInit(ProceedingJoinPoint pjp, String bizName, int times) throws Throwable{ 34 System.out.println("CustomAspect aroundInit: " + bizName + " --- " + times); 35 Object obj = null; 36 System.out.println("CustomAspect around1 ..."); 37 obj = pjp.proceed(); 38 System.out.println("CustomAspect around2 ..."); 39 40 return obj; 41 } 42 43 }
3、新建一个spring配置文件,spring-aop-schema-advice.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd 8 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" 9 > 10 11 <bean id="customAspect" class="com.test.spring.aop.schema.advice.CustomAspect"></bean> 12 13 <bean id="aspectBiz" class="com.test.spring.aop.schema.advice.biz.AspectBiz"></bean> 14 15 <aop:config> 16 <aop:aspect id="customAspectAOP" ref="customAspect" > 17 18 <aop:pointcut id="customPointCut" expression="execution(* com.test.spring.aop.schema.advice.biz.*Biz.*(..))" /> 19 <aop:before method="before" pointcut-ref="customPointCut"/> 20 <aop:after-returning method="afterReturning" pointcut-ref="customPointCut"/> 21 <aop:after-throwing method="afterThrowing" pointcut-ref="customPointCut"/> 22 <aop:after method="after" pointcut-ref="customPointCut"/> 23 <aop:around method="around" pointcut-ref="customPointCut"/> 24 <!-- 25 参数环绕通知 26 <aop:around method="aroundInit" pointcut="execution(* com.test.spring.aop.schema.advice.biz.AspectBiz.init(String, int)) and args(bizName, times)"/> 27 --> 28 <!-- 29 1、简介允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来代表这些对象 30 2、<aop:aspect>中的<aop:declare-parents>元素声明该元素用于声明所匹配的类型拥有一个新的parent(因此得名) 31 32 <aop:declare-parents 33 types-matching="com.test.spring.aop.schema.advice.biz.*(+))" 34 implement-interface="com.test.spring.aop.schema.advice.Fit" 35 default-impl="com.test.spring.aop.schema.advice.FitImpl"/> 36 --> 37 </aop:aspect> 38 39 </aop:config> 40 41 </beans>
4、新建一个测试类
1 package com.test.spring.test; 2 3 import org.springframework.context.support.ClassPathXmlApplicationContext; 4 5 import com.test.spring.aop.schema.advice.biz.AspectBiz; 6 7 8 public class TestSpringAOPAdvice { 9 10 public static void main(String[] args) { 11 12 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-aop-schema-advice.xml"); 13 AspectBiz aspectBiz = (AspectBiz) context.getBean("aspectBiz"); 14 aspectBiz.biz(); 15 16 } 17 18 }
5、运行测试结果,如下
@AspectJ支持
@AspectJ指的是将方面声明为使用注释注释的常规Java类的样式。作为AspectJ 5版本的一部分,AspectJ项目引入了@AspectJ样式 。
要在Spring配置中使用@AspectJ方面,您需要启用Spring支持,以基于@AspectJ方面配置Spring AOP,并根据这些方面是否建议自动代理bean。通过自动代理,如果Spring确定bean被一个或多个方面建议,它会自动为该bean生成一个代理来拦截方法调用,并确保根据需要执行建议。
使用Java配置启用@AspectJ支持
1 @Configuration 2 @EnableAspectJAutoProxy 3 public class AppConfig { 4 5 }
使用XML配置启用@AspectJ支持,要使用基于XML的配置启用@AspectJ支持,请使用该aop:aspectj-autoproxy
元素,如以下示例所示:
1 <aop:aspectj-autoproxy/>
使用参考: 【Spring】基于@Aspect的AOP配置