Java-Spring-AOP 实现

AOP即面向切面编程,是针对OOP编程编程的不足之处的补充计划。OOP通过引入封装,多态,继承等概念建立一种对象的层次结构,用于模拟公共行为的一个集合。

不过OOP定义的是对象的纵向关系,顶部总是高度抽象化的对象,底部总是具体的实现。但OOP不能适应定义横向的关系。比如日志功能,异常的处理,请求的安全检查等等,这些功能一般都存在散布在各个层次的代码中,又与具体的功能没有什么关联。在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP恰恰相反,它是定义了对象的横向关系,剖开对象的内部,讲那些会影响多个类的公共行为封装到单独的模块。这个模块称之为切面,原本分散的代码称之为横切。所谓的横切就是与业务逻辑无关的,却被多个业务所调用的逻辑的封装,以便减少代码冗余。

AOP把代码分成2个部分:核心关注点和横切关注点。核心关注点主要是功能部分,也就是业务逻辑代码块。与之关系不大的就是横切关注点。同一个横切关注点一般重复出现在核心关注点的某处,比如比如权限认证、日志、事物。将横切剥离出来,也就是把应用业务逻辑和系统服务分开。使得业务逻辑更加的纯粹。

Java-Spring-AOP 实现-我的技术分享




核心概念

横切关注点

需要对那些方法进行拦截,进行怎么样的处理。

切面

具体的是对横切剥离后的公共模块,是横切关注点的抽象。

连接点

被拦截的位置。一般指的是具体的方法。

切入点

对连接点进行拦截的定义。

通知

拦截到连接点之后要执行的代码

目标对象

代理的目标对象

织入

将切面应用到目标对象并导致代理对象创建的过程

引入

运行期为类动态地添加一些方法或字段


基于aspectj框架实现最简单的aop

1.在pom中添加aspectj依赖

 <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
           <version>1.8.5</version>
 </dependency>

2.添加切面类

package com.demo.oa.test;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


@Aspect
public class AspectModule {
    // 配置切入点 是一个表达式 可以使用【within && || !】组合 这里表示user所有下一级类中的所有方法
    // 切入点可能是一个普通的方法,也可能是一个构造方法,也可能是读写变量的行为,方法可分为方法的执行和调用。。。
    @Pointcut("execution(* com.ywsoftware.oa.controller.user.*.*(..))")
    public void pointCut(){}
    
    // 本方法和下面的方法都使用Advice注解,指示代码在何时执行
    // Before 表示方法执行之前
    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint){
        System.out.println("AOP Before Advice...");
    }
    // After表示在方法执行之后执行
    @After("pointCut()")
    public void doAfter(JoinPoint joinPoint){
        System.out.println("AOP After Advice...");
    }
    //AfterReturning表示在方法返回结果之后返回,可以访问方法的返回值
    @AfterReturning(pointcut="pointCut()",returning="returnVal")
    public void afterReturn(JoinPoint joinPoint,Object returnVal){
        System.out.println("AOP AfterReturning Advice:" + returnVal);
    }
    //AfterThrowing表示关联的方法若抛出异常则执行,可以访问该异常,并进行操作
    @AfterThrowing(pointcut="pointCut()",throwing="error")
    public void afterThrowing(JoinPoint joinPoint,Throwable error){
        System.out.println("AOP AfterThrowing Advice..." + error);
        System.out.println("AfterThrowing...");
    }
    //around表示围绕着方法进行执行,上述四种的功能都能实现,其必须有返回值
    @Around("pointCut()")
    public void around(ProceedingJoinPoint pjp){
        System.out.println("AOP Aronud before...");
        try {
            pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("AOP Aronud after...");
    }
}

Tips:
切入点也是可以自定义的,可以让我们更加精确的切入一个或多个指定的切入点。
首先定义一个注解(也可以是个接口之类的)

@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
     public @interface DebugTrace {
}

在需要插入代码的地方使用这个注解

    @DebugTrace
    public void logTest() {
        Log.e(TAG, "log test");
    }

然后创建切入代码

@Pointcut("execution(@com.demo.oa.test.DebugTrace * *..*.*(..))")
public void DebugTraceMethod() {}

@Before("DebugTraceMethod()")
public void beforeDebugTraceMethod(JoinPoint joinPoint) throws Throwable {
    String key = joinPoint.getSignature().toString();
    Log.e(TAG, "beforeDebugTraceMethod: " + key);
}

这个时候,所有使用@DebugTrace 注解的方法都会被代理。

3.注册bean 并启用aspectj

    <bean id="log" class="com.test.oa.test.AspectModule"/>
    <aop:aspectj-autoproxy/>

4.调用结果

18:47:53 DEBUG - Returning cached instance of singleton bean 'log'
AOP Aronud before...
AOP Before Advice...
AOP Aronud after...
AOP After Advice...
AOP AfterReturning Advice:null