springAOP及一些简单的应用(一)


以下内容仅为做一个记录,以备可能会用到。

什么是AOP

AOP就是面向切面编程,它是spring框架的核心之一,开应用程序开发过程中可以解决很多问题,比如:日志记录,事务,权限等。同时,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

AOP中的相关术语

  • Aspect(切面):横切关注点的模块化,在spring中通常是一个类,里面可以定义切入点和通知。
  • JointPoint(连接点):指在程序执行过程中某个特定的点,一般是方法的调用。
  • Advice(通知):在切面的某个特定的连接点上执行的动作,在spring中有before,after,afterReturning,afterThrowing,around这五种。
  • Pointcut(切入点):其实就是带有通知的连接点,在程序中主要体现为书写切入点表达式。
  • 引入(Introduction):用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制
  • AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
  • 织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

通知类型

  • 前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
  • 后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
  • 异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
  • 最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
  • 环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行,像事务,日志等都是环绕通知。

springboot中使用AOP例子

  • 工程目录:
    在这里插入图片描述
    我们需要加入spring-boot-starter-aop启动器
  • 业务类
package com.springboot.springbootaop.service;

import org.springframework.stereotype.Service;

@Service
public class TestService {

	public void test() {
		System.out.println("this is test");
	}

}

  • 切面类
@Component
@Aspect
public class TestAspect {

	/**
	 * 切入点,这里我们直接切入到这个方法
	 */
	@Pointcut("execution(* com.springboot.springbootaop.service.TestService.test(..))")
	public void pointCut() {
	}

	/**
	 * 前置通知
	 * 
	 * @param joinPoint
	 */
	@Before("pointCut()")
	public void doBefore(JoinPoint joinPoint) {
		System.out.println("AOP 前置通知...");
	}

	/**
	 * 最终通知,不管方法执行是否异常
	 * 
	 * @param joinPoint
	 */
	@After("pointCut()")
	public void doAfter(JoinPoint joinPoint) {
		System.out.println("AOP 最终通知...");
	}

	/**
	 * 后置通知,方法正常执行返回后
	 * 
	 * @param joinPoint
	 * @param returnVal
	 */
	@AfterReturning(pointcut = "pointCut()", returning = "returnVal")
	public void afterReturn(JoinPoint joinPoint, Object returnVal) {
		System.out.println("AOP 后置通知:" + returnVal);
	}

	/**
	 * 异常通知,方法执行异常时通知
	 * 
	 * @param joinPoint
	 * @param error
	 */
	@AfterThrowing(pointcut = "pointCut()", throwing = "error")
	public void afterThrowing(JoinPoint joinPoint, Throwable error) {
		System.out.println("AOP 异常通知..." + error);
	}

	/**
	 * 环绕通知
	 * 
	 * @param pjp
	 */
	@Around("pointCut()")
	public void around(ProceedingJoinPoint pjp) {
		System.out.println("AOP 环绕通知 before...");
		try {
			pjp.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("AOP 环绕通知 after...");
	}
}

  • 测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootAopApplicationTests {
	
	@Autowired
	private TestService testService;

	@Test
	public void contextLoads() {
		this.testService.test();
	}

}
  • 结果
    没有异常时:
AOP 环绕通知 before...
AOP 前置通知...
this is test
AOP 环绕通知 after...
AOP 最终通知...
AOP 后置通知:null

有异常时:

AOP 环绕通知 before...
AOP 前置通知...
java.lang.ArithmeticException: / by zero
	at com.springboot.springbootaop.service.TestService.test(TestService.java:9)
	at com.springboot.springbootaop.service.TestService$$FastClassBySpringCGLIB$$64f0483d.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
	at com.springboot.springbootaop.aspect.TestAspect.around(TestAspect.java:76)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	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:185)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:55)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
	at com.springboot.springbootaop.service.TestService$$EnhancerBySpringCGLIB$$c0bc1172.test(<generated>)
	at com.springboot.springbootaop.SpringbootAopApplicationTests.contextLoads(SpringbootAopApplicationTests.java:20)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
	at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
AOP 环绕通知 after...
AOP 最终通知...
AOP 后置通知:null

例子分析

在上述的列子结果中我们可以分析出通知执行的优先级:

  • 无异常的情况
    进入目标方法时,先织入Around,再织入Before,退出目标方法时,先织入Around,再织入After,最后才织入AfterReturning。
    在没有异常的时候,切入点方法执行以后,除了上述例子的结果以外,也有可能先执行Around通知,再执行AfterReturning通知,再执行After通知。

  • 有异常的情况

  • 可以看到例子中有异常的情况下AfterThrowing通知没有执行,因为Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用,同时使用也没啥意义,这是只是演示而已。

切入点表达式

切入点表达式有:

  • execution - 匹配方法执行的连接点,这是你将会用到的Spring的最主要的切入点指示符。

  • within - 限定匹配特定类型的连接点(在使用Spring AOP的时候,在匹配的类型中定义的方法的执行)。

  • this - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中bean reference(Spring AOP 代理)是指定类型的实例。

  • target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中目标对象(被代理的应用对象)是指定类型的实例。

  • args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中参数是指定类型的实例。

  • @target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中正执行对象的类持有指定类型的注解

  • @args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中实际传入参数的运行时类型持有指定类型的注解。

  • @within - 限定匹配特定的连接点,其中连接点所在类型已指定注解(在使用Spring AOP的时候,所执行的方法所在类型已指定注解)

  • @annotation - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中连接点的主题持有指定的注解

具体springboot整合AOP实际操作中切入点表达式怎么用在另一篇文章中再详细叙述。


参考文章:
http://shouce.jb51.net/spring/aop.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章