七、AOP基本概念

AOP 中必须明白的几个概念

1、切面(Aspect)

官方的抽象定义为“ 一个关注点的模块化,这个关注点可能会横切多个对象” 。“ 切面”在ApplicationContext 中来配置。
连接点(Joinpoint) :程序执行过程中的某一行为,例如,MemberService .get 的调用或者MemberService .delete 抛出异常等行为。

2、通知(Advice)

“切面”对于某个“连接点”所产生的动作。其中,一个“切面”可以包含多个“Advice”。

3、切入点(Pointcut)

匹配连接点的断言,在AOP 中通知和一个切入点表达式关联。切面中的所有通知所关注的连接点,都由切入点表达式来决定。

4、目标对象(Target Object)

被一个或者多个切面所通知的对象。例如,AServcieImpl 和BServiceImpl,当然在实际运行时,SpringAOP 采用代理实现,实际AOP 操作的是TargetObject 的代理对象。

5、AOP 代理(AOP Proxy)

在Spring AOP 中有两种代理方式,JDK 动态代理和CGLib 代理。默认情况下,TargetObject 实现了接口时,则采用JDK 动态代理,例如,AServiceImpl;反之,采用CGLib 代理,例如,BServiceImpl。强制使用CGLib 代理需要将的proxy-target-class 属性设为true。

通知(Advice)类型:

6、前置通知(Before Advice)

在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在里面使用元素进行声明。例如,TestAspect 中的doBefore 方法。

7、后置通知(After Advice)

当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext 中在里面使用元素进行声明。例如,ServiceAspect 中的returnAfter 方法,所以Teser 中调用UserService.delete 抛出异常时,returnAfter 方法仍然执行。

8、返回后通知(After Return Advice)

在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext 中在里面使用元素进行声明。

9、环绕通知(Around Advice)

包围一个连接点的通知,类似Web 中Servlet 规范中的Filter 的doFilter 方法。可以在方法的调用前后完成自定义的行为, 也可以选择不执行。ApplicationContext 中在 里面使用元素进行声明。例如,ServiceAspect 中的around 方法。

10、异常通知(After Throwing Advice)

在方法抛出异常退出时执行的通知。ApplicationContext 中在 里面使用元素进行声明。例如,ServiceAspect 中的returnThrow 方法。
注:可以将多个通知应用到一个目标对象上,即可以将多个切面织入到同一目标对象。

AOP的使用

使用Spring AOP 可以基于两种方式,一种是比较方便和强大的注解方式,另一种则是中规中矩的xml配置方式。

先说注解,使用注解配置Spring AOP 总体分为两步,第一步是在xml 文件中声明激活自动扫描组件功能,同时激活自动代理功能(来测试AOP 的注解功能):

为Aspect 切面类添加注解

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

//声明这是一个组件
@Component
//声明这是一个切面Bean
@Aspect
@Slf4j
public class AnnotaionAspect {
    //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
    @Pointcut("execution(* com.gupaoedu.vip.pattern.spring.aop.service..*(..))")
    public void aspect() {
    }

    /*
     * 配置前置通知,使用在方法aspect()上注册的切入点
     * 同时接受JoinPoint 切入点对象,可以没有该参数
     */
    @Before("aspect()")
    public void before(JoinPoint joinPoint) {
        log.info("before 通知" + joinPoint);
    }

    //配置后置通知,使用在方法aspect()上注册的切入点
    @After("aspect()")
    public void after(JoinPoint joinPoint) {
        log.info("after 通知" + joinPoint);
    }

    //配置环绕通知,使用在方法aspect()上注册的切入点
    @Around("aspect()")
    public void around(JoinPoint joinPoint) {
        long start = System.currentTimeMillis();
        try {
            ((ProceedingJoinPoint) joinPoint).proceed();
            long end = System.currentTimeMillis();
            log.info("around 通知" + joinPoint + "\tUse time : " + (end - start) + " ms!");
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            log.info("around 通知" + joinPoint + "\tUse time : " + (end - start) + " ms with exception :
                    " + e.getMessage());
        }
    }

    //配置后置返回通知,使用在方法aspect()上注册的切入点
    @AfterReturning("aspect()")
    public void afterReturn(JoinPoint joinPoint) {
        log.info("afterReturn 通知" + joinPoint);
    }

    //配置抛出异常后通知,使用在方法aspect()上注册的切入点
    @AfterThrowing(pointcut = "aspect()", throwing = "ex")
    public void afterThrow(JoinPoint joinPoint, Exception ex) {
        log.info("afterThrow 通知" + joinPoint + "\t" + ex.getMessage());
    }
}

测试代码

@ContextConfiguration(locations = {"classpath*:application-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class AnnotationTester {
    @Autowired
    MemberService annotationService;
    @Autowired
    ApplicationContext app;

    @Test
    // @Ignore
    public void test() {
        System.out.println("=====这是一条华丽的分割线======");
        AnnotaionAspect aspect = app.getBean(AnnotaionAspect.class);
        System.out.println(aspect);
        annotationService.save(new Member());
        System.out.println("=====这是一条华丽的分割线======");
        try {
            annotationService.delete(1L);
        } catch (Exception e) {
        //e.printStackTrace();
        }
    }
}

简单说一下xml 配置方式




    
    
        
        
        
        
        
        
        
    

简单地介绍一下切入点表达式的配置规则

通常情况下,表达式中使用”execution“就可以满足大部分的要求。表达式格式如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?

modifiers-pattern:方法的操作权限
ret-type-pattern:返回值 (必须)
declaring-type-pattern:方法所在的包
name-pattern:方法名 (必须)
parm-pattern:参数名
throws-pattern:异常

你可能感兴趣的:(七、AOP基本概念)