1 概述
介绍完Spring AOP所具备的功能特性,接下来,看一下再应用程序中使用AOP时应该遵循哪些最佳实践。
2 活用切点表达式
Spring AOP的一大特色在于在开发人员提供了非常灵活的切点机制。Spring在编译期间处理切入点,并尝试进行优化匹配。然后,检查代码中的匹配规则将是一个代价高昂的过程。因此,为了获得最佳性能,需要仔细考虑想要实现的目标,并尽可能缩小搜索或匹配条件的范围。在前面已经看到过一个切点表达式,如下:
@Pointcut("execution(* com.jay.aop.service.AccountService.doAccountTransaction(..))")
public void doAccountTransaction(){
}
在这里的execution()代表的就是表达式的主体,它的基本语法如下,其中“?”部分表示可选项。
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(paramPattern)throws-pattern?)
这个表达式看似复杂,但是逐步分解所有的模式,它们其实就是描述了一个方法的特征。
modifiers-pattern:表示方法的修饰符
ret-type-pattern:表示方法的返回值
declaring-type-pattern:表示方法所在的类的路径
name-pattern:表示方法名
param-pattern:表示方法的参数
throws-pattern:表示方法抛出异常。
这些模式的作用就是完成切点的匹配。在各个模式中,可以使用"*"来表示匹配所有选项。Spring AOP还为开发人员提供了一组非常有用的描述符来简化切点表达式的使用过程。例如,args描述符表示方法的参数属于一个特定的类;within描述符表示方法属于一个特定类;target描述符表示方法所属的类等。
为了获得良好的性能,在设计切点表达式时,至少应该包含方法和类型模式。这并不是说如果只使用方法或类型模式中的一种,匹配就会不生效,而是因为类型模式的匹配过程非常快,它通过快速选择无法进一步处理的连接点来缩小搜索空间。同时,建议在空方法上声明切点,并通过空方法引用这些切点。
另一种最佳实践在于尽量声明小的切点,并把它们组合起来构建复杂的切点。下面代码展示了定义小切点并将它们连接起来的代码实例。
@Pointcut("execution(public * *(..))")
private void anyPublicMethod(){}
@Pointcut("execution(* com.jay.aopproxy.service.impl.AccountServiceImpl.doAccountTransaction(..))")
public void doSomething(){}
@Pointcut("anyPublicMethod() && doAccountTransaction()")
private void transactionOperation(){}
这里的transactionOperation()就是由anyPublicMethod()和doAccountTransaction()这两个切点组合而成的。
3 Spring AOP面试题分析
3.1 Spring AOP是基于什么技术体系实现的?
解答:Spring AOP的实现依赖代理机制。代理机制在具体实现上一般有两种方式,一种是静态代理机制,一种是动态代理机制。Spring AOP基于动态代理模式提供了面向切面机制。
3.2 Spring AOP提供了那些类型的通知机制?
解答:Spring AOP的通知机制类型非常丰富,开发人员可以在方法执行之前、之后、前后、返回以及抛出异常时实现各种自定义的通知逻辑。而且,Spring AOP实现通知的方式使用注解来标记,这些注解包括@Before、@After、@Around、@AfterReturning和@AfterThrowing等,分别对应于方法执行的各个阶段。
3.3 Spring AOP使用了哪几种动态代理机制?性能上哪种更优?
解答:常见的动态代理实现技术包括JDK自带的代理类、第三方的CGLIB和javassist。Spring AOP使用的是JDK和CGLIB。从性能上讲,JDK动态代理效率高于CGLIB。
3.4 如果想要在一个类的内部方法实现AOP,有什么办法?
解答:关于Spring AOP有一点需要注意,只能在方法的调用过程中嵌入通知机制。这是一个很重要的限制,会导致对同一个类中的内部方法无法有效地实施动态代理。这是用可以使用Spring AOP提供的AopContext上下文对象来获取当前的AOP代理。AopContext是一个非常有用的工具类,想要获取该类,需要确保ProxyFactoryBean的exposeProxy属性被设置为true。
3.5 如果想要基于Spring AOP实现对切点的精细化管理,有什么策略?
解答:Spring AOP创建切点的方式是非常灵活的,Spring专门提供了一个execution()配置方法。可以根据方法的修饰符、返回值、类路径、方法名、方法参数以及异常信息来设置方法调用与切点的匹配规则。这是实现精细化管理的一个维度。
第二个精细化管理的维度是设置切点的粒度。可以尽量声明小的切点,并把它们组合起来构建复杂的切点。这样,切点之间就具备了灵活的可重用性以及组合性。