zoukankan      html  css  js  c++  java
  • 初窥AspectJ

    AspectJ可以说是Java中当之无愧的黑魔法。说它是黑魔法,一方面是因为它很强大,能够解决一些传统编程方法论解决不了的问题,而另一方面,它也相当的晦涩,有着比较陡峭的学习曲线。

    本文将带大家探索下AspectJ是什么,能做什么,以及如何来做,希望通过本文能够让大家初窥AspectJ之门道

    AOP是什么

    相信很多人第一次听说AOP是在学习spring的时候,笔者也是。这个概念其实困扰了我很久,到底是AOP?AOP是Aspect Oriented Programming的缩写,和OOP(Object Oriented Programming)一样,都代表一种编程思想。不同的是,OOP是对世界万物的抽象,而AOP做的则是对业务处理过程的抽象,一定程度上说,AOP是OOP思想的一种延续,对程序进行了进一步的封装。

    那么AOP到底能够解决什么问题呢?以在现有系统上增加一个安全策略为例,我们需要在各个模块的代码中不同地方添加代码,进行安全策略的检查。这种方式实现起来很复杂,容易出错,而且没法复用。这里描述的安全问题就是一个横切关注点问题,开发者需要找到所有需要关注的代码断,在现有代码中间插入新的业务代码(就好像对现有代码做了切分)。类似这里安全策略的问题还有很多,比如tracing等。

    AspectJ基本概念

    AspectJ是AOP的Java实现版本,定义了AOP的语法,可以说是对Java的一个扩展。相对于Java,AspectJ引入了join point(连接点)的概念,同时引入三个新的结构,pointcut(切点), advice(通知),inter-type declaration(跨类型声明)以及aspect。其中pointcut和advice是AspectJ中动态额部分,用来指定在什么条件下切断执行,以及采取什么动作来实现切面操作。顾名思义,这里的pointcut就是用来定义什么情况下进行横切,而advice则是指横切情况下我们需要做什么操作,所以说pointcut和advice会动态的影响程序的运行流程。从某种角度上说,pointcut(切点)和我们平时用IDE调试程序时打的断点很类似,当程序执行到我们打的断点的地方的时候(运行到满足我们定义的pointcut的语句的时候,也就是join point连接点),我们可以执行一段脚本(执行advice中定义的行为)。

    而AspectJ中的inter-type declaration(跨类型声明)则是AspectJ中静态的部分,它影响的是程序的静态结构,比如成员变量,类之间的关系等。Aspect则是对前三个结构的封装,类似于java中的类。

    第一个AspectJ程序

    这里我们先不去具体探讨AspectJ的语法问题,而重点关注如何用AspectJ写一个简单的Demo。这里我用的开发环境是IntelliJ,且项目使用maven来构建。

    maven依赖

    要运行AspectJ程序,首先要引入AspectJ运行时的依赖:

    <!--aspectj runtime classes -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.9</version>
    </dependency>
    

    除了运行时依赖,还需要aspectjweaver.jar

    <!--to introduce aspect to java class in load time-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.9</version>
    </dependency>
    

    一个简单的类

    先写一个简单的类:

    package cc.databus.aspect;
    
    public class Account {
        double balance = 200;
    
        public boolean withdraw(int amount) {
            if (balance < amount) {
                return false;
            }
            balance = balance - amount;
            return true;
        }
    
        @Override
        public String toString() {
            return "Account{" +
                    "balance=" + balance +
                    '}';
        }
    }
    

    该类定义了一个Account类,并提供了withdraw(取款)的方法。

    aspect定义

    创建一个AccountAspect.aj文件来记录取款前后的信息:

    // aspect 
    package cc.databus.aspect;
    
    public aspect AccountAspect {
    
        // define a pointcut to pick up invoking Accont.withdraw 
        pointcut callWithDraw(int amount, Account account):
                call(boolean Account.withdraw(int)) 
                && args(amount) 
                && target(account);
    
        // advice definition executing before enterring method body
        before(int amount, Account acc): callWithDraw(amount, acc) {
            System.out.println("Start withdraw " + amount + " from " + acc);
        }
    
    
        after(int amount, Account acc) returning (Object ret): callWithDraw(amount, acc) {
            System.out.print("Finish withdraw, return " 
            + ret +", account after withdraw is: " +  acc);
        }
    }
    

    通过IntelliJ可以很方便的创建aspect文件,在包上面右键->New->Aspect:

    正如你所见,上面的AccountAspect.aj定义了AspectJ的pointcut,advice以及aspect。

    aspect织入(weaving)

    Weaving....很奇怪的词。。。这里指的是将aspect中定义的advice植入到运行时的过程。这里我们使用一个maven插件来讲aspect织如,这个插件是Mojo AspectJ Maven Plugin:

    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.7</version>
            <configuration>
                <complianceLevel>1.8</complianceLevel>
                <source>1.8</source>
                <target>1.8</target>
                <showWeaveInfo>true</showWeaveInfo>
                <verbose>true</verbose>
                <encoding>utf-8</encoding>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <!--use this goal to weave all your main classes-->
                        <goal>compile</goal>
                        <!--use this goal to weave all your test classes-->
                        <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
    

    OK,下面我们写一个UT来测试下aspect是否生效了:

    import cc.databus.aspect.Account;
    import org.junit.Before;
    import org.junit.Test;
    
    import static org.junit.Assert.assertFalse;
    import static org.junit.Assert.assertTrue;
    
    public class TestAccount {
    
        private Account account;
    
        @Before
        public void before() {
            account = new Account();
        }
    
        @Test
        public void given20AndMin10_whenWithdraw5_thenSuccess() {
            assertTrue(account.withdraw(5));
        }
        
        @Test
        public void given20AndMin10_whenWithdraw100_thenFail() {
            assertFalse(account.withdraw(100));
        }
    }
    
    

    运行测试,如果命令行有如下的输出,则表示aspect成功织入我们的代码,并且pointcut成功切入了Account.withdraw的调用:

    Start withdraw 5 from Account{balance=200.0}
    Finish withdraw, return true, account after withdraw is: Account{balance=195.0}
    Process finished with exit code 0
    

    总结

    总的来说,AspectJ是一个相当晦涩难懂的技术,但是不得不承认它很强大。本文在从理论出发,先介绍AOP以及AspectJ的基本概念,然后以一个简单的Demo程序介绍了如何在项目中使用AspectJ。

    文章同步发布在我的个人博客https://jianyuan.me上,欢迎拍砖。
    传送门: 初窥AspectJ

    个人博客地址: https://jianyuan.me
  • 相关阅读:
    stat函数讲解
    ptrace 人人小站
    调试器工作原理——基础篇
    open和fopen的区别:
    memset ,memcpy 和strcpy 的根本区别?
    log4j日志配置详解
    jvm调优的参数设置
    mysql的密码设置
    java基础类、接口、成员属性的修饰符
    java基础之类(包含内部类)与接口的特点
  • 原文地址:https://www.cnblogs.com/yflog/p/9582421.html
Copyright © 2011-2022 走看看