Android中AOP实践之三AspectJ解析篇

介绍

AspectJ是Java的一个简单实用的面向方面的扩展。通过几个新的构造,AspectJ提供了对一系列横切关注的模块化实现的支持。

在现有的Java开发项目中采用AspectJ可能是一个简单而且增量的任务。一条路径是从开发方面开始,继续使用生产方面,然后在使用AspectJ建立经验之后再使用方面。采用也可以遵循其他途径。例如,一些开发人员将从马上使用生产方面受益。其他人可能几乎可以立即编写干净的可重用方面。

AspectJ支持基于名称和基于属性的横切。使用基于名称的横切的方面倾向于影响少数其他类。但是,尽管规模较小,但与普通的Java实现相比,它们通常可以消除显着的复杂性。使用基于属性的横切的方面可以具有小规模或大规模。

使用AspectJ会导致横切关注的干净模块化的实现。当作为AspectJ方面编写时,横切关注的结构是明确的且易于理解的。方面也是高度模块化的,使得开发横切功能的即插即用实现成为可能。

基础知识

Join point 连接点

是指程序中可能作为代码注入目标的特定的点,例如一个方法调用或者方法入口。
程序中连接点有很多,下面做一个表格一一指出:

Join Points定义解释
method call调用方法一般在执行某个方法前会先调用该方法
method execution执行方法这个点是已经执行到了方法的内部
constructor call调用构造方法同调用方法
constructor execution执行构造方法同执行方法
field get获取参数比如获取某个变量的值,get()
field set设置参数比如设置某个变量的值,int num = 3
pre-initialization预初始化在第一次初始化前会预初始化
initialization初始化初始化类的时候会执行
static initialization静态初始化静态块或静态类初始化的时候会执行
handler异常处理
advice execution通知执行

//这里可以用一个例子来演示一下所有的连接点

Pointcut 切入点

一个程序中会有很多JPoint连接点,但不一定我们都要去关注。那么我们可以选择我们需要的点来作为切入点。

我们利用Pointcut的功能来筛选出对我们有用的点作为切入,pointcut有一套专门的语法,只要搞懂他后面就不愁了。

一个例子

    @Pointcut("within(@com.jie.aoptest.aop.DebugLog *)")
    public void withinAnnotatedClass() {}

    @Pointcut("execution(!synthetic * *(..)) && withinAnnotatedClass()")
    public void methodInsideAnnotatedType() {}

    @Pointcut("execution(@com.jie.aoptest.aop.DebugLog * *(..)) || methodInsideAnnotatedType()")
    public void method() {}

这个例子表明切入点在DebugLog类中所有执行方法的点,这里用到了within和execution两个指示符,within用于匹配指定类型内的方法执行,而exection则用于匹配方法执行的连接点。有synthetic标记的field和method是class内部使用的,正常的源代码里不会出现synthetic field。

切入点指示符

常用指示符

指示符说明
execution用于匹配方法执行的连接点
within用于匹配指定类型内的方法执行
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配
target用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配
args用于匹配当前执行的方法传入的参数为指定类型的执行方法
@within用于匹配所以持有指定注解类型内的方法
@target用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解
@args用于匹配当前执行的方法传入的参数持有指定注解的执行
@annotation用于匹配当前执行方法持有指定注解的方法

AspectJ切入点支持的切入点指示符还有: call、get、set、preinitialization、staticinitialization、initialization、handler、adviceexecution、withincode、cflow、cflowbelow、if、@this、@withincode,感兴趣的可以了解,就不一一说明了。

类型匹配语法

先来看一下AspectJ类型匹配的通配符
  1. *:匹配任何数量字符;
  2. ..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
  3. +:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
接下来看一下具体匹配表达式类型
  1. 注解:可选,方法上持有的注解,如@Deprecated;
  2. 修饰符:可选,如public、protected;
  3. 返回值类型:必填,可以是任何类型模式;“*”表示所有类型;
  4. 类型声明:可选,可以是任何类型模式;
  5. 方法名:必填,可以使用“*”进行模式匹配;
  6. 参数列表:“()”表示方法没有任何参数;“(..)”表示匹配接受任意个参数的方法,“(..,java.lang.String)”表示匹配接受java.lang.String类型的参数结束,且其前边可以接受有任意个参数的方法;“(java.lang.String,..)” 表示匹配接受java.lang.String类型的参数开始,且其后边可以接受任意个参数的方法;“(*,java.lang.String)” 表示匹配接受java.lang.String类型的参数结束,且其前边接受有一个任意类型参数的方法;
  7. 异常列表:可选,以“throws 异常全限定名列表”声明,异常全限定名列表如有多个以“,”分割,如throws java.lang.IllegalArgumentException, java.lang.ArrayIndexOutOfBoundsException。

组合切入点表达式

AspectJ使用 且(&&)、或(||)、非(!)来组合切入点表达式。

常用场景举例

Advice通知参数

前面已经介绍了Join point 连接点和 Pointcut 切入点,如果基本掌握了的话那么恭喜你内功已经修炼7成了。

我们成功设置好切入点后需要获取通知来执行要切入的代码片段,这里的通知相当于钩子/回调方法,在程序执行到JPoint时候会调起通知,接下来就介绍一下获取通知的方式。

Advice通知有三种类型

类型说明
before()是指在JPoint之前可以执行一些操作
after()是指在JPoint之后可以执行一些操作
around()环绕JPoint执行操作,它包含了前后两个过程,使用这种类型需要手动调用procees方法来执行原操作

来看一个例子

我们来用检测是否登录做一个例子
这个是检查登录的注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface CheckLogin {
}

先用before和after两个类型来做一个测试

    @Pointcut("execution(@com.jie.aoptest.aop.CheckLogin * *(..))")
    public void methodAnnotated() {
    }

    @Before("methodAnnotated()")
    public void beforeMethod(ProceedingJoinPoint joinPoint) {
        Log.d("aspect", "beforeMethod");
        Log.d("login", "请您登录");
        Toast.makeText(App.getAppContext().getCurActivity(), "请您登录", Toast.LENGTH_SHORT).show();
    }

    @After("methodAnnotated()")
    public void afterMethod(ProceedingJoinPoint joinPoint) {
        Log.d("aspect", "afterMethod");
    }

来看一下打印日志是这样的

11-12 06:30:52.476 10388-10388/com.jie.aoptest D/aspect: beforeMethod
11-12 06:30:52.477 10388-10388/com.jie.aoptest D/login: 请您登录
11-12 06:30:52.486 10388-10388/com.jie.aoptest D/aspect: afterMethod

然后我们用around做一个测试

    @Pointcut("execution(@com.jie.aoptest.aop.CheckLogin * *(..))")
    public void methodAnnotated() {
    }

    @Around("methodAnnotated()")
    public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        Log.d("aspect", "aroundMethod");
        Log.d("login", "请您登录");
        Toast.makeText(App.getAppContext().getCurActivity(), "请您登录", Toast.LENGTH_SHORT).show();
        joinPoint.proceed();
        Log.d("aspect", "aroundMethod");
    }

打印日志是这样的

11-12 06:35:09.696 10512-10512/com.jie.aoptest D/aspect: aroundMethod
11-12 06:35:09.696 10512-10512/com.jie.aoptest D/login: 请您登录
11-12 06:35:09.700 10512-10512/com.jie.aoptest D/aspect: aroundMethod

两种方式都可以,但要注意一点around和after两种类型是有冲突的,around和before可以共存,所以还是建议两种方式,一种before和after配合使用,一种around单独使用。

参数的获取

方法参数的获取

方法参数的获取很简单,可以通过joinPoint.getArgs()来获取参数,举个例子:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        safe("haha", 20, true);
        ...
    }

    @Safe
    private void safe(String a, int b, boolean c) {
        Log.d("aop", "获取参数")
    }

通知方法

    @Around("execution(!synthetic * *(..)) && methodAnnotated()")
    public void aroundJoinPoint(final ProceedingJoinPoint joinPoint) throws Throwable {
        for (Object arg : joinPoint.getArgs()) {
            Log.d("arg", arg.toString());
        }
        joinPoint.proceed(joinPoint.getArgs());
    }

日志打印

11-12 06:56:08.062 29915-29915/com.jie.aoptest D/arg: haha
11-12 06:56:08.062 29915-29915/com.jie.aoptest D/arg: 20
11-12 06:56:08.062 29915-29915/com.jie.aoptest D/arg: true
注解参数的获取

直接上代码例子
首先需要在注解上声明参数

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckPermission {
    //声明参数
    String declaredPermission();
}

然后看一下Activity中的调用方法,注意这里在注解后设置参数值

    @CheckPermission(declaredPermission="android.permission.READ_PHONE_STATE")
    private void checkPhoneState(){
        Log.d("CheckPermission","Read Phone State succeed");
    }

看一下切片类的写法,注意这里在切点上要用@annotation来获取注解对象,然后我们在aroundMethod方法中多了一个checkPermission对象,最后从这个对象中拿到注解参数

@Aspect
public class CheckPermissionAspect {

    @Pointcut("execution(@com.jie.aoptest.aop.CheckPermission * *(..)) && @annotation(checkPermission)")
    public void checkPermission(CheckPermission checkPermission){};

    @Around("checkPermission(checkPermission)")
    public void aroundMethod(JoinPoint joinPoint, CheckPermission checkPermission){
        //从注解信息中获取声明的权限。
        String neededPermission = checkPermission.declaredPermission();
        Log.d("CheckPermissionAspect", joinPoint.toShortString());
        Log.d("CheckPermissionAspect", "\tneeded permission is " + neededPermission);
    }
}

最后来看一下输出日志,证明我们已经成功拿到注解参数了

11-12 08:00:21.203 24559-24559/com.jie.aoptest D/CheckPermissionAspect: execution(MainActivity.checkPhoneState())
11-12 08:00:21.203 24559-24559/com.jie.aoptest D/CheckPermissionAspect:     needed permission is android.permission.READ_PHONE_STATE
11-12 08:00:21.203 24559-24559/com.jie.aoptest D/CheckPermission: Read Phone State succeed

总结

AspectJ解析基本就到这里了,掌握了它就可以全面的用AOP思想去解决问题了,核心还是在解决问题的思路。我也是在边学习边整理从而写出这篇文档,这里讲解了一些AspectJ的基础用法,高级用法大家可以从参考文献的书中去慢慢探索。

参考文献

  1. 深入理解Android之AOP 博主写的细致入微,我也从中有所参考。
  2. Manning.AspectJ.in.Action第二版
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
I’ve always felt that implementing a software system is much harder than it needs to be. It is difficult to map requirements to the implementation and then trace the implementation back to the requirements. Although many approaches—such as object-oriented programming, component-oriented programming, and design patterns—help to some extent, none of them satisfactorily addresses the system-level requirements, often referred to as crosscutting concerns, that must be included in multiple modules. I came across AspectJ version 0.3 in 1998 while looking for better ways to architect a Java-based system. AspectJ was an implementation of aspect-oriented programming (AOP), a new methodology that specifically targeted the management of crosscutting concerns. Even though AspectJ was in its infancy, I became fascinated by its potential. The struggle to keep up with all the new advances in the Java and XML world, along with other priorities in my life, prevented me from pursuing it further. Still, exploring AspectJ was always on my to-do list, and I started looking at it again when it was in version 0.8. By then, AspectJ had evolved into a much more powerful language. I started using AspectJ and found that the more I used it, the more I fell in love with it. Today, the current version of AspectJ (1.1)—which this book is based on—has morphed into a mature, robust language. In early 2002, I wrote a series of articles for JavaWorld describing AOP and AspectJ; the book you are holding grew out of that series. From reader responses, I realized that most developers understand that AspectJ can be xviii PREFACE used to modularize the crosscutting concern of logging, but they struggle to imagine how it may be applied beyond that. Logging, while an important concern, is not something developers lose sleep over. Logging using AspectJ, therefore, is best characterized as a vitamin and not a painkiller; while vitamins are important, often the need for them is not pressing enough to require immediate action. To further complicate the situation, the examples of AOP that are widely available today either repeat the same logging problem or are too abstract to be of immediate practical value. My mission statement for this book is “to be a key element in bringing AOP and AspectJ into everyday practice.” To accomplish this goal, the book not only presents the AspectJ language but also provides practical AspectJ-based solutions to a wide variety of real-world problems. You will find that you can utilize these solutions to quickly reap the benefits of the language. I have tried to use current technologies as the basis for these solutions so that you can readily apply them to your system. This also demonstrates that these latest technologies by themselves are not enough to manage crosscutting concerns, since combined with AspectJ, they provide a better solution. The book also presents a few original design patterns that increase the power of AspectJ significantly. It is not often that one gets to write about such an exciting new programming methodology and language. I enjoyed writing this book. I hope you will enjoy reading it.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值