AspectJ的基本用法
aop虽然是方法论,但就好像oop中的java一样,一些先行者也开发了一套语言来支持aop。目前用得比较火的就是aspectj了,它是一种几乎和java完全一样的语言,而且完全兼容java(aspectj应该就是一种扩展java,但它不是像groovy[1]那样的拓展。)。当然,除了使用aspectj特殊的语言外,aspectj还支持原生的java,只要加上对应的aspectj注解就好。所以,使用aspectj有两种方法:
完全使用aspectj的语言。这语言一点也不难,和java几乎一样,也能在aspectj中调用java的任何类库。aspectj只是多了一些关键词罢了。
或者使用纯java语言开发,然后使用aspectj注解,简称*@aspectj*。
aspectj的配置可以参考另一篇文章android中使用aspectj详解
join points介绍
join points是aspectj中的一个关键概念。join points可以看作是程序运行时的一个执行点,比如:一个函数的调用可以看作是个join points,如log.e()这个函数,e()可以看作是个join points,而调运e()的函数也可以认为是一个join points;设置一个变量,或者读取一个变量也可以是个join points;for循环也可以看作是join points。
理论上说,一个程序中很多地方都可以被看做是join points,但是aspectj中,只有下面所示的几种执行点被认为是join points:
join points | 说明 | 示例 |
---|---|---|
method call | 函数调用 | 比如调用log.e(),这是一处jpoint |
method execution | 函数执行 | 比如log.e()的执行内部,是一处join points。注意它和method call的区别。method call是调用某个函数的地方。而execution是某个函数执行的内部。 |
constructor call | 构造函数调用 | 和method call类似 |
constructor execution | 构造函数执行 | 和method execution类似 |
field get | 获取某个变量 | 比如读取demoactivity.debug成员 |
field set | 设置某个变量 | 比如设置demoactivity.debug成员 |
pre-initialization | object在构造函数中做得一些工作。 | |
initialization | object在构造函数中做得工作 | |
static initialization | 类初始化 | 比如类的static{} |
handler | 异常处理 | 比如try catch(xxx)中,对应catch内的执行 |
advice execution | 这个是aspectj的内容,稍后再说 |
这里列出了aspectj所认可的joinpoints的类型。实际上,也就是你想把新的代码插在程序的哪个地方,是插在构造方法中,还是插在某个方法调用前,或者是插在某个方法中,这个地方就是join points,当然,不是所有地方都能给你插的,只有能插的地方,才叫join points。
pointcuts介绍
一个程序会有多个join points,即使同一个函数,也还分为call和execution类型的join points,但并不是所有的join points都是我们关心的,pointcuts就是提供一种使得开发者能够选择自己需要的joinpoints的方法。
advice
advice就是我们插入的代码以何种方式插入,有before还有after、around。
看个例子
@before("execution(* android.app.activity.on**(..))") public void onactivitymethodbefore(joinpoint joinpoint) throws throwable { }
这里会分成几个部分,我们依次来看:
- @before:advice,也就是具体的插入点
- execution:处理join point的类型,例如call、execution
- (* android.app.activity.on**(..)):这个是最重要的表达式,第一个*表示返回值,*表示返回值为任意类型,后面这个就是典型的包名路径,其中可以包含 * 来进行通配,几个 * 没区别。同时,这里可以通过&&、||、!来进行条件组合。()代表这个方法的参数,你可以指定类型,例如android.os.bundle,或者(..)这样来代表任意类型、任意个数的参数。
- public void onactivitymethodbefore:实际切入的代码。
before和after其实还是很好理解的,也就是在pointcuts之前和之后,插入代码,那么around呢,从字面含义上来讲,也就是在方法前后各插入代码,是的,他包含了before和after的全部功能,代码如下:
@around("execution(* com.xys.aspectjxdemo.mainactivity.testaop())") public void onactivitymethodaround(proceedingjoinpoint proceedingjoinpoint) throws throwable { string key = proceedingjoinpoint.getsignature().tostring(); log.d(tag, "onactivitymethodaroundfirst: " + key); proceedingjoinpoint.proceed(); log.d(tag, "onactivitymethodaroundsecond: " + key); }
其中,proceedingjoinpoint.proceed()代表执行原始的方法,在这之前、之后,都可以进行各种逻辑处理。
自定义pointcuts
自定义pointcuts可以让我们更加精确的切入一个或多个指定的切入点。
首先我们要定义一个注解类
@retention(retentionpolicy.class) @target({elementtype.constructor, elementtype.method}) public @interface debugtrace { }
在需要插入代码的地方加入这个注解。如在mainactivity中加入,
public class mainactivity extends appcompatactivity { final string tag = mainactivity.class.getsimplename(); @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); logtest(); } @debugtrace public void logtest() { log.e(tag, "log test"); } }
最后,创建切入代码
@pointcut("execution(@com.kun.aspectjtest.aspect.debugtrace * *..*.*(..))") public void debugtracemethod() {} @before("debugtracemethod()") public void beforedebugtracemethod(joinpoint joinpoint) throws throwable { string key = joinpoint.getsignature().tostring(); log.e(tag, "beforedebugtracemethod: " + key); }
log如下
在aspectj的切入点表达式中,我们前面都是使用的execution,实际上,还有一种类型——call,那么这两种语法有什么区别呢,对于call来说:
call(before) pointcut{ pointcut method } call(after)
对于execution来说:
pointcut{ execution(before) pointcut method execution(after) }
withincode
这个语法通常来进行一些切入点条件的过滤,作更加精确的切入控制。如下
public class mainactivity extends appcompatactivity { final string tag = mainactivity.class.getsimplename(); @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); aspectj1(); aspectj2(); aspectj3(); } public void aspectjtest() { log.e(tag, "execute aspectjtest"); } public void aspectj1(){ aspectjtest(); } public void aspectj2(){ aspectjtest(); } public void aspectj3(){ aspectjtest(); } }
aspectj1(),aspectj2(),aspectj3()都调用了aspectjtest方法,但只想在aspectj2调用aspectjtest时插入代码,这个时候就需要使用到pointcut和withincode组合的方式,来精确定位切入点。
@pointcut("(call(* *..aspectjtest()))&&withincode(* *..aspectj2())") public void invokeaspectjtestinaspectj2() { } @before("invokeaspectjtestinaspectj2()") public void beforeinvokeaspectjtestinaspectj2(joinpoint joinpoint) throws throwable { log.e(tag, "method:" + getmethodname(joinpoint).getname()); } private methodsignature getmethodname(joinpoint joinpoint) { if (joinpoint == null) return null; return (methodsignature) joinpoint.getsignature(); }
log如下
04-02 23:44:40.681 12107-12107/ e/mainactivity: execute aspectjtest 04-02 23:44:40.681 12107-12107/ e/aspecttest: method:aspectjtest 04-02 23:44:40.681 12107-12107/ e/mainactivity: execute aspectjtest 04-02 23:44:40.681 12107-12107/ e/mainactivity: execute aspectjtest
以上就是aspecj的基本使用方法,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!
上一篇: Java编程兵书 博客分类: 计算机图书
下一篇: Java计算文本MD5加密值的方法示例