[TOC]
1. 概念
AOP(Aspect Oriented Programming,面向切面编程),可使各业务逻辑分离,降低耦合,提高复用,增加开发效率。
应用于日志记录、性能统计、安全控制、权限管理、事务处理、异常处理和资源池管理等。
2. 概念
- 切面(aspect)
关注点的模块化,本文的AopAspect
,用<aop:aspect>
配置 - 连接点(Joinpoint)
程序执行中的某个行为,<aop:pointcut id="minstrelPointCut" expression="execution(* com.cui.springShizhan.*.*(..))"/>
中的expression
定义了连接点 - 通知(Advice)
切面对于连接点产生的动作,分为5种:- 前置通知(Before advice):在连接点之前执行,配置在
<aop:before>
中; - 后置通知(After advice):在连接点之后执行,配置在
<aop:after>
中; - 返回后通知(After return advice):程序正常完成后执行,不包括抛出异常,配置杂
<after-returning>
; - 环绕通知(Around advice):包围一个连接点的通知,配置在
<aop:around>
中; - 抛出异常后通知(After throwing advice):抛出异常退出执行,配置在
<aop:after-throwing>
中。
- 前置通知(Before advice):在连接点之前执行,配置在
- 切入点(Pointcut)
匹配连接点的断言,通知(Advice)和一个切入点关联。配置在<aop:pointcut>
中,如<aop:pointcut id="savePoint" expression="execution(* com.cui.springShizhan.aop.*.save(..))"></aop:pointcut>
。
详细介绍见 第3章。 - 目标对象(Target Object)
被一个或多个切面通知的对象,实际操作的是代理对象 - AOP代理(AOP Proxy)
spring采用JDK
动态代理和CGLIB
代理,目标对象实现接口,则使用JDK
动态代理;未实现接口,则使用CGLIB
代理。
3. 切入点
3.1 匹配语法
- “ * ”:匹配任何数量字符;
- “ .. ”:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
- “ + ”:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
如:
-
java.lang.String
:匹配String类型; -
java.*.String
:匹配java包下的任何“一级子包”下的String类型;如匹配java.lang.String
,但不匹配java.lang.ss.String
。
3.java..*
:匹配java包及任何子包下的任何类型;如匹配java.lang.String
、java.lang.annotation.Annotation
。 -
java.lang.*ing
:匹配任何java.lang
包下的以ing
结尾的类型; -
java.lang.Number+
:匹配java.lang
包下的任何Number
的自类型;如匹配java.lang.Integer
,也匹配java.math.BigInteger
。
3.2 匹配逻辑
可以使用且(&&)、或(||)、非(!)来组合切入点表达式。由于在XML中使用“&&”需要使用转义字符“&&”来代替之,所以很不方便,因此Spring ASP 提供了and、or、not来代替&&、||、!。
3.3 切入点表达式
- execution:匹配方法,支持通配符
- 所有公共方法
execution(public * *(..))
- 以
set
开头的所有方法
execution(* set*(..))
-
AccountService
接口中定义的所有方法
execution(* com.xyz.service.AccountService.*(..))
-
service
包中定义的所有类的方法(不包含子包)
execution(* com.xyz.service.*.*(..))
-
service
包中定义的所有类的方法(包含子包)
execution(* com.xyz.service..*.*(..))
- within:匹配类,支持通配符
-
service
包下的所有类(不包含子类)
within(com.xyz.service.*)
-
service
包下的所有类(包含子类)
within(com.xyz.service..*)
-
- this:代理类类型匹配。不支持通配符
- 匹配所有代理类(AOP生成的代理类,包括动态代理和CGLIB代理)实现了
AccountService
接口的类
this(com.xyz.service.AccountService)
- 匹配所有代理类(AOP生成的代理类,包括动态代理和CGLIB代理)实现了
- target:目标类匹配。不支持通配符
- 匹配所有目标类实现了
AccountService
接口的类
target(com.xyz.service.AccountService)
- 匹配所有目标类实现了
- args:参数匹配和参数绑定,支持通配符
- 匹配一个参数,且为
Serializable
的方法
args(java.io.Serializable)
- 匹配
String
的参数,并传递到before
方法中
- 匹配一个参数,且为
@Before("execution(* com.cui.springShizhan.ch4.Test.test(String)) && args(testName)")
public void before(String testName) {
System.out.println("before");
System.out.println("para:" + testName);
}

- @target:目标类注解匹配,在接口中不注解不匹配。不支持通配符
-
目标类加了
@Transactional
注解
@target(org.springframework.transaction.annotation.Transactional)
-
目标类加了
- @within:申明类型注解匹配,如果声明为接口,则注解在接口生效;如果为实现类,则注解在实现类才会生效。不支持通配符。
-
声明类型加了
@Transactional
注解
@within(org.springframework.transaction.annotation.Transactional)
-
声明类型加了
- @annotation:执行方法注解匹配。不支持通配符
- 当前执行方法加了
@Transactional
注解
@annotation(org.springframework.transaction.annotation.Transactional)
- 当前执行方法加了
- @args:参数直接匹配
- 参数加了
Classified
注解
@args(com.xyz.security.Classified)
- 参数加了
- bean:
beanName
匹配- 只匹配
beanName
为tradeService
的bean
bean(tradeService)
- 只匹配
beanName
以Service
结尾的bean
bean(*Service)
- 只匹配
4. 实例
- maven
<!--spring的context上下文即IoC容器-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring aop依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring测试依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!--junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--spring aop依赖AspectJ-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
注:比如引入aspectjweaver
,否则会报错Spring AOP报错:java.lang.NoClassDefFoundError: org/aspectj/lang/JoinPoint
- 目标对象(Target Object)
接口定义:
package com.cui.springShizhan.aop;
/**
* 接口定义
*/
public interface IDateOperationService {
int save();
int query() throws Exception;
}
接口实现:
package com.cui.springShizhan.aop;
/**
* 实现接口,使用JDK动态代理,否则使用CGLIB代理
*/
public class DateOperationServiceImpl implements IDateOperationService {
@Override
public int save() {
System.out.println("----保存到数据库----");
return 1;
}
@Override
public int query() throws Exception {
System.out.println("----从数据库中得到查询结果----");
try {
int i = 1 / 0;
} catch (Exception e) {
throw new Exception();
}
return 9;
}
}
- 切面定义:
package com.cui.springShizhan.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* AOP 切面相关定义
*/
public class AopAspect {
/**
* 前置通知:在连接点之前执行
* @param joinPoint 连接点
*/
public void before(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("前置通知: " + methodName + "执行前");
}
/**
* 后置通知:在连接点之后执行
* @param joinPoint 连接点
*/
public void after(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("后置通知:" + methodName + "执行后");
}
/**
* 返回后通知:在return后执行这段逻辑,异常退出不执行
* @param joinPoint
* @param result 返回结果
*/
public void afterReturn(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("返回后通知: " + methodName + "已正常return, result: " + result);
}
/**
* 抛出异常后通知:抛出异常后执行这段逻辑
* @param joinPoint
* @param ex 异常类型
*/
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("抛出异常后通知:method: " + methodName + "抛出异常,异常为: " + ex);
}
/**
* 环绕通知:可实现前面4种通知
* @param proceedingJoinPoint
* @return
*/
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
Object result = null;
String methodName = proceedingJoinPoint.getSignature().getName();
try {
System.out.println("环绕通知 → 前置通知: " + methodName + "执行前");
result = proceedingJoinPoint.proceed();
System.out.println("环绕通知 → 返回后通知: " + methodName + "已正常return, result: " + result);
} catch (Throwable throwable) {
//异常通知
System.out.println("环绕通知 → 抛出异常后通知:method: " + methodName + "抛出异常,异常为: " + throwable);
throw new RuntimeException(throwable);
}
System.out.println("环绕通知 → 后置通知:" + methodName + "执行后");
return result;
}
}
- xml配置:
<?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" 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.xsd">
<bean id="dateOperationService" class="com.cui.springShizhan.aop.DateOperationServiceImpl"/>
<!--定义切面-->
<bean id="aopAspect" class="com.cui.springShizhan.aop.AopAspect"/>
<aop:config>
<!--id:唯一标识符,ref:切面,order:执行顺序-->
<aop:aspect id="aopTest" ref="aopAspect" order="1">
<!--定义切点-->
<aop:pointcut id="savePoint" expression="execution(* com.cui.springShizhan.aop.*.save(..))"></aop:pointcut>
<aop:pointcut id="queryPoint" expression="execution(* com.cui.springShizhan.aop.*.query(..))"/>
<aop:before method="before" pointcut-ref="savePoint"/>
<aop:after method="after" pointcut-ref="savePoint"/>
<aop:after-returning method="afterReturn" returning= "result" pointcut-ref="savePoint"/>
<aop:around method="around" pointcut-ref="savePoint"/>
<!--异常定义-->
<aop:after-throwing method="afterThrowing" pointcut-ref="queryPoint" throwing="ex"/>
<aop:around method="around" pointcut-ref="queryPoint"/>
</aop:aspect>
</aop:config>
</beans>
- 客户端:
package com.cui.springShizhan.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Client {
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:springshizhan/applicationContext-aop.xml");
IDateOperationService dateOperationService = (IDateOperationService) applicationContext.getBean("dateOperationService");
System.out.println("-------------------save开始-----------------");
dateOperationService.save();
System.out.println("-------------------save结束-----------------\n\n");
System.out.println("-------------------query开始-----------------");
dateOperationService.query();
System.out.println("-------------------query结束-----------------\n\n");
}
}
- 输出结果:
-------------------save开始-----------------
前置通知: save执行前
环绕通知 → 前置通知: save执行前
----保存到数据库----
环绕通知 → 返回后通知: save已正常return, result: 1
环绕通知 → 后置通知:save执行后
返回后通知: save已正常return, result: 1
后置通知:save执行后
-------------------save结束-----------------
-------------------query开始-----------------
环绕通知 → 前置通知: query执行前
----从数据库中得到查询结果----
环绕通知 → 抛出异常后通知:method: query抛出异常,异常为: java.lang.Exception
抛出异常后通知:method: query抛出异常,异常为: java.lang.RuntimeException: java.lang.Exception
Exception in thread "main" java.lang.RuntimeException: java.lang.Exception
at com.cui.springShizhan.aop.AopAspect.around(AopAspect.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy4.query(Unknown Source)
at com.cui.springShizhan.aop.Client.main(Client.java:15)
Caused by: java.lang.Exception
at com.cui.springShizhan.aop.DateSaveServiceImpl.query(DateSaveServiceImpl.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
at com.cui.springShizhan.aop.AopAspect.around(AopAspect.java:58)
网友评论