什么是AOP & AspectJ AOP

一、为什么要用AOP?什么是AOP?
  • 为什么要用AOP?

      面向对象的特点是继承、封装和多态,而封装要求将功能分散到不同的对象中,伴随着业务系统越来越复杂,我们的核心业务中会参杂一些特殊业务,比如日志记录、权限验证、性能检测、错误检测等,这些外围操作会带来核心业务代码混乱、重复冗余逻辑散布、代码扩展困难等种种问题。

      我们希望这些模块能够具有热插拔特性,而不需要将这些外围代码入侵到核心模块中,假如把日志、权限、性能监测、错误检测等外围业务看作单独的关注点,每个关注点都可以在需要的时候被运用而且无需整合到核心代码中,如下图,每个关注点与核业务模块分离,作为单独的功能,横切几个核心业务模块,核心模块只需关注自己相关的业务。这些关注点叫横切关注点,这种技术叫面向切面编程,即AOP(Aspect Oriented Program)。

  • 什么是AOP?

      AOP从本质上来说是将那些重复的代码从业务逻辑中剥离出来,独立封装成一个类和若干方法,这样剥离出来的类和方法称为横切逻辑和增强,剥离后的业务逻辑的类和方法称为目标逻辑,剥离的位置称为切点。此外,增强有不同的增强类型,AOP编程就是在正确的切点使用正确的增强类型,将横切逻辑织入目标逻辑中。

  • 什么是AOP的静态织入与动态织入?

      在AOP中,将切面(aspect)应用到目标函数(类)的过程叫做织入,织入的过程分为两类:静态织入和动态织入。动态织入的方式是在运行时动态地将要增强的代码织入到目标类中,一般通过动态代理技术完成,如Java JDK动态代理或CGLIB动态代理,Spring AOP就采用这种动态代理技术;静态织入是在编译期将代码织入到目标类中,AspectJ采用静态织入方式,AspectJ的织入依赖acj编译器。

二、AspectJ AOP Demo

  AspectJ是一个Java实现的AOP框架,也是目前实现AOP框架中最成熟、功能最丰富的语言,AspectJ语言与Java完全兼容,AspectJ编译器能够对Java代码进行AOP编译,让Java代码具有AOP功能,这个过程一般是在编译期进行。此外,除了使用AspectJ特殊的语言外,AspectJ还支持原生的Java语言,只需加上AspectJ注解。因此使用AspectJ有两种方式:1.完全使用AspectJ语言;2.使用纯Java语言,然后使用AspectJ注解。

  • AspectJ AOP简单Demo

      创建maven项目并引入AspectJ依赖包

...
    <dependency>
        <groupId>aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.6.1</version>
    </dependency>
...

  同时引入AspectJ maven编译插件(注:不引入无法正确变异AspectJ文件)

...
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.8</version>
            </plugin>
        </plugins>
    </build>
...

  创建测试类,即目标逻辑

public class AspectJDemo {

    public void sayHello() {
        System.out.println("hello world");
    }

    public Object doErr(Integer i) {
        if(i.equals(0)) {
        }
        return null;
    }

    public static void main(String ... args) {
        AspectJDemo demo = new AspectJDemo();
        demo.sayHello();
        System.out.println(demo.doErr(1));
    }
}

  使用AspectJ语言创建横切逻辑(注:引入AspectJ包后才能创建),AspectJ类后缀为.aj

/**
 * 记录日志切面
 */
public aspect AspectJRecordLog {

    /**
     * 定义切点:日志记录切点
     */
    pointcut recordLog() : call(public * sayHello(..));

    /**
     * 前置通知
     */
    before() : recordLog() {
        System.out.println("before record log");
    }

    /**
     * 后置通知
     */
    after() : recordLog() {
        System.out.println("after record log");
    }
}

/**
 * 异常处理切面
 */
public aspect AspectHandleErr {

    /**
     * 处理异常
     */
    pointcut handleErr() : call(public * com.xiaofan.aop.AspectJDemo.doErr(..));

    /**
     * 环绕通知
     */
    Object around() : handleErr() {
        try {
            System.out.println("before proceed");
            Object result = proceed();
            System.out.println("after proceed correctly");
            return Integer.MAX_VALUE;
        } catch (Throwable e) {
            System.out.println("proceed and catch error");
        }
        return null;
    }
}

  使用Java语言,通过注解实现横切逻辑:

@Aspect
public class AspectJRecordLogJava {

    @Before("execution(* sayHello(..))")
    public void beforeSayHello(){
        System.out.println("before record log1");
    }

    @After("execution(* sayHello(..))")
    public void afterSayHello() {
        System.out.println("after record log");
    }
}

@Aspect
public class AspectJHandleErrJava {

    @Around("execution(* doErr(..))")
    public Object aroundDoErr(ProceedingJoinPoint joinPoint) {
            try {
                System.out.println("before proceed");
                System.out.println("joinPoint args : " + joinPoint.getArgs()[0]);
                System.out.println("joinPoint target : " + joinPoint.getTarget().getClass());
                Object result = joinPoint.proceed();
                System.out.println("after proceed correctly");
                return Integer.MAX_VALUE;
            } catch (Throwable e) {
                System.out.println("proceed and catch error");
            }
            return null;
    }
}

  运行测试类,返回结果

before record log1
hello world
after record log
before proceed
joinPoint args : 1
joinPoint target : class com.xiaofan.aop.AspectJDemo
after proceed correctly
2147483647
  • AspectJ AOP结合Annotation

      创建测试注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface TestAnnotation {
}

  创建横切面。注意这里需要制定 “execution(* *(..))”,让增强方法作用于注解方法本身,否则该增强方法将被同时织入到注解方法和调用点,导致重复织入问题(可反编译对比横切效果)。

@Aspect
public class AspectJAnnotation {

    @Before("execution(* *(..)) && @annotation(com.xiaofan.aop.TestAnnotation)")
    public void testAnnotation() {
        System.out.println("before test annotation");
    }
}

  创建测试目标逻辑:

public class AspectJDemo {

    @TestAnnotation
    public void sayHello() {
        System.out.println("hello world");
    }

    public static void main(String ... args) {
        AspectJDemo demo = new AspectJDemo();
        demo.sayHello();
    }
}

  测试输出

before test annotation
hello world
参考
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值