AOP面向切面编程,通过预编译方式和运行期动态代理实现程序的统一维护的一种技术。
Aspectj框架就是通过预编译方式实现程序的统一维护,今天介绍如何使用:
//配置版本信息分两步,使用分三步
最新版本的aspectj是1.9.4但是需要SDK最低版本24,这里我就是使用1.8.9版本
//配置信息
1:在工程的build.gradle添加下面依赖
buildscript {
repositories {
google()
//镜像仓,解决需要翻墙下载的
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
//aspectj框架
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
//镜像仓,解决需要翻墙下载的
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
jcenter()
}
}
2:在module的build.gradle添加下面依赖
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "30.0.0"
defaultConfig {
applicationId "com.example.myaspectj"
minSdkVersion 16
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.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
//使用aspectj框架
implementation 'org.aspectj:aspectjrt:1.8.9'
}
//aspectj配置打印log
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 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
}
}
}
}
使用三部曲
1>自定义注解:根据你的需要设置注解的目标,和作用域
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginTrace {
String value() default "";
}
2>创建个切面处理类,处理所有使用注解的逻辑,统一在这个类处理
//切面
@Aspect
public class LoginAapect {
//切入点 ("execution(@注解全类名 * *(..)")
@Pointcut("execution(@com.example.administrator.aop_aspectj.annotation.LoginTrace * *(..))")
public void methodAnnotationWithLoginTrace() {
}
//切入点逻辑处理(可以对 切入点 前处理 后处理 前后处理 )
//参数--->切入点方法名()--->methodAnnotationWithLoginTrace()
// @After("methodAnnotationWithLoginTrace()")//切入点后运行 --》使用注解LoginTrace的方法运行完后执行这个方法代码(可以不返回对象)
// @Before("methodAnnotationWithLoginTrace()")//切入点前运行(可以不返回对象)
@Around("methodAnnotationWithLoginTrace()")//切入点前 后 都运行(后运行是在ProceedingJoinPoint对象非空时运行) (必须返回object对象)
public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
Log.e("zdh", "-------------前");
//如果想要获取自定义注解属性值 必须使用@Around 通过ProceedingJoninPoint获取
//例如
LoginTrace loginTrace=((MethodSignarure)joinPoint.getSignature()).getMethod().getAnnotation(LoginTrace.class);//获取自定义注解对象
String value=loginTrace.value();//注解属性值
Object proceed=null;
if (joinPoint != null) { //如果使用Around注解需要返回proceed对象,要不然注解方法里面代码不执行
proceed = joinPoint.proceed();
Log.e("zdh", "-------------后");
}
return proceed;
}
}
//在需要统一处理的方法添加注解
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//如果你想区分不同方法可以使用标记区分--》@LoginTrace("send")
@LoginTrace
public void send(View view) {
Log.e("zdh","------------测试");
}
}
//运行代码就可以看到打印日志如下
注意混淆 在项目的build.build里面配置 扫描指定文件,防止扫描其他文件导致错误
aspectjx { //指定只对含有关键字'universal-image-loader', 'AspectJX-Demo/library'的库进行织入扫描,忽略其他库,提升编译效率 // includeJarFilter 'universal-image-loader', 'AspectJX-Demo/library' include 'com.gemini' exclude 'com.amitshekhar' exclude 'com.mobile' exclude 'com.nirvana' exclude 'com.cmic' exclude 'com.unicom' // excludeJarFilter '.jar' // ajcArgs '-Xlint:warning' }
是不是很简单。
总结:1:在依赖时需要注意1.9前和1.9的SDK最低版本问题,
2:在依赖导报可以添加镜像仓看看。
3:如果使用Around注解需要返回proceed对象,要不然注解方法里面代码不执行
4:使用注解的方法一般是不会影响方法里面代码执行的,所以要考虑方法里面代码执行后的效果,
//下面以实际项目需要写个demo,一般在开发中我们有些页面是需要用户登录后才能进入的,这里面就以登录为实例写个demo