AOP思想-使用AspectJ处理全局单点登录问题

250 阅读3分钟

介紹

  1. AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
  2. AspectJ是一个面向切面编程的框架。AspectJ是对java的扩展,而且是完全兼容java的,AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。AspectJ还支持原生的Java,只需要加上AspectJ提供的注解即可。在Android开发中,一般就用它提供的注解和一些简单的语法就可以实现绝大部分功能上的需求了。

常用名词释义

  1. Joinpoint(连接点) 是指被拦截到的点
  2. Pointcut(切入点) 要对哪些 Joinpoint连接点 进行拦截
  3. Advice(通知/增强) 拦截到 Joinpoint 之后所要做的事情就是通知,具体业务逻辑处理,包括:@Before 前置通知, 在目标执行之前执行通知,@After 后置通知, 目标执行后执行通知,@Around 环绕通知, 在目标执行中执行通知, 控制目标执行时机
  4. execution 用于匹配方法执行的连接点
  5. @target 用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解
  6. @annotation 用于匹配当前执行方法持有指定注解的方法
  7. Aspect(切面) 是切入点和通知(引介)的结合

举个例子

声明:本文使用目前最新版aspectj的版本 1.9.4,minSdkVersion 24 不能低于Android 7.0

 classpath 'org.aspectj:aspectjtools:1.9.4'
implementation 'org.aspectj:aspectjrt:1.9.4'

登录的注解,要切面的点

/**
 * @author YangTianFu
 * @date 2019/9/29  19:48
 * @description  登录的注解,要切面的点
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginFilter {

    int userDefine() default  0;
}

登录的切面

package com.bliss.aspectjlogin.annotation.aspect;

import android.content.Context;

import com.bliss.aspectjlogin.annotation.LoginFilter;
import com.bliss.aspectjlogin.core.ILogin;
import com.bliss.aspectjlogin.core.LoginAssistant;
import com.bliss.aspectjlogin.exception.AnnotationException;
import com.bliss.aspectjlogin.exception.NoInitException;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import java.security.SignatureSpi;

/**
 * @author YangTianFu
 * @date 2019/9/29  19:49
 * @description 登录的切面
 */
public class LoginFilterAspect {
    private static final String TAG = "LoginFilterAspect";

//    切入点 对LoginFilter中的*通配   所有参数类型和个数的有无返回值的方法
    @Pointcut("execution(@com.bliss.aspectjlogin.annotation.LoginFilter * *(..))")
    public void loginFilter(){

    }

//    通知 对进入切面的内容如何处理 切面的具体执行 loginFilter方法名自起,但要保持一致,
//   @Around 需要控制切入点的执行逻辑,调用joinPoint.proceed()
    @Around("loginFilter()")
    public void aroundLoginPoint(ProceedingJoinPoint joinPoint) throws Throwable{
//        访问当前正在执行的方法中的所有方法和参数 Joinpoint(连接点) 被拦截到的点
        ILogin iLogin = LoginAssistant.getInstance().getiLogin();
        if (iLogin == null){
            throw new NoInitException("LoginSDK 没有初始化!");
        }

        //获取当前方法的签名
        Signature signature = joinPoint.getSignature();
        if (!(signature instanceof MethodSignature)){
            throw new AnnotationException("LoginFilter 注解只能用于方法上");
        }

        MethodSignature methodSignature = (MethodSignature) signature;
        LoginFilter loginFilter = methodSignature.getMethod().getAnnotation(LoginFilter.class);
        if ((loginFilter == null)) return;

        Context param = LoginAssistant.getInstance().getApplicationContext();
        if (iLogin.isLogin(param)){
           //如果已经登录,继续执行后边的业务
            joinPoint.proceed();
        }else {
        //没有登录去登录
            iLogin.login(param,loginFilter.userDefine());
        }
    }
}

注册方法

package com.bliss.aspectjlogin;

import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

import com.bliss.aspectjlogin.core.ILogin;
import com.bliss.aspectjlogin.core.LoginSDK;
import com.bliss.aspectjlogin.util.SharePreferenceUtil;

/**
 * @author YangTianFu
 * @date 2019/9/29  19:05
 * @description
 */
public class MyApplication extends Application {


    @Override
    public void onCreate() {
        super.onCreate();
        LoginSDK.getInstance().init(this, iLogin);
    }


    ILogin iLogin = new ILogin() {
        @Override
        public void login(Context applicationContext, int userDefine) {
            switch (userDefine) {
                case 0:
                    Intent intent = new Intent(applicationContext, LoginActivity.class);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                    break;
                case 1:
                    Toast.makeText(applicationContext, "您还没有登录,请登陆后执行", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    Toast.makeText(applicationContext, "执行失败,因为您还没有登录!", Toast.LENGTH_SHORT).show();
                    break;
            }
        }

        @Override
        public boolean isLogin(Context applicationContext) {
            return SharePreferenceUtil.getBooleanSp(SharePreferenceUtil.IS_LOGIN, applicationContext);
        }

        @Override
        public void clearLoginStatus(Context applicationContext) {
            SharePreferenceUtil.setBooleanSp(SharePreferenceUtil.IS_LOGIN, false, applicationContext);
        }
    };
}

源码地址