AOP of architecture design of centralized landing architecture based on aspect oriented idea

Keywords: Gradle Maven Android nexus

1, Before you start:

Centralized login architecture design: this login is not a login request, but a judgment of whether the current login status is available. If it is not, skip the login interface; otherwise, perform the corresponding skip logic. This business is a global business, and the global business is extracted to one aspect.
If it is OOP thought programming, use SharedPreferences to save isLogined login status. If isLogined == true, jump to my integral, my zone, etc. Otherwise, jump to LoginActivity.

2, Implementation mode 1: implementation of dynamic agent at runtime

It is implemented in the way of dynamic agent at run time. Check the code specifically CentralizedLoginArchitecture
Focus on mode 2:

3, Implementation mode 2: AspectJ

1. For example, there are the following requirements:


Similar requirements, such as user behavior statistical analysis, are also a global business. It can also be done by AOP aspect.

Get back to the point AspectJ AspectJ is a framework of aspect oriented programming, which extends the Java language and defines the syntax to implement AOP.

2. Explanation of terms:

PointCut, PointCut
Advice, advice
Joint Point, before after around

3. Integrate AspectJ

AspectJ can be integrated through plug-ins or directly through gradle.

1. Plug in integration
  1. New main module app
  2. New android library aspectj
2. gradle integration
  1. Project root build.gradle add dependency:
    // Top-level build file where you can add configuration options common to all sub-projects/modules.
    
    buildscript {
        repositories {
            maven {
                url 'http://maven.aliyun.com/nexus/content/groups/public/'
            }
            google()
            jcenter()
    //        mavenCentral()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:3.5.0'
    
            // Version boundary: as-3.0.1 + gradle 4.4-all (NDK environment of r17 needs to be configured)
            // Or: as-3.2.1 + gradle 4.6-all (normal use, no warning)
            classpath 'org.aspectj:aspectjtools:1.8.9'
            classpath 'org.aspectj:aspectjweaver:1.8.9'
        }
    }
    
    allprojects {
        repositories {
            maven {
                url 'http://maven.aliyun.com/nexus/content/groups/public/'
            }
            google()
            jcenter()
    //        mavenCentral()
        }
    }
    
    task clean(type: Delete) {
        delete rootProject.buildDir
    }
    
    
    It is worth noting that if an error is reported during project synchronization: received close  notify during handshake, you can try to use Alibaba cloud's warehouse as above
    	maven {
            url 'http://maven.aliyun.com/nexus/content/groups/public/'
        }
    
  2. Modify build.gradle in the moudle of app: compile with Aspect specific compiler instead of traditional javac
    // Version boundary: as-3.0.1 + gradle 4.4-all (NDK environment of r17 needs to be configured)
    // Or: as-3.2.1 + gradle 4.6-all (normal use, no warning)
    buildscript { // Compile with Aspect specific compiler instead of traditional javac
        repositories {
            maven {
                url 'http://maven.aliyun.com/nexus/content/groups/public/'
            }
    //        mavenCentral()
        }
        dependencies {
            classpath 'org.aspectj:aspectjtools:1.8.9'
            classpath 'org.aspectj:aspectjweaver:1.8.9'
        }
    }
    
  3. The whole is as follows:
    apply plugin: 'com.android.application'
    
    // Version boundary: as-3.0.1 + gradle 4.4-all (NDK environment of r17 needs to be configured)
    // Or: as-3.2.1 + gradle 4.6-all (normal use, no warning)
    buildscript { // Compile with Aspect specific compiler instead of traditional javac
        repositories {
            maven {
                url 'http://maven.aliyun.com/nexus/content/groups/public/'
            }
    //        mavenCentral()
        }
        dependencies {
            classpath 'org.aspectj:aspectjtools:1.8.9'
            classpath 'org.aspectj:aspectjweaver:1.8.9'
        }
    }
    
    android {
        compileSdkVersion 29
        buildToolsVersion "29.0.2"
    
    
        defaultConfig {
            applicationId "com.example.aspectj"
            minSdkVersion 15
            targetSdkVersion 29
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
    
        implementation 'androidx.appcompat:appcompat:1.0.2'
        implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'androidx.test:runner:1.1.1'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    
        // Package with aspectj
        implementation 'org.aspectj:aspectjrt:1.8.13'
    }
    
    
    /**
     * The following groovy code doesn't need much attention, just as a support
     */
    // Version boundary: as-3.0.1 + gradle 4.4-all (NDK environment of r17 needs to be configured)
    // Or: as-3.2.1 + gradle 4.6-all (normal use, no warning)
    import org.aspectj.bridge.IMessage
    import org.aspectj.bridge.MessageHandler
    import org.aspectj.tools.ajc.Main
    
    final def log = project.logger
    final def variants = project.android.applicationVariants
    
    variants.all { variant ->
        if (!variant.buildType.isDebuggable()) {
            log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
            return;
        }
    
        JavaCompile javaCompile = variant.javaCompile
        javaCompile.doLast {
            String[] args = ["-showWeaveInfo",
                             "-1.8",
                             "-inpath", javaCompile.destinationDir.toString(),
                             "-aspectpath", javaCompile.classpath.asPath,
                             "-d", javaCompile.destinationDir.toString(),
                             "-classpath", javaCompile.classpath.asPath,
                             "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
            log.debug "ajc args: " + Arrays.toString(args)
    
            MessageHandler handler = new MessageHandler(true);
            new Main().run(args, handler);
            for (IMessage message : handler.getMessages(null, true)) {
                switch (message.getKind()) {
                    case IMessage.ABORT:
                    case IMessage.ERROR:
                    case IMessage.FAIL:
                        log.error message.message, message.thrown
                        break;
                    case IMessage.WARNING:
                        log.warn message.message, message.thrown
                        break;
                    case IMessage.INFO:
                        log.info message.message, message.thrown
                        break;
                    case IMessage.DEBUG:
                        log.debug message.message, message.thrown
                        break;
                }
            }
        }
    }
    
    

4. Code operation

Requirements: click log in; view my zone, my coupons, my points (check whether you are logged in before checking, if so, jump to the corresponding functional area, otherwise jump to the login page and log in again); in addition, conduct statistical analysis.

Question: how to achieve statistical analysis of user behavior?
A: third party statistics such as the friendship League. But if you want to implement it, you can only implement it through AOP.

Count the behavior of users clicking a function. (in the actual project, it is stored locally and transmitted to the server every N days).

1. User behavior statistics
  1. Custom behavior annotation

    //User click trace (Behavior Statistics) user defined notes refer to: open class IOC container
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ClickBehavior {
        String value();
    }
    
  2. Apply behavior annotation to code requiring behavior statistics.

        // User behavior statistics
        @ClickBehavior(value = "Sign in")
        public void login(View view) {
            Log.e(TAG, "login: Verification passed, login succeeded");
        }
    
  3. Define AspectJ
    When writing this class, you need to be careful not to write a wrong symbol, otherwise it is not easy to check.
    AspectJ consists of PointCut and advice. Advice has three situations: Before After Around

    1. Use the annotation @ Aspect to define a tangent class.
      @Aspect
      public class ClickBehaviorAspect {
      }
      
      
    2. Find the entry point to deal with through @ Pointcut
          // 1. Which annotations are used in the application, put them into the current pointcut for processing (find the pointcut to be processed)
          // Execution triggers the Aspect class with the method execution as the pointcut. Syntax: execution()
          // ** (..) wildcard, which can handle all methods of ClickBehavior class, or specify com.example.aspectj.MainActivity.login() method as the entry point
          @Pointcut("execution(@com.example.aspectj.annotation.ClickBehavior * *(..))") //
          public void methodPointCut() {
          }
      
      You can view the wildcard character * (..) execution(* com.xxx..(...))
    3. Process entry point
      	// 2. How to deal with the entry point,
          // Around how to deal with pointcuts
          @Around("methodPointCut()")
          public Object jointPoint(ProceedingJoinPoint joinPoint) throws Throwable {
          	// Follow three rules: 1. Return value Object 2. Parameter ProceedingJoinPoint 3. Throw an exception Throwable
          }
      
2. Centralized login
  1. Also define LoginCheck annotations
    // Check if you are logged in
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LoginCheck {
    }
    
  2. Using LoginCheck annotations
        //Statistics of user behavior after successful login
        @ClickBehavior(value = "My points")
        @LoginCheck()
        public void score(View view) {
            Log.e(TAG, "score: Jump my points");
            startActivity(new Intent(MainActivity.this, OtherActivity.class));
        }
    
  3. Define Aspect and handle pointcuts
    @Aspect
    public class LoginCheckAspect {
        private static final String TAG = LoginCheckAspect.class.getSimpleName();
    
        @Pointcut("execution(@com.example.aspectj.annotation.LoginCheck * *(..))")
        public void methodLoginCheck() {
        }
    
        @Around("methodLoginCheck()")
        public Object check(ProceedingJoinPoint joinPoint) throws Throwable {
            Context context = (Context) joinPoint.getThis();
            boolean isLogin = false; //In the actual project, read from SharedPreference
            if (isLogin) {
                Log.e(TAG, "Login detected");
                return joinPoint.proceed();//Login detected, continue with subsequent code
            } else {
                Log.e(TAG, "No sign in detected");
                context.startActivity(new Intent(context, LoginActivity.class));
                return null; //Without login, subsequent code does not need to be executed.
            }
        }
    }
    
    

See the code for details. AspectJ

4, Pits encountered in the use of AspectJ

  1. Critical version as 3.0.1 and gradle 4.4-all need to configure the r17 environment of ndk
  2. as3.2.1 and gradle 4.6-all are in normal use without obsolete warning
  3. as3.4.0 and gradle 5.1.1-all have outdated api warnings
Published 10 original articles, won praise 0, visited 228
Private letter follow

Posted by keystroke on Thu, 30 Jan 2020 22:50:33 -0800