美文网首页
写给自己的AOP简单实现

写给自己的AOP简单实现

作者: 无良安生 | 来源:发表于2021-04-08 16:32 被阅读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 这段时间的相关学习到这就差不多了,更多的需要项目中实现

    相关文章

      网友评论

          本文标题:写给自己的AOP简单实现

          本文链接:https://www.haomeiwen.com/subject/wsjvkltx.html