Spring AOP 表达式记录

前言

项目中除了纵向的业务逻辑线外,通常我们还需要处理一些模块共用的逻辑,比如权限认证、性能统计等。这些逻辑通常是‘横向’的,贯穿多个业务模块。这时候 AOP 就派上用场了。

一、AOP 简单介绍

面向切面编程,简称 AOP(Aspect Oriented Programming)。是指将切面逻辑按一定的方式织入到特定的业务模块中,从而在执行业务逻辑的过程的同时完成切面逻辑。

AOP 术语

通知(Advice)

需要完成的切面逻辑叫做通知。

连接点(Join point)

就是spring中允许使用通知的地方,基本上就是方法(方法执行前、执行后、异常抛出等)。

切点(Poincut)

其实就是筛选出的连接点,如果说通知定义了切面的动作的话,切点则定义了执行的地点或者执行的时机。

切面(Aspect)

通知和切点的结合,通知和切点共同定义了切面的全部内容,它是干什么的,什么时候在哪执行

引入(Introduction)

在不改变一个现有类代码的情况下,为该类添加属性和方法。可以无需修改源码,让它们具有新的行为或状态。

目标(target)

被通知的业务对象。

织入(Weaving)

切面逻辑可以在多个时间点添加到目标业务逻辑:

  • 编译期:切面在目标类编译时被织入
  • 类加载期:切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码
  • 运行期:切面在应用运行的某个时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP就是以这种方式织入切面的。

AOP 切点的主要类型

类型说明
@Before切面在业务模块代码执行之前执行,其不能阻止业务模块的执行,除非抛出异常
@AfterReturning切面在业务模块代码执行之后执行
@AfterThrowing切面在业务模块抛出指定异常后执行
@After切面在所有的 Advice 执行完成后执行,无论业务模块是否抛出异常,类似于finally的作用
@Around切面包裹业务模块的执行,其可以传入一个 ProceedingJoinPoint 用于调用业务模块的代码,调用前逻辑和调用后逻辑都可以在该方法中编写,也可以根据一定的条件阻断业务模块的调用
@DeclareParents在属性声明上使用,主要用于为指定的业务模块添加新的接口和相应的实现

执行顺序
around(befor invoke) => before => around (after invoke)=> after => afterReturning

二、Spring AOP 表达式

1. 整体示例

	@Aspect
	@component
	public class MyAspect {
	  @Around("{切入点表达式}")
	  public Object around(ProceedingJoinPoint pjp) throws Throwable {
	    System.out.println("this is before around advice");
	    Object result = pjp.proceed();
	    System.out.println("this is after around advice");
	    return result;
	  }
	}

2. 声明切面类

@Aspect 注解用在类定义上,指明当前类是切面类,并且该注解中可以指定当前类是何种实例化方式,主要有三种:singleton、perthis和pertarget。

3. 切入点表达式

3.1 类型

3.1.1 execution 匹配方法

语法:[modifiers-pattern] <ret-type-pattern> [declaring-type-pattern] <name-pattern><(param-pattern)> [throws-pattern]

execution 表达式可以指定方法返回类型,类名,方法名和参数名等与方法相关的部件。Spring 中大部分需要使用 AOP 的业务场景达到方法级别即可,因此 execution 表达式的使用是最为广泛的。如下是 execution 表达式的语法:

表达式项说明(问号表示当前项可选):
表达式项说明
modifiers-pattern方法的可见性,如 public,protected
ret-type-pattern方法的返回值类型,如 int,void 等
declaring-type-pattern方法所在类的全路径名,如 com.spring.Aspect
name-pattern方法名类型,如 buisinessService()
param-pattern方法的参数类型,如 java.lang.String
throws-pattern方法抛出的异常类型,如 java.lang.Exception
示例
	//表示返回值为任意类型,com.spring.service.BusinessObject类中参数个数为零的所有方法
	execution(* com.spring.service.BusinessObject.*())

	//表示返回值为任意类型,在com.spring.service包中,以Business为开头的类中参数个数为零方法:
	execution(* com.spring.service.Business*.*())
	
 	//表示匹配返回值为任意类型,并且是com.spring.service包及其子包下的任意类的名称为businessService的方法,而且该方法不能有任何参数
	execution(* com.spring.service..*.businessService())
	
	//使用..表示任意个数的参数,需要注意,表示参数的时候可以在括号中事先指定某些类型的参数,而其余的参数则由..进行匹配:
	execution(* com.spring.service.BusinessObject.businessService(java.lang.String,..))
3.1.2 within 匹配类

语法:within(declaring-type-pattern)

within 参数为全路径的类名(可使用通配符),匹配当前表达式的所有类中的方法都将被当前方法环绕。

示例
	//匹配 aop.Apple 中的所有方法
	@Around(value = "within(aop.Apple)")
	
	//匹配 com.spring.service 包下的所有类,不包括子包中的类
	@Around(value = "within(com.spring.service.*)")
	
	//匹配 com.spring.service 包及子包下的所有类
	@Around(value = "within(com.spring.service..*)")
3.1.3 args 匹配方法参数

语法:args(param-pattern)

args 表达式的作用是匹配指定参数类型和指定参数数量的方法,需要注意的是,args 指定的参数必须是全路径的。

示例
	//匹配所有只有一个参数,并且参数类型是java.lang.String类型的方法
	args(java.lang.String)
	//匹配第一个参数为java.lang.String,最后一个参数为java.lang.Integer,并且中间可以有任意数量和类型参数的方法
	args(java.lang.String,..,java.lang.Integer)
3.1.4 this 和 target 匹配代理对象

语法:this(declaring-type-pattern)
语法:target(declaring-type-pattern)

  • this 匹配调用当前切点表达式所指代对象方法的对象
  • target匹配切点表达式指定类型的对象。

以上两种方式只有在目标对象实现接口,且 Spring 采用 JDK 动态代理时才有区别。由于 JDK 动态代理生成的代理对象与目标对象只是实现了相同的接口,而非相同类型的对象,因此不符合 this 语义。而 target 描述的就是目标对象,因此是符合 target 语义的。

示例
	@Component
	class Apple implements Fruit{...}
	// 使用 target 表达式时, 符合语义,会执行切入逻辑。
	@Around("target(aop.Apple)")
    Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("this is before around advice.");
        Object proceed = pjp.proceed(pjp.getArgs());
        System.out.println("this is after around advice.");
        return proceed;
	}

输出:
this is before around advice.
aop.Apple.desc method invoked…
this is after around advice.

	// 使用 this 表达式时, 不符合语义。
	@Around("this(aop.Apple)")
    Object around(ProceedingJoinPoint pjp) throws Throwable {...}

输出:
aop.Apple.desc method invoked…

注意:spring 2.3.4 默认使用的是 CGLib 方式代理。需要在配置文件中配置 spirng.aop.proxy-target-class = false

3.2 注解匹配

通过使用注解的类、方法或参数等实现切入点匹配

3.2.1 @within 匹配带有指定注解的类

语法:@within(annotation-type)

3.2.2 @annotation 匹配带有指定注解的方法

语法:@annotation(annotation-type)

3.2.3 @args 匹配使用注解的类作为参数的方法

语法:@args(annotation-type)

3.3 通配符的使用

表达式项中可以使用通配符实现批量匹配,类似于正则表达式的使用。

  • * 通配符,用于匹配单个单词,或者是以某个词为前缀或后缀的单词。
  • .. 通配符,表示0个或多个项,主要用于 declaring-type-patternparam-pattern
    如果用于 declaring-type-pattern 中,则表示匹配当前包及其子包,
    如果用于 param-pattern 中,则表示匹配0个或多个参数。

Introduction(引入)

@DeclareParents也称为Introduction(引入),可以动态的为指定的类引入新的属性和方法。

语法:@DeclareParents(value = “TargetType”, defaultImpl = WeaverType.class

示例

	interface IDescriber {
	    void desc();
	}

	class DescriberImpl implements IDescriber {
	    @Override
	    void desc() {
	        println "this is an introduction describer."
	    }
	}
	
	//目标类,未实现接口 IDescriber 
	@Component
	class Apple {
	
	    void eat(String msg) {
	        System.out.println("aop.Apple.eat method invoked.." + msg);
	    }
    }

	//AOP 类织入
	@Aspect
	@Component
	class MyAspect {
	    @DeclareParents(value = "aop.Apple", defaultImpl = DescriberImpl.class)
	    private IDescriber describer;	
	}
	
	@SpringBootApplication
	class AppleApp {
	
	    public static void main(String[] args) {
	
	        ConfigurableApplicationContext run = new SpringApplicationBuilder(AppleApp.class).web(WebApplicationType.NONE).run(args);
			// 获取的 apple bean 被织入了 desc 方法
	        IDescriber apple = (IDescriber) run.getBean("apple");
	        apple.desc();
	    }
	}

输出:
this is an introduction describer.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值