当用户未登录时,如果是操作业务模块A或者其它业务模块,都需要先登录,这时候登录模块就是针对三个业务模块的面向切面的模块,这个就是AOP的使用场景。以往是在每个业务模块都添加是否登录的判断,但是现在通过AOP就不需要每个地方都添加是否登录的判断,通过使用动态代理或者预编译方式实现,在方法的前面增加判断
用户数据在删除前都要进行备份
public interface DBoperation {
void delete();
void insert();
void save();
}
import android.util.Log;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DBhandler implements InvocationHandler {
private DBoperation db;
private static final String TAG="DB invoke==";
public DBhandler(DBoperation db) {
this.db = db;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(db!=null){
Log.e(TAG,"备份数据开始");
db.save();
Log.e(TAG,"备份数据结束");
return method.invoke(db,args);
}
return null;
}
}
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import androidx.annotation.Nullable;
import java.lang.reflect.Proxy;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class AopActivity extends Activity implements DBoperation {
private static final String TAG = "DB invoke==";
@BindView(R.id.btn_click)
Button btnClick;
private DBoperation instance;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_btn);
ButterKnife.bind(this);
instance = (DBoperation) Proxy.newProxyInstance(DBoperation.class.getClassLoader()
, new Class[]{DBoperation.class}, new DBhandler(this));
}
@OnClick(R.id.btn_click)
public void onViewClicked() {
instance.delete();
}
@Override
public void delete() {
Log.e(TAG, "数据删除");
}
@Override
public void insert() {
Log.e(TAG, "数据插入");
}
@Override
public void save() {
Log.e(TAG, "数据备份");
}
}
点击删除代码,日志如下
2020-04-18 22:20:58.952 9361-9361/com.xiaoma.brick E/DB invoke==: 备份数据开始
2020-04-18 22:20:58.952 9361-9361/com.xiaoma.brick E/DB invoke==: 数据备份
2020-04-18 22:20:58.952 9361-9361/com.xiaoma.brick E/DB invoke==: 备份数据结束
2020-04-18 22:20:58.952 9361-9361/com.xiaoma.brick E/DB invoke==: 数据删除
总结:在做删除操作时,可以在对应的操作前面进行数据备份,但是需要不同的业务实现同一个接口,不同实现实现不同的删除和备份操作,但是通过动态代理都可以保证在做删除操作前进行保存.
在项目的build.gradle内添加
dependencies {
。....
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
在 app目录下的bulid.gradle 文件中配置如下信息:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
}
android {
}
dependencies {
......
implementation 'org.aspectj:aspectjrt:1.8.13'
}
// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
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
}
}
}
}
上面配置号后,新建注解类用于获取行为统计
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClickBehavior {
String value();
}
新建注解类用于统一处理是否登录
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginCheck {
}
新建需要自定义行为的类--行为统计的前面处理
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
@Aspect
public class ClickBehaviorAspect {
private static final String TAG = "aspectj >>";
@Pointcut("execution(@com.xiaoma.pattern.aop.ClickBehavior * *(..))")
public void methodPointCut() {
}
@Around("methodPointCut()")
public Object jointPoint(ProceedingJoinPoint joinPoint) throws Throwable {
//获取签名方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//获取方法所属的类名
String className = methodSignature.getDeclaringType().getSimpleName();
//获取方法名
String methodName = methodSignature.getName();
//获取方法的注解值
String value = methodSignature.getMethod().getAnnotation(ClickBehavior.class).value();
long begin = System.currentTimeMillis();
Log.e(TAG, "ClickBehavior Method Star");
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - begin;
Log.e(TAG, "ClickBehavior Method End");
Log.e(TAG,String.format("统计了:%s功能,在%s类的%s方法,用时%d ms",value,className,methodName,duration));
return result;
}
}
需要自定义行为的类--是否需要提前登录处理
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class LoginBehaviorAspect {
private static final String TAG = "aspectj >>";
@Pointcut("execution(@com.xiaoma.pattern.aop.LoginCheck * *(..))")
public void methodPointCut() {
}
@Around("methodPointCut()")
public Object jointPoint(ProceedingJoinPoint joinPoint) throws Throwable {
Context context = (Context) joinPoint.getThis();
if (false) { //定义是否登录的参数,默认没有登录
Log.e(TAG, "已登录");
return joinPoint.proceed();
} else {
context.startActivity(new Intent(context, AopActivity.class));
return null;
}
}
}
之后再需要登录或者行为统计的地方使用
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.annotation.Nullable;
import com.xiaoma.brick.R;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class AspectjActivity extends Activity {
private static final String TAG = "aspectj";
/**
* AspectJ 面向切面编程的框架
* as3.0.1 gradle4.4 ndk r17-----
* as3.2.1 gradle4.6
* as3.4.0 gradle5.1.1 有问题
*
* 一个是面向切面登录,一种是统计
*
* 切入点PointCut
* Advice 通知 before after around
* Joint Point 连接点
*/
@ClickBehavior("area")
@LoginCheck
private void area() {
Log.e(TAG, "area");
}
@ClickBehavior("login")
private void login() {
Log.e(TAG, "login");
}
}
login的日志如下
2020-04-18 23:22:03.986 10168-10168/com.xiaoma.brick E/aspectj >>: ClickBehavior Method Star
2020-04-18 23:22:03.986 10168-10168/com.xiaoma.brick E/aspectj: login
2020-04-18 23:22:03.986 10168-10168/com.xiaoma.brick E/aspectj >>: ClickBehavior Method End
2020-04-18 23:22:03.987 10168-10168/com.xiaoma.brick E/aspectj >>: 统计了:login功能,在AspectjActivity类的login方法,用时0 ms
area的日志如下:
点击后直接跳转到登录页面,如果设置已经登录则相关内容如下
@Around("methodPointCut()")
public Object jointPoint(ProceedingJoinPoint joinPoint) throws Throwable {
Context context = (Context) joinPoint.getThis();
if (true) {
Log.e(TAG, "已登录");
return joinPoint.proceed();
} else {
context.startActivity(new Intent(context, AopActivity.class));
return null;
}
}
///日志如下
2020-04-18 23:24:30.680 10600-10600/com.xiaoma.brick E/aspectj >>: 已登录
2020-04-18 23:24:30.682 10600-10600/com.xiaoma.brick E/aspectj >>: ClickBehavior Method Star
2020-04-18 23:24:30.682 10600-10600/com.xiaoma.brick E/aspectj: area
2020-04-18 23:24:30.682 10600-10600/com.xiaoma.brick E/aspectj >>: ClickBehavior Method End
2020-04-18 23:24:30.683 10600-10600/com.xiaoma.brick E/aspectj >>: 统计了:area功能,在AspectjActivity类的area方法,用时0 ms