写给自己的AOP简单实现

2021-04-08  本文已影响0人  无良安生

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个
热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑
的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高
了开发的效率。

一、需求

项目中有很多操作需要在登陆后才能操作的功能
普通oop操作就是每个功能开启前都要去写一段判断登录的代码

二、动态代理 proxy

使用动态代理,在执行的方法中去做自己想做的事情
上代码:
如果我们要进入 购物车、积分商城、 我的订单都要去做登录处理
比如我么能进入积分商城,代码如下

   @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityHomeBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        binding.bntLogin.setOnClickListener(this);
        binding.bntOrderList.setOnClickListener(this);
        binding.bntPointsMall.setOnClickListener(this);
        binding.bntShopCart.setOnClickListener(this);
    }
    
    //就这样,每次都要去判断是否是登录
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bnt_login:
                login();
                break;
            case R.id.bnt_order_list:
                if(!isLogin()){
                    login();
                }else{
                    orderList();
                }
                break;
            case R.id.bnt_points_mall:
              if(!isLogin()){
                    login();
                }else{
                    pointsMall();
                }
                break;
            case R.id.bnt_shop_cart:
              if(!isLogin()){
                    login();
                }else{
                     shopCart();
                }
                break;
        }
    }

    public void shopCart() {
        Log.e(TAG, "--------------------->购物车");
    }

    public void pointsMall() {
        Log.e(TAG, "--------------------->积分商城");
    }

    public void orderList() {
        Log.e(TAG, "--------------------->我的订单");
    }

    public void login() {
        Log.e(TAG, "--------------------->登录");
    }
    
    public void isLogin():Boolean {
        Log.e(TAG, "--------------------->是否登录");
    }

看到oop写法,下面看看aop写法:

  1. 先定义一个接口:
public interface UserManager {

     void shopCart();

     void pointsMall();

     void orderList();
}
  1. 然后在界面中动态代理
public class AOPProxyActivity extends AppCompatActivity implements UserManager {

private UserManager user;


   @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityHomeBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        binding.bntLogin.setOnClickListener(this);
        binding.bntOrderList.setOnClickListener(this);
        binding.bntPointsMall.setOnClickListener(this);
        binding.bntShopCart.setOnClickListener(this);
        
          db = (UserManager) Proxy.newProxyInstance(UserManager.class.getClassLoader(), new Class[]{UserManager.class}, new UserManager(this));
    }
    
    class DBHander implements InvocationHandler {
        private DBManager user;

        public DBHander(DBManager user) {
            this.user = user;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            if (db != null) {
                if(isLogin()){
                //这个就是你要执行的方法
                    return method.invoke(db, args);
                }else{
                //没登陆就先登录
                    login();
                }
            }
            return null;
        }
    }
    
    
    
    //就这样,每次都要去判断是否是登录
       @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bnt_login:
                login();
                break;
            case R.id.bnt_order_list:
                orderList();
                break;
            case R.id.bnt_points_mall:
                pointsMall();
                break;
            case R.id.bnt_shop_cart:
                shopCart();
                break;
        }
    }

    public void shopCart() {
        Log.e(TAG, "--------------------->购物车");
    }

    public void pointsMall() {
        Log.e(TAG, "--------------------->积分商城");
    }

    public void orderList() {
        Log.e(TAG, "--------------------->我的订单");
    }

    public void login() {
        Log.e(TAG, "--------------------->登录");
    }
    
    public void isLogin():Boolean {
        Log.e(TAG, "--------------------->是否登录");
    }
}

比如进入订单,打印如下

  1. --------------------->是否登录
  2. --------------------->登录
  1. --------------------->是否登录
  2. --------------------->我的订单

以上就是简单的AOP

三、预编译AOP

需要用到 Aspect

  1. 工程gradle:
      dependencies {
      //这个是自己项目的
        classpath "com.android.tools.build:gradle:4.1.2"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
        // 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
  1. 项目gradle

//这个要在plugins模块之上
// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
// 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
// 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
buildscript { // 编译时用Aspect专门的编译器,不再使用传统的javac
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}
plugins {
   //自己项目的//
}

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;
            }
        }
    }
}

然后看看项目是否报错

前方高能哈
注意:

  1. 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
  2. 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)
  3. 版本界限:As-3.0.1 + gradle4.4-all (需要配置r17的NDK环境)
  4. 或者:As-3.2.1 + gradle4.6-all (正常使用,无警告)

这个才完成配置而已

接下来,我们做个登录和行为统计

先来个注解(主要用来传行为注释)

/**
 * 行为统计
 */
@Target(ElementType.METHOD)//目标作用在方法之上:ElementType.METHOD
@Retention(RetentionPolicy.RUNTIME)
public @interface ClickBehavior {
    String value();
}

然后来个aspect

@Aspect
public class ClickBehaviorAspect {
    private final String TAG = getClass().getSimpleName();

    // 1、应用中用到了哪些注解,放到当前的切入点进行处理(找到需要处理的切入点)
    // execution,以方法执行时作为切点,触发Aspect类
    // * *(..)) 可以处理ClickBehavior这个类所有的方法
    @Pointcut("execution(@com.cxz.demo.ClickBehavior * *(..))")
    public void methodPointCut() {
    }
    
    // 2、对切入点如何处理
    @Around("methodPointCut()")
    public Object jointPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        Log.e(TAG,"》》》》》》》》》》》》》》》》》》》  开始执行");
        //获取签名方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        //获取方法所属的类名
        String className = methodSignature.getDeclaringType().getSimpleName();

        String methodName = methodSignature.getName();

        String funName = methodSignature.getMethod().getAnnotation(ClickBehavior.class).value();

        long beginTime = System.currentTimeMillis();

        Object procce = joinPoint.proceed();

        long drationTime = System.currentTimeMillis()-beginTime;
        Log.e(TAG,String.format("开始了开始了,方法:%s  行为:%s  需要的时间:%s  " ,className,funName,drationTime+"ms"));

        Log.e(TAG,"》》》》》》》》》》》》》》》》》》》  执行结束");
        return procce;
    }

}

然后可以开始浪了
看看界面

public class HomeActivity extends AppCompatActivity implements View.OnClickListener {
    final String TAG = this.getClass().getSimpleName();

    private ActivityHomeBinding binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityHomeBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        binding.bntLogin.setOnClickListener(this);
        binding.bntOrderList.setOnClickListener(this);
        binding.bntPointsMall.setOnClickListener(this);
        binding.bntShopCart.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bnt_login:
                login();
                break;
            case R.id.bnt_order_list:
                orderList();
                break;
            case R.id.bnt_points_mall:
                pointsMall();
                break;
            case R.id.bnt_shop_cart:
                shopCart();
                break;
        }
    }

    @ClickBehavior("购物车")
    public void shopCart() {
        Log.e(TAG, "--------------------->购物车");
    }

    @ClickBehavior("积分商城")
    public void pointsMall() {
        Log.e(TAG, "--------------------->积分商城");
    }

    @ClickBehavior("我的订单")
    public void orderList() {
        Log.e(TAG, "--------------------->我的订单");
    }

    @ClickBehavior("登录")
    public void login() {
        Log.e(TAG, "--------------------->登录");
    }
}

运行后

2021-04-08 16:27:41.740 3219-3219/com.cxz.demo E/ClickBehaviorAspect: 》》》》》》》》》》》》》》》》》》》  开始执行
2021-04-08 16:27:41.740 3219-3219/com.cxz.demo E/HomeActivity: --------------------->购物车
2021-04-08 16:27:41.742 3219-3219/com.cxz.demo E/ClickBehaviorAspect: 开始了开始了,方法:HomeActivity  行为:购物车  需要的时间:0ms  
2021-04-08 16:27:41.742 3219-3219/com.cxz.demo E/ClickBehaviorAspect: 》》》》》》》》》》》》》》》》》》》  执行结束

补充 这个是和接口 来看一下和注解一起使用的

注解


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAnnotation {

}

Aspect中的写法

   @Pointcut("execution(@com.cxz.demo.UserAnnotation * *(..))")
    public void methodPointCut() {
    }

界面中使用

    @UserAnnotation
    public void shopCart() {
        Log.e(TAG, "--------------------->购物车");
    }
    @UserAnnotation
    public void pointsMall() {
        Log.e(TAG, "--------------------->积分商城");
    }

运行结果

2021-04-13 18:10:37.629 25254-25254/com.cxz.demo E/ClickBehaviorAspect: 》》》》》》》》》》》》》》》》》》》  开始执行
2021-04-13 18:10:37.631 25254-25254/com.cxz.demo E/ClickBehaviorAspect: 开始了开始了,方法:HomeActivity  行为:shopCart  需要的时间:0ms  
2021-04-13 18:10:37.631 25254-25254/com.cxz.demo E/ClickBehaviorAspect: 》》》》》》》》》》》》》》》》》》》  执行结束

nice 这段时间的相关学习到这就差不多了,更多的需要项目中实现

上一篇下一篇

猜你喜欢

热点阅读