AOP(Aspect Oriented Programing)
面向切面编程采用横向抽取机制,以取代传统的纵向继承体系的重复性代码(如性能监控/事务管理/安全检查/缓存实现等)
Spring 实现AOP方式有cglib和Java动态代理,默认实现方式为Java动态代理。
AOP相关术语
- Joinpoint:连接点 指那些被拦截到的点.在Spring中,这些点指方法(因为Spring只支持方法类型的连接点).
- Pointcut:切入点 指需要(配置)被增强的Joinpoint
- Advice:通知/增强 指拦截到Joinpoint后要做的操作.通知分为前置通知/后置通知/异常通知/最终通知/环绕通知等.
- Aspect:切面 切入点和通知的结合.
- Target:目标对象 需要被代理(增强)的对象.
- Proxy:代理对象 目标对象被AOP 织入 增强/通知后,产生的对象.
- Weaving:织入 指把增强/通知应用到目标对象来创建代理对象的过程(Spring采用动态代理织入,AspectJ采用编译期织入和类装载期织入).
- Introduction: 引介 一种特殊通知,在不修改类代码的前提下,可以在运行期为类动态地添加一些Method/Field(不常用).
-
引入图中的包
- 在spring配置文件中配置
- 使用@Aspect定义一个切面类
@Aspect
public class TestAspectModel {
...
}
- 定义一个需要增强的类
@Component(value = "firstaspect")
public class Firstaspect {
public void noReturn() {
System.out.println("执行方法noReturn()");
}
public String hasReturn(String argument) {
System.out.println("执行方法hasReturn()"+argument);
return " (hasReturn方法执行返回值) "+argument;
}
public void throwException() {
System.out.println("执行方法throwException()");
throw new IllegalArgumentException("i am a runtime exception");
}
}
- 定义增强方法
增强方式有:
@Before
@AfterReturning
@AfterThrowing
@After
@Around
5.1 @Before
@Aspect
public class TestAspectModel {
@Before("execution(* com.lq.play.model.*.*(..))")
public void before() {
System.out.println("Before!");
}
}
测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:config/spring/spring-servlet.xml"})
public class ApplicationTest {
@Autowired
private Firstaspect firstaspect;
@Test
public void test() throws Exception {
firstaspect.hasReturn("");
}
}
执行结果:
Before!
执行方法hasReturn()
@Before 增强只能在目标方法执行之前织入增强,如果需要阻止目标方法执行,可通过抛出一个异常来实现。
5.2 @AfterReturning
@AfterReturning增强方法将在目标方法正常完成后被织入。
@AfterReturning注解有两个属性:
- pointcut/value:两个属性作用相同,既可以是一个已有的切入点,也可以是一个切入点表达式,当指定了pointcut属性之后,value值将被覆盖。
- returning:指定一个形参名,该形参可用于访问方法返回值。在Advice方法中可以指定形参的类型,会限制目标方法必须返回指定类型的值或者没有返回值。
在TestAspectModel 中增加一个织入方法:
@AfterReturning(returning = "returnValue",pointcut = "execution(* com.lq.play.model.*.*(..))")
//@AfterReturning(returning = "returnValue",pointcut = "myPointCut()")
public void afterReturning(JoinPoint joinPoint,Object returnValue) {
System.out.println("afterReturning!");
System.out.println("afterReturning!: 目标方法返回值:"+returnValue);
System.out.println("afterReturning! 被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
System.out.println("afterReturning! 被织入增强处理的目标方法的参数为:"+joinPoint.getArgs());
System.out.println("afterReturning! 被织入增强处理的目标对象为:" +joinPoint.getTarget());
}
执行结果:
执行方法hasReturn()
afterReturning!
afterReturning!: 目标方法返回值: (hasReturn方法执行返回值)
afterReturning! 被织入增强处理的目标方法为:hasReturn
afterReturning! 被织入增强处理的目标方法的参数为:[Ljava.lang.Object;@2a8448fa
afterReturning! 被织入增强处理的目标对象为:com.lq.play.model.Firstaspect@6f204a1a
5.3 @AfterThrowing
@AfterThrowing增强方法将在目标方法正常完成后被织入。
@AfterThrowing注解有两个属性:
- pointcut/value:两个属性作用相同,既可以是一个已有的切入点,也可以是一个切入点表达式,当指定了pointcut属性之后,value值将被覆盖。
- throwing:指定一个形参名,该形参可用于访问目标方法抛出的异常。在Advice方法中可以指定形参的类型,会限制目标方法必须抛出指定类型的异常。
向TestAspectModel增加织入方法:
@AfterThrowing(throwing = "ex",pointcut = "execution(* com.lq.play.model.*.*(..))")
public void afterThrowing(IllegalArgumentException ex) {
System.out.println("afterThrowing:"+ex);
}
向Firstaspect添加抛异常方法
public void throwException() {
System.out.println("执行方法throwException()");
throw new IllegalArgumentException("i am a runtime exception");
}
测试用例
@ContextConfiguration(locations = {"classpath:config/spring/spring-servlet.xml"})
public class ApplicationTest {
@Autowired
private Firstaspect firstaspect;
@Test
public void test() throws Exception {
// firstaspect.hasReturn("");
firstaspect.throwException();
}
}
返回结果:
执行方法throwException()
afterThrowing:java.lang.IllegalArgumentException: i am a runtime exception
java.lang.IllegalArgumentException: i am a runtime exceptionat com.lq.play.model.Firstaspect.throwException(Firstaspect.java:22)at com.lq.play.model.Firstaspect$$FastClassBySpringCGLIB$$89d307c5.invoke()at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)...
5.4 @After
@AfterReturning增强处理在目标方法成功执行后才会被织入。
@After增强处理不管目标方法成功与否,它都会被织入,和异常捕捉的finally关键字类似。
增强处理类:
```java
@Aspect
public class TestAspectModel {
@After("execution(* com.lq.play.model.*.*(..))")
public void after() {
System.out.println("After!");
}
@AfterReturning(returning = "returnValue", pointcut = "execution(* com.lq.play.model.*.*(..))")
public void afterReturning(JoinPoint joinPoint, Object returnValue) {
System.out.println("afterReturning!");
System.out.println("afterReturning!: 目标方法返回值:" + returnValue);
System.out.println("afterReturning! 被织入增强处理的目标方法为:" + joinPoint.getSignature().getName());
System.out.println("afterReturning! 被织入增强处理的目标方法的参数为:" + joinPoint.getArgs());
System.out.println("afterReturning! 被织入增强处理的目标对象为:" + joinPoint.getTarget());
}
}
测试用例
@ContextConfiguration(locations = {"classpath:config/spring/spring-servlet.xml"})
public class ApplicationTest {
@Autowired
private Firstaspect firstaspect;
@Test
public void test() throws Exception {
// firstaspect.hasReturn("");
firstaspect.throwException();
}
}
返回结果
执行方法throwException()
After!
java.lang.IllegalArgumentException: i am a runtime exception
at com.lq.play.model.Firstaspect.throwException(Firstaspect.java:22)at com.lq.play.model.Firstaspect$$FastClassBySpringCGLIB$$89d307c5.invoke()at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
执行了@After增强,没有执行@AfterReturning
5.6 @Around
这个是功能最强大的增强处理,既可以在执行方法前织入增强动作,也可以在执行目标方法之后织入增强动作。
- @Around可以决定目标方法在什么时候执行,如何执行,甚至可以组织目标方法的执行。
- @Around可以改变执行目标方法的参数值,也可以改标执行目标方法之后的返回值。
- @Around增强方法的第一个参数必须是ProceedingJoinPoint类型(至少包含一个形参),如果程序没有调用ProceedingJoinPoint 参数的proceed方法,则目标方法不会被执行。
- 调用ProceedingJoinPoint 参数的proceed方法时,可以传入一个Object[]对象作为参数,该数组中的值将被传入目标方法为执行方法的实参。
织入方法:
@Aspect
public class TestAspectModel {
@Around("execution(* com.lq.play.model.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around!");
System.out.println("around! 被织入增强处理的目标方法为:"+pjp.getSignature().getName());
System.out.println("around! 被织入增强处理的目标方法的参数为:"+Arrays.toString(pjp.getArgs()));
System.out.println("around! 被织入增强处理的目标对象为:" +pjp.getTarget());
System.out.println("around!");
Object[] args = pjp.getArgs();
if (args != null && args.length > 0) {
args[0]="around 增加的参数前缀"+args[0];
}
Object returnValue = pjp.proceed(args);
return "around 增加的执行后前缀"+(String)returnValue;
}
}
目标方法
public String hasReturn(String argument) {
System.out.println("执行方法hasReturn()"+"传入的参数值:"+argument);
return "(hasReturn方法执行返回值)"+argument;
}
测试用例:
System.out.println(firstaspect.hasReturn(""));
执行结果:
around!
around! 被织入增强处理的目标方法为:hasReturn
around! 被织入增强处理的目标方法的参数为:[Ljava.lang.Object;@533bda92
around! 被织入增强处理的目标对象为:com.lq.play.model.Firstaspect@304bb45b
around!
执行方法hasReturn()传入的参数值:around 增加的参数前缀
around 增加的执行后前缀(hasReturn方法执行返回值)around 增加的参数前缀
五个增强都加入执行结果
around!
around! 被织入增强处理的目标方法为:hasReturn
around! 被织入增强处理的目标方法的参数为:[Ljava.lang.Object;@7bd7d6d6
around! 被织入增强处理的目标对象为:com.lq.play.model.Firstaspect@43f02ef2
around!
Before!
执行方法hasReturn()传入的参数值:around 增加的参数前缀
After!
afterReturning!
afterReturning!: 目标方法返回值:around 增加的执行后前缀(hasReturn方法执行返回值)around 增加的参数前缀
afterReturning! 被织入增强处理的目标方法为:hasReturn
afterReturning! 被织入增强处理的目标方法的参数为:[Ljava.lang.Object;@5b7a7f33
afterReturning! 被织入增强处理的目标对象为:com.lq.play.model.Firstaspect@43f02ef2
执行了
- @Around
- @Before
- 目标方法
- @After
- @AfterReturning
- 访问目标方法的参数
在增强方法里第一个参数定义为JoinPoint类型(ProceedingJoinPoint 即为JoinPoint子类),JoinPoint代表了织入增强处理的连接点。JoinPoint包含如下几个方法。
- Object[] getArgs():返回目标方法执行时的参数。
- Signature getSignature():返回被增强的方法的相关信息。
- Object getTarget():返回被织入增强处理的目标对象。
- Object getThis(): 返回AOP框架为目标对象生成的代理对象。
示例如@Around的增强方法:
@Around("execution(* com.lq.play.model.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around!");
System.out.println("around! 被织入增强处理的目标方法为:" + pjp.getSignature().getName());
System.out.println("around! 被织入增强处理的目标方法的参数为:" + Arrays.toString(pjp.getArgs()));
System.out.println("around! 被织入增强处理的目标对象为:" + pjp.getTarget());
System.out.println("around!");
Object[] args = pjp.getArgs();
if (args != null && args.length > 0) {
args[0] = "around 增加的参数前缀" + args[0];
}
Object returnValue = pjp.proceed(args);
return "around 增加的执行后前缀" + (String) returnValue;
}
可以指定织入优先级
- 让切面类实现org.springframework.core.Ordered接口,实现该接口值需要实现一个int getOrder()方法,该方法返回值越小,则优先级越高。
- 直接使用@Order注解来修饰一个切面类,指定一个int类型的value属性,该方法返回值越小,则优先级越高。
可以为表达式指定参数,如下:
@AfterReturning(returning = "returnValue", pointcut = "execution(* com.lq.play.model.*.*(..))&&args(arg1)")
//@AfterReturning(returning = "returnValue",pointcut = "myPointCut()")
public void afterReturning(JoinPoint joinPoint, Object returnValue,String arg1) {}
- 定义切入点
当多个表达式相同时,可以定义一个
@Pointcut("execution(* com.lq.play.model.*.*(..))")
public void myPointCut() {
}
然后其它引入即可
@AfterReturning(returning = "returnValue",pointcut = "myPointCut()")
- 也可以使用xml配置
id定义改切面的标识名
ref用于将ref属性所引用的普通Bean转换为切面Bean.
order:指定该切面Bean的优先级,值越小,优先级越高。
这几个元素都不支持子元素,但有如下属性:
- pointcut 织入点表达式
- pointcut-ref 和pointcut只能有一个,指定一个pointcut
- method 指定将该方法转为增强处理方法
- throwing 指定抛出的异常参数
- returning 目标方法返回值