The introduction

AspectJ is familiar to many of you who have done Web, and Spring’s AOP is based on it. If you’re writing a program, you probably don’t need it. If you need to debug it, you just need to add system.out.printfln () or log.d (). However, due to the inherent defects of object-oriented, many of the same module, the same level of work has to be repeated in many classes. Such things as logging, monitoring method execution times, modifying application runtime parameters, etc., are all code that can be reused.

If in a large project, the use of manual modification of the source code to achieve the purpose of debugging, monitoring, first, the need to insert a lot of repeated code (print logs, monitoring method execution time), the code can not be reused; Second, the cost of modification is too high, manual modification is needed everywhere (minute tired, dazzling).

  • OOP: Object orientation treats everything as an object, so each object has its own life cycle and is an encapsulated whole. Each object has its own set of vertical series of methods and properties, don’t need too much when we use the object implementation process and the relationship between its internal details, only need to pay attention to the input and output, this is very similar with our way of thinking, greatly reducing the costs of our code (and not as a headache as C!) . But in the real world, not all problems fit neatly into modules. To take the simplest and most common example, now that you want to add logging to every module, you want the module to log when it runs. In the absence of AOP, the usual approach is to design a Log output module that provides a Log output API, such as the Log class in Android. Then, other modules call several functions of the Log class when they need to output logs, such as e(TAG,…). W (TAG,…). D (TAG,…). I (TAG,…). And so on.
  • AOP: OOPIs open another era of programming, but over time also revealed its shortcomings, the most obvious is that it cannot transverse cutting one kind of methods, properties, and when we need to know one kind of methods, one kind of attribute information, will have to be in each class method (even if they are the same way, because of different classes and different) add monitoring code, This is not an option in the case of large amounts of code. As a result, AOP programming was created, and AOP-based programming allows us to horizontally slice a class of methods and properties (regardless of what class it is!). AOP is not the antithesis of OOP, but a complement to OOP because it makes debugging and monitoring easier and clearer.

1. Introduce AspectJ

1.1 AspectJ is just a code compiler

AspectJ stands for Java Aspect, Java AOP. It’s not really a new language, it’s just a code compiler (AJC) that adds some of its own keyword recognition and compilation methods to the Java compiler. Therefore, AJC can also compile Java code. At compile time, it weaves the Aspect program written by the developer into the target program and reconstructs the target program, aiming to establish the connection between the target program and the Aspect program (coupling, obtaining the reference of the other side (obtaining the declaration type, not the runtime type) and context information). This serves the purpose of AOP (here the original program’s code is still modified at compile time, but AJC does it for us).

1.2 AspectJ is used to do AOP programming

Cross-cutting concerns: Although most classes in an object-oriented model implement a single specific function, it is common to open up common dependencies to other classes. For example, we want to add logs to classes in the data access layer, but also when a thread in the UI layer enters or exits to call a method. Although each class has one main function that differentiates it from the others, it is often necessary to add some of the same ancillary functions in the code.

  • Advice: Code injected into a class file. Typical Advice types are before, after, and around, which represent code that is executed before, after, and completely replaces the target method, respectively. In addition to injecting code into methods, other changes may be made to the code, such as adding fields or interfaces to a class.
  • Joint point: A specific point in a program that may be the target of code injection, such as a method call or method entry.
  • Pointcut: Tells the code injection tool where to inject a particular code expression. For example, which joint points to apply a particular Advice to. Pointcuts can have a single choice, such as executing a method, or multiple choices, such as all methods that mark a custom annotation defined as @debGuTrace.
  • Aspect: The combination of Pointcut and Advice is considered an Aspect. For example, we add a log aspect in our application by defining a pointcut and giving the appropriate advice.
  • Weaving: the process of putting code into joint points.

The diagram below summarizes these concepts briefly.

Traditional programming: Insert validation user modules one by one

AOP solution: Focus

1.3. Why AspectJ?
  • Non-intrusive monitoring: support code injection at compile time and load time, you can monitor the operation of the monitoring object without repairing it, intercept a certain class of methods, and even modify its parameters and runtime trajectory!
  • Easy to use: It’s Java, and anyone who knows Java can use it.
  • Powerful, extensible high: it is a compiler + a library, developers can maximize the implementation of various AOP procedures!

2. Download AspectJ resources and build. Gradle configuration

2.1 download address

Download aspectj address www.eclipse.org/aspectj/dow… \

2.2. Decompress the AspectJ JAR package to obtain AspectJrt.jar
2.3 build. Gradle configuration

Refer to the build. Gradle aspectJ writing fernandocejas.com/2014/08/03/… Build. gradle configuration in the root directory:


buildscript { repositories { mavenCentral() google() jcenter() } dependencies { classpath 'com. Android. Tools. Build: gradle: 3.0.0' classpath 'org. Aspectj: aspectjtools: 1.8.13' classpath 'org. Aspectj: aspectjweaver: 1.8.13' / / NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }Copy the code

Gradle configuration in modules:


apply plugin: 'com.android.application' import org.aspectj.bridge.IMessage import org.aspectj.bridge.MessageHandler import org.aspectj.tools.ajc.Main android { compileSdkVersion 26 defaultConfig { applicationId "com.haocai.aopdemo" 15 targetSdkVersion 26 versionCode minSdkVersion 1 versionName testInstrumentationRunner "1.0" "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } 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; } } } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation' com. Android. Support: appcompat - v7:26.1.0 'implementation 'com. Android. Support. The constraint, the constraint - layout: 1.0.2' testImplementation junit: junit: '4.12' compile Files (' libs/aspectjrt. Jar) androidTestImplementation 'com. Android. Support. Test: runner:' 1.0.1 androidTestImplementation 'com. Android. Support. Test. Espresso: espresso - core: 3.0.1' / / compile 'org. Aspectj: aspectjrt: 1.8 +'}Copy the code
Note:

Jar (‘libs/aspectjrt.jar’)

3. Sample program

Create annotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface BehaviorTrace {
    String value();
    int type();
}
Copy the code
Aspect class

/** * Created by Xionghu on 2018/1/23. * Desc: @aspect Public Class BehaviorAspect {private static Final String TAG = "MainAspect"; SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); / * * * according to the point of tangency cut into what look like * * / the @pointcut (" execution (@ com. Haocai. Aopdemo. BehaviorTrace * * (..) ") public void annoBehavior() {} /** * * */ @around ("annoBehavior()") public Object dealPoint(ProceedingJoinPoint point) throws Throwable MethodSignature methodSignature = (MethodSignature)point.getSignature(); BehaviorTrace behaviorTrace = methodSignature.getMethod().getAnnotation(BehaviorTrace.class); String contentType = behaviorTrace.value(); int type = behaviorTrace.type(); Log. I (TAG,contentType+" 下 载 "+ SimpleDateFormat.format (new Date())); long beagin=System.currentTimeMillis(); Object Object = null; try{ object = point.proceed(); }catch (Exception e){ e.printStackTrace(); } log. I (TAG," elapsed time: "+(system.currentTimemillis ()-beagin)+"ms"); return object; }}Copy the code
Calling main program

package com.haocai.aopdemo; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; public class MainActivity extends AppCompatActivity { private static final String TAG = "Main"; SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @behaviorTrace (value = "BehaviorTrace ",type = 1) public void mShake(view view) { SystemClock.sleep(3000); Log. I (TAG," shake a red envelope "); }} /** * the BehaviorTrace(value = "BehaviorView :",type = 1) public void mAudio(view view) { SystemClock.sleep(3000); Log. I (TAG," I want to get a red envelope "); }} /** * Typing module ** @param view */ @behaviorTrace (value = "Typing :",type = 1) public void mText(view view) { SystemClock.sleep(3000); Log. I (TAG," Typing logic, I got a big red envelope "); } // ** // @behaviorTrace (value = "BehaviorTrace ",type = 1) // Public void mShake(view) view) // { // SystemClock.sleep(3000); // log. I (TAG," shake a soft model: no date "); / /} / / / / / / / * * * shake modules / / * / / * @ param view / / * / / / public void mShake view (view) / / {/ / / / long beagin=System.currentTimeMillis(); // log. I (TAG," simpleDateformat.format (new Date())); // // shake the code logic // {// systemclock. sleep(3000); // // log. I (TAG," shake a red envelope "); / / / /} / / / / / / event statistics logic Log. I (TAG, "elapsed time:" + (System. CurrentTimeMillis () - beagin) + "ms"); / / / /} / / / / / * * * voice module / / * / / * @ param view / / * / / / public void mAudio (view view) / / {/ / long beagin=System.currentTimeMillis(); // log. I (TAG," voice: usage time: "+ simpleDateformat.format (new Date()))); // // voice code logic // {// systemclock. sleep(3000); // // log. I (TAG," send voice: I want to get a red envelope "); / / / /} / / / / / / event statistics logic Log. I (TAG, "elapsed time:" + (System. CurrentTimeMillis () - beagin) + "ms"); / /} / / / / / / / * * * type module / / * / / * @ param view / / * / / / public void mText (view view) / / {/ / / / / / statistics of user behavior logic Log. I (TAG," + simpleDateformat.format (new Date())); // long beagin=System.currentTimeMillis(); // // // type module logic // {// systemclock. sleep(3000); // log. I (TAG," typing logic, I got a big red envelope "); / / / /} / / / / / / event statistics logic Log. I (TAG, "elapsed time:" + (System. CurrentTimeMillis () - beagin) + "ms"); / /}}Copy the code
Note: The following comments are traditionally written
The results

01-23 19:39:09. 579. 13051-13051 / com haocai. Aopdemo I/MainAspect: 2018-01-23 19:39:09 01-23 19:39:12.589 13051-13051/com.haocai.aopdemo I/Main: 01-23 19:39:12.589 13051-13051/com.haocai.aopdemo I/MainAspect: 01-23 19:39:12.589 13051-13051/com.haocai.aopdemo I/MainAspect: 2018-01-23 19:39:1201-23 19:39:15.599 13051-13051/com.haocai.aopdemo I/Main: 01-23 19:39:15.599 13051-13051/com.haocai.aopdemo I/MainAspect: 01-23 19:39:15.599 13051-13051/com.haocai. aOPdemo I/MainAspect: 609 13051-13051/com.haocai.aopdemo I/Main: 01-23 19:39:18.609 13051-13051/com.haocai.aopdemo I/MainAspect: Consumption time: 3000msCopy the code