AspectJ语法(二)

回顾

上节写了一个简单的测试用例,look下代码

@Aspect
public class CoreAspetJ {
    @After("call(* com.mmvoice.aspectjdemo.MainActivity.showMsg(java.lang.String))")
    public void showTaost(JoinPoint joinPoint) {
        Log.d("wyz", "AspectJ:牛B个锤子");
    }
}

解释

讲解下@Aspect注解使用在类上,代表这个类是注入的类

  • @After作用于方法(Advice),也就是具体的插入点
  • call处理Join Point的类型(其实是代码需要注入的地方)
  • (* com.mmvoice.aspectjdemo.MainActivity.showMsg(java.lang.String))这个是最重要的表达式,第一个『』表示返回值,『』表示返回值为任意类型,后面这个就是典型的包名路径,其中可以包含『』来进行通配,几个『』没区别。同时,这里可以通过『&&、||、!』来进行条件组合。()代表这个方法的参数,你可以指定类型,例如android.os.Bundle,或者(…)这样来代表任意类型、任意个数的参数。
    这里还有一些匹配规则,可以作为示例来进行讲解:
表达式含义
java.lang.String代表匹配String类型
java.*.String匹配java包下的任何“一级子包”下的String类型,如匹配java.lang.String,但不匹配java.lang.ss.String
java…*匹配java包及任何子包下的任何类型,如匹配java.lang.String、java.lang.annotation.Annotation
java.lang.*ing匹配任何java.lang包下的以ing结尾的类型
java.lang.Number+匹配java.lang包下的任何Number的自类型,如匹配java.lang.Integer,也匹配java.math.BigInteger
参数含义
()表示方法没有任何参数
(…)表示方法任意类型,任意个数参数
(…,java.lang.String)表示方法任意类型,任意个数参数,但是结尾必须是java.lang.String类型
(java.lang.String,…)表示方法任意类型,任意个数参数,但是开头必须是java.lang.String类型
(*,java.lang.String)表示只有2个参数,第一个参数任意类型,第二个必须是java.lang.String

总结下

  1. * 代表占1个位置,任意类型
  2. 代表任意个数任意类型
  3. + 代表子类

Advice

Alt
我们的代码中使用了==@Before==,代表注入点是方法前哦!注意@Before和@After不能一起使用哦,如果要一起使用请用@Around替换

间接选择Join Point

间接选择Join Point说明
within(TypePattern)符合 TypePattern 的代码中的 Join Point
withincode(MethodPattern)在某些方法中的 Join Point
withincode(ConstructorPattern)在某些构造函数中的 Join Point
cflow(Pointcut)Pointcut 选择出的切入点 P 的控制流中的所有 Join Point,包括 P 本身
cflowbelow(Pointcut)Pointcut 选择出的切入点 P 的控制流中的所有 Join Point,不包括 P 本身
this(Type or Id)Join Point 所属的 this 对象是否 instanceOf Type 或者 Id 的类型
target(Type or Id)Join Point 所在的对象(例如 call 或 execution 操作符应用的对象)是否 instanceOf Type 或者 Id 的类型
args(Type or Id, …)方法或构造函数参数的类型
if(BooleanExpression)满足表达式的 Join Point,表达式只能使用静态属性、Pointcuts 或 Advice 暴露的参数、thisJoinPoint 对象

直接选择Join Point

Alt

理解

call和execution

  @After("call(* com.mmvoice.aspectjdemo.MainActivity.showMsg(java.lang.String))")

call的意思说,在所有调用MainActivity的showMsg的时候调用,注意我这里说的是调用哦
execution的意思是说,直接方法内部的前或者后添加

@After丶@Before

插入代码前和后的区别,当然如果是call,就是在调用前插入,同理execution是在方法内部的签名

    public void showMsg(String msg){
        Log.d("wyz", msg);
    }
    // 下面是AspectJ代码,call是调用的前面插入哦
 	@After("call(* com.mmvoice.aspectjdemo.MainActivity.showMsg(java.lang.String))")
    public void showTaost(JoinPoint joinPoint) {
        Log.d("wyz", "AspectJ:牛B个锤子");
    }

如果调用 showMsg(“我好牛B哦”);
输出顺序是:我好牛币哦丶AspectJ:牛B个锤子

    public void showMsg(String msg){
        Log.d("wyz", msg);
    }
    // 下面是AspectJ代码,call是在调用的后面插入哦
 	@Before("call(* com.mmvoice.aspectjdemo.MainActivity.showMsg(java.lang.String))")
    public void showTaost(JoinPoint joinPoint) {
        Log.d("wyz", "AspectJ:牛B个锤子");
    }

输出顺序是:AspectJ:牛B个锤子丶我好牛币哦

withincode

我的理解是编写条件的一个操作符吧
例如,上门的showMsg方法在2个地方调用了,一个是onCreate和onResume都调用了showMsg方法,但是我只想对onCreate方法拦截呢?

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        showMsg("我是onCreate的好牛B");
    }

    @Override
    protected void onResume() {
        super.onResume();
        showMsg("我是onResume的好牛B");
    }
    public void showMsg(String msg){
        Log.d("wyz", msg);
    }
}

AspectJ代码

    @Before("call(* com.mmvoice.aspectjdemo.MainActivity.showMsg(java.lang.String))" +
            " && " +
            "withincode(* com.mmvoice.aspectjdemo.MainActivity.onCreate(..))")
    public void showTaost(JoinPoint joinPoint) {
        Log.d("wyz", "AspectJ:牛B个锤子");
    }

运行结果,可以看到只注入了onCreate方法
在这里插入图片描述

within丶this

这个和withincode有啥区别呢?我的个人理解是withincode是作用于方法,而within的范围要光一个写作用于类
,例如

public class ToastUtil {
    private ToastUtil(){
        if (SingletonHolder.instance != null) {
            throw new IllegalStateException();
        }
    }
    private static class SingletonHolder {
        private static ToastUtil instance = new ToastUtil();
    }

    public static ToastUtil getInstance(){
        return SingletonHolder.instance;
    }
    public void shwoToast(Context context, String msg){
        Toast.makeText(context,msg,Toast.LENGTH_LONG).show();
    }
}
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ToastUtil.getInstance().shwoToast(getApplicationContext(),getClass().getSimpleName());
        showMsg("我是onCreate的好牛B");
    }
}
public class Main2Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        ToastUtil.getInstance().shwoToast(getApplicationContext(),getClass().getSimpleName());
    }
}

可以看到ToastUtil有2处调用了,那如果我只想注入MainActivity的呢?

    @After("call(* com.mmvoice.aspectjdemo.ToastUtil.showToast(..))" +
            " && " +
            "!within(com.mmvoice.aspectjdemo.Main2Activity)")
    public void hookToastUtil(JoinPoint joinPoint) {
        Log.d("wyz", "AspectJ:ToastUtil调用了:" + joinPoint.getArgs()[1]);
    }

这里使用within,这里我使用了1个!,java中非的意思,所以语法是支持,&& ,||,!
这里补充下this,上面也解释的很清楚,代码如下

public class Main2Activity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        ToastUtil.getInstance().showToast(getApplicationContext(),getClass().getSimpleName());
        NeiClass.call(getApplicationContext());
    }
    public static class NeiClass{
        public static void call(Context context){
            ToastUtil.getInstance().showToast(context,NeiClass.class.getSimpleName());
        }
    }
}

如果使用的是within,Main2Activity的内部类,也不会注入的,但是使用this就能注入哦

@Around

例如我想在一个方法调用前做操作,调用后做操作?最简单的,统计MainActivity的所有方法的调用时间,有认识用@Before+@After不就行了吗?不行哦,前面说过这2个不能同时作用于一个方法哦

    @Around("execution(* com.mmvoice.aspectjdemo.MainActivity.*(..))")
    public Object statisticsTime(ProceedingJoinPoint joinPoint) {
        Object proceed = null;
        long start = System.currentTimeMillis();
        try {
             proceed = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        long end = System.currentTimeMillis();
        Log.d("wyz", joinPoint.getSignature().getName() + ":执行时间:" + (end - start));
        return proceed;
    }

这里注意一个地方哦,关于使用@Around有没有返回值,最好都写一下返回,我一开始就没写返回,一直报错,花了好长时间,以后只要使用@Around就都写返回,绝对不会报错

args

args是用于方法参数的描述,例如args(int,…),方法参数为int开头的,举个例子代码

    @Pointcut("execution(* com.mmvoice.aspectjdemo.MainActivity.testParameter(..)) && args(data,value)")
    public void getParameter(String data, int value) {
    }

上门定义了一个Pointcut,看条件的意思是com.mmvoice.aspectjdemo.MainActivity的testParameter方法且参数第一个为String类型,第二个为int类型,注意args里面参数,要和下面方法参数昵称必须一致

我们可以这样引用

   @Before("getParameter(data,value)")
    public void hookParameter(JoinPoint joinPoint, String data, int value) {
        Log.d("wyz", "hook:" + data + "  " + value);
    }

总结

方法的先讲到这里,我也理解了这些,后来慢慢补充

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值