一、Spring AOP 配置
首先,明白一点,Spring的AOP代理,分为JDK动态代理和Cglib动态代理,这两种代理的实现方式不一样,他们针对同一Aspect的配置效果也可能不一样。
JDK的动态代理,只能代理接口,无法代理非接口的方法。
Cglib动态代理,采用的是继承代理对象的方法,所以可以代理非private的所有方法。
Spring和AspectJ的关系:
AspectJ是Eclipse基金会的一个动态代理框架(官方网站为:http://www.eclipse.org/aspectj/),它发明了一种表达式,按照这种表达式可以很方便的配置AOP的切入点。
Spring选择模仿和遵循AspectJ定义的表达式规范,并实现了对Aspect表达式的解析,但是它只实现了AspectJ官方表达式的一部分功能,同时又扩展了一些Spring特有的功能。
Spring引用了AspectJ的核心包: aspectjweaver,其关键的底层功能应该都是调用的AspectJ的函数。
SpringMVC要开启AOP,需要配置一个标签:
<aop:aspectj-autoproxy proxy-target-class="true" />
其中proxy-target-class=true表示可以启用Cglib代理(至于什么时候使用,由Spring自己决定:判断是否基于接口代理,如果是就采用JDK动态代理,否则采用Cglib代理),如果配置proxy-target-class=false就意味着禁用Cglib代理,如果Spring发现无法代理时,就会报错。因为Cglib动态代理的效率比JDK动态代理的效率要低很多,所以如果你只代理接口,你可以设置proxy-target-class=false。
public AopProxy createAopProxy(AdvisedSupport config) { if (config.isOptimize() || config.isProxyTargetClass()) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface()) { // 基于接口 return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { // 如果配置proxy-target-class=false,直接JDK return new JdkDynamicAopProxy(config); } }
SpringBoot,默认会识别工程中是否有AOP配置,如果有,则自动启用aop,无需配置,但是默认配置是基于JDK的,如果需要用到Cglib,则如下配置:
@EnableAspectJAutoProxy(proxyTargetClass=true)
二、个人常用的AspectJ表达式介绍
1、execution(* com.zollty.**.dao.*.*(..))
其含义为:所有 com.zollty.**.dao包 下面的类(只包含一级目录,不包含下面的子目录),匹配这些类的所有 非private的方法。
这个表达式简单,但是效率低。官方不建议这么做,后面会讲,官方的建议做法。
2、within(com.zollty.**.dao.*) && execution(public * *(..))
加上了within筛选,效率要高一些。效果同(1)一样,只是后面加上了&&条件限制,只匹配public的方法。
3、within(com.zollty..BaseService+) && @annotation(anno)
基于 包路径 和 BaseService基类的匹配,并且 带有 anno 注解,anno注解在@Pointcut的方法参数里面指定,例如:
@Pointcut(@annotation(anno))
public void test(SelectDB anno) {
}
4、within(com.zollty..BaseMapper+) && !execution(* toString()) && !execution(* hashCode()) && !execution(* getClass())
和(3)类似,同时排除掉 toString等方法。
3、官方建议的切面表达式性能优化方法
However, AspectJ can only work with what it is told, and for optimal performance of matching the user should think about what they are trying to achieve and narrow the search space for matches as much as they can in the definition. Basically there are three kinds of pointcut designator: kinded, scoping and context:
Kinded designators are those which select a particular kind of join point. For example: execution, get, set, call, handler
Scoping designators are those which select a group of join points of interest (of probably many kinds). For example: within, withincode
Contextual designators are those that match (and optionally bind) based on context. For example: this, target, @annotation
A well written pointcut should try and include at least the first two types (kinded and scoping), whilst the contextual designators may be included if wishing to match based on join point context, or bind that context for use in the advice. Supplying either just a kinded designator or just a contextual designator will work but could affect weaving performance (time and memory used) due to all the extra processing and analysis. Scoping designators are very fast to match, they can very quickly dismiss groups of join points that should not be further processed - that is why a good pointcut should always include one if possible.
翻译过来,即:至少使用两者类型的匹配模式,建议使用 within 和execution的组合,或者 within 和 @annotation的组合。
四、切面表达式 参考文档:
Spring:
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-pointcuts
Aspectj官方:
http://www.eclipse.org/aspectj/doc/released/progguide/quick.html
http://www.eclipse.org/aspectj/doc/released/progguide/language-joinPoints.html
http://www.eclipse.org/aspectj/doc/released/adk15notebook/index.html
参考文章:
https://wenku.baidu.com/view/7e91a92d4b73f242336c5ff4.html
http://www.360doc.com/content/13/1212/09/14416931_336521220.shtml
https://blog.csdn.net/qq525099302/article/details/53996344