2、Spring-AOP
1、面向切面编程AOP
AOP:解耦,一组类共享相同的行为
Spring只支持方法拦截的AOP,即拦截发最小粒度为类中方法
术语:
切面Aspect:在一个什么样的环境中工作,动态代理中可以理解为一个拦截器
通知Advice:切面的方法,类比拦截器中的方法
前置通知before
后置通知after
返回通知afterReturning
异常通知afterThrowing
环绕通知around
引入Introduction:在现有的类里添加自定义类和方法
切点Pointcut:限定什么时候启动拦截并织入对应的流程中
连接点join point:具体要拦截的东西,可以通过切点的正则表达式去判断哪些方法是连接点
织入Weaving:生成代理对象并将切面内容放入到流程中的过程
Spring是以JDK的CGLIB动态代理来生成代理对象
2、AspectJ注解式切面编程
基于注解拦截
基于方法规则拦截
3、AspectJ注解
@EnableAspectJAutoProxy:开启Spring对AspectJ的支持,注意,该注解是放在配置类上的
@Aspect:声明是一个切面
@PointCut:声明切点
通知类型:
@Before:前置通知,被代理对象的方法前调用
@After:后置通知,被代理对象的方法后调用
@AfterReturning:返回通知,要求被代理对象的方法执行过程中没有发生异常,方法正常返回后调用
@AfterThrowing:异常通知,要求被代理对象的方法执行过程中产生异常,方法抛出异常后调用
@Around:环绕通知
4、切点定义
常用的基于方法规则的拦截
@Pointcut("execution(* com.ning.ch1.aop.service.FunService.printInputString(..))")
public void methodPointCut() {
};
execution:限定匹配带有指定注解的连接点
*:表示任意返回类型的方法,注意,* 后必须有一个空格
com.ning.xxx...printInputString:被拦截的方法
连接点的定义很灵活,最小粒度为类,也可以以类、包维度拦截,通过正则表达式可以灵活定义
printInputString:被拦截的方法
(..):任意的参数
5、多个切面
一般情况下,多个切面调用时无序的,也可以指定调用顺序
6、环绕通知及给通知传递参数
1 import org.aspectj.lang.ProceedingJoinPoint; 2 import org.aspectj.lang.annotation.Around; 3 import org.aspectj.lang.annotation.Aspect; 4 import org.aspectj.lang.annotation.Before; 5 import org.aspectj.lang.annotation.Pointcut; 6 import org.springframework.stereotype.Component; 7 8 @Aspect 9 @Component 10 public class AroundAspect { 11 @Pointcut("execution(* com.ning.ch1.aop.service.DemoAroundService.printInputString(..))") 12 public void aroundPointCut() { 13 }; 14 15 // beforePointCut入参名称要和切点定义中args的参数名称保持一致 16 @Pointcut("execution(* com.ning.ch1.aop.service.DemoAroundService.printInputString02(..)) && args(strff)") 17 public void beforePointCut(String strff) { 18 }; 19 20 @Around("aroundPointCut()") 21 public Object around(ProceedingJoinPoint pjp) { 22 System.out.println("AroundAspect around() before..."); 23 // 如果没有返回值,则printInputString()执行也没有返回值 24 Object proceed = null; 25 try { 26 proceed = pjp.proceed(); 27 } catch (Throwable e) { 28 e.printStackTrace(); 29 } 30 System.out.println("AroundAspect around() after..."); 31 return proceed; 32 } 33 34 // 没必要和beforePointCut参数名称保持一致,此处只要上下一致即可 35 @Before("beforePointCut(strddd)") 36 public void before(String strddd) { 37 // 此处的str获取的是printInputString02()方法的入参实际值 38 System.out.println(strddd); 39 System.out.println("AroundAspect before()"); 40 } 41 }