Spring-AOP
标签:Spring

AOP

1. 什么是AOP

AOP:Aspect Oriented Programming的缩写,意为:面向切面编程,同预编译方式运行时动态代理实现程序功能的统一维护的一种技术。

主要功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。

各个功能是横向的,切面与各个功能之间是垂直的。

1.2 AOP实现方式

1.3 AOP几个相关概念

名称 说明
切面 (Aspect) 一个关注点的模块化,这个关注点可能会横切多个对象
连接点 (Joinpoint) 程序运行过程中的某个特定的点
通知 (Advice) 在切面的某个特定的连接点上执行的动作
切入点 (Pointcut) 匹配连接点的断言,在AOP的通知和一个切入点表达式关联
引入 (Introduction) 在不修改类代码的前提下,为类添加新的方法和属性
目标对象 (Target Object) 被一个或多个切面所通知的对象
AOP代理 (AOP Proxy) AOP框架创建的对象,用来实现切面的契约(aspect contract)(包括通知方法执行等功能)
织入 (Weaving) 把切面连接到其他应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入,类加载时织入,执行时织入

1.4 Advice 类型

名称 说明
前置通知 (Before Advice) 在某连接点(join point)之前执行的通知,但不能阻止连接点前的执行(除非它抛出一个异常)
返回后通知 (After returning advice) 在某连接点正常完成后执行的通知
抛出异常后通知 (After throwing advice) 在方法抛出异常退出时执行的通知
后通知 (After(finally) advice) 当某连接点退出的时候执行的通知(不论正常返回还是异常退出)
环绕通知 (Around advice) 包围一个连接点的通知

1.5 Spring框架中AOP的用途

1.6 SpringAOP的实现

1.7 有接口和无接口的Spring AOP实现区别

2. 配置切面Aspect

Spring所有的切面和通知器都必须放在一个<aop:config>内,可以配置包含多个<aop:config>元素,每一个<aop:config>可以包含pointcut,advisor和aspect元素。它们必须按照这个顺序进行声明<aop:config>风格的配置大量使用了Spring的自动代理机制。

<bean id="moocAspect" class="com.liuyao.aop.schema.MoocAspect"></bean>

<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">

    </aop:aspect>
</aop:config>

3. 配置切入点

Spring AOP和Aspect都支持的:

只有Spring AOP支持的

<bean id="aapectBiz" class="com.liuyao.aop.schema.biz.AapectBiz"></bean>

        <bean id="moocAspect" class="com.liuyao.aop.schema.MoocAspect"></bean>

        <aop:config>
            <aop:aspect id="moocAspectAOP" ref="moocAspect">
                <aop:pointcut id="moocPonit" expression="execution(* com.liuyao.aop.schema.biz.*Biz.*(..))"></aop:pointcut>
            </aop:aspect>
        </aop:config>

4. 配置Advice

4.1 Before

前置通知

<bean id="moocAspect" class="com.liuyao.aop.schema.MoocAspect"></bean>

<bean id="aspectBiz" class="com.liuyao.aop.schema.biz.AspectBiz"></bean>

<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:pointcut expression="execution(* com.liuyao.aop.schema.biz.*Biz.*(..))" id="moocPiontcut"/>
        <aop:before method="before" pointcut-ref="moocPiontcut"/>
    </aop:aspect>
</aop:config>xml
public class MoocAspect {
    public void before(){
        System.out.println("MoocAspect before");
    }
    
}

4.2 After Returning

返回通知

<bean id="moocAspect" class="com.liuyao.aop.schema.MoocAspect"></bean>

<bean id="aspectBiz" class="com.liuyao.aop.schema.biz.AspectBiz"></bean>

<aop:config>
    <aop:aspect id="moocAspectAOP" ref="moocAspect">
        <aop:pointcut expression="execution(* com.liuyao.aop.schema.biz.*Biz.*(..))" id="moocPiontcut"/>
        <aop:before method="before" pointcut-ref="moocPiontcut"/>
        <aop:after-returning method="afterReturning" pointcut-ref="moocPiontcut"/>
    </aop:aspect>
</aop:config>
public class MoocAspect {
    public void before(){
        System.out.println("MoocAspect before");
    }

    public void afterReturning() throws Throwable {
        System.out.println("MoocAspect afterReturning");
    }
}

4.3 After Throwing

使用throwing属性来指定可被传递的异常的参数名称。如果方法执行没有异常抛出,将不会执行,如果同时添加了 afterReturning 和 afterThrowing 那么有异常发生只会执行 afterThrowing 而不会执行 afterReturning 。

<aop:after-throwing method="afterThrowing" pointcut-ref="moocPiontcut"></aop:after-throwing>
public void afterThrowing(JoinPoint joinPoint) throws Throwable {
	System.out.println("MoocAspect afterThrowing");
}

4.4 After(finally)

相当于finally方法,一定会执行。

 <aop:after method="after" pointcut-ref="moocPiontcut"></aop:after>
    public void after(JoinPoint joinPoint) throws Throwable {
        System.out.println("MoocAspect after");
    }

4.5 Around

通知方法的第一个参数必须是ProceedingJoinPoint类型

<aop:around method="around" pointcut-ref="moocPiontcut"></aop:around>
  public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("around 1");
       Object obj=proceedingJoinPoint.proceed();
        System.out.println("around 2");
        return obj;
    }

4.6 Advice Parameters

 <aop:around method="aroundInit" pointcut="execution(* com.liuyao.aop.schema.biz.AspectBiz.init(String,int)) and args(bizName,times)"></aop:around>

    public Object aroundInit(ProceedingJoinPoint proceedingJoinPoint,String bizName,int times) throws Throwable {
        System.out.println(bizName+" "+times);
        System.out.println("aroundInit 1");
        Object obj=proceedingJoinPoint.proceed();
        System.out.println("aroundInit 2");
        return obj;
    }
public class AspectBiz {
    public void biz(){
        System.out.println("AspectBiz biz");
//        throw new RuntimeException();
    }

    public void init(String bizName,int times){
        System.out.println("AspectBiz init "+bizName+" "+times);
    }
}

@Test
public void testInit(){
    AspectBiz biz=super.getBean("aspectBiz");
    biz.init("liuyao",12);
}

5. Introductions

简介允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来代表这些对象。

<aop:aspect>中的 <aop:declare-parents>元素声明该元素用于声明所匹配的类型拥有一个新的parent (因此得名)

配置XML:

<aop:config>
        <aop:aspect id="moocAspectAOP" ref="moocAspect">
            <aop:declare-parents types-matching="com.liuyao.aop.schema.biz.*(+)" implement-interface="com.liuyao.aop.schema.Fit" default-impl="com.liuyao.aop.schema.FitImpl"></aop:declare-parents>
        </aop:aspect>
    </aop:config>

声明接口:

package com.liuyao.aop.schema;

/**
 * Created By liuyao on 2018/4/25 20:16.
 */
public interface Fit {
    void filter();
}

声明实现类:

package com.liuyao.aop.schema;

/**
 * Created By liuyao on 2018/4/25 20:17.
 */
public class FitImpl implements Fit {

    @Override
    public void filter() {
        System.out.println("FitImpl filter");
    }
}

测试:

   @Test
    public void testFit(){
        Fit fit=(Fit) super.getBean("aspectBiz");
        fit.filter();
    }

最后结果:输出实现了接口的实现类的方法。

所有基于配置文件的Aspect只支持Singleton模式

6. Advisors

advisor 就像一个小的自包含的方面,只有一个advice

切面自身通过一个bean表示,并且必须实现某个advice接口,同时,advisor也可以很好的利用AspectJ的切入点表达式

Spring通过配置文件中 <aop:advisor> 元素支持advisor,实际使用中,大多数 情况下它会和 transactional advice配合使用。

为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性来定义advisor的顺序。

7. AspectJ

7.1 Spring中配置@AspectJ

7.2 aspect

7.3 pointcut

@Pointcut("execution(* transfer(..))")
private void anyOldTransfer(){}
名称 说明
execution 匹配方法执行的连接点
within 限定匹配特定类型的连接点
this 匹配特定连接点的bean引用是指定类型的实例的限制
target 限定匹配特定连接点的目标对象是指定类型的实例
args 限定匹配特定连接点的参数是给点类型的实例
@target 限定匹配特定连接点的类执行对象的具有给定类型的注解
@args 限定匹配特定连接点实际传入参数的类型具有给定类型的注解
@annotation 限定匹配特定连接点的主体具有给给定的注解

7.3.1 组合pointcut

@Pointcut("execution(public * (..))")
private void A(){}

@Pointcut("within(com.liuyao..)")
private void B(){}

@Pointcut("A() && B()")
private void C(){}

7.3.2 定义良好的pointcuts

7.4 Before Advice

    @Before("execution(* com.liuyao.aop.aspectj.biz.*Biz.*(..))")
    public void before(){
        System.out.println("before");
    }

或者:

 @Pointcut("execution(* com.liuyao.aop.aspectj.biz.*Biz.*(..))")
    public void pointcut(){}

    @Before("pointcut()")
    public void before(){
        System.out.println("before");
    }

7.5 AfterReturning Advice

通过 returning 设置返回值

    @AfterReturning(pointcut = "within(com.liuyao.aop.aspectj.biz.*)",returning = "returnVal")
    public void afterReturning(Object returnVal){
        System.out.println("afterReturning "+returnVal);
    }

7.6 AfterThrowing Advice

通过throwing获得异常信息:

@AfterThrowing(pointcut = "pointcut()",throwing = "ex")
public void afterThrowing(RuntimeException ex){
    System.out.println("afterThrowing "+ex.getMessage());
}

7.7 After(finally) Advice

    @After("pointcut()")
    public void after(){
        System.out.println("after");
    }

7.7 Around Advice

@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("around 1");
    Object retVal=proceedingJoinPoint.proceed();
    System.out.println("around 2");
    return retVal;
}

7.8 Advice传递参数

    @Before("pointcut() && args(arg)")
    public void beforeWithParam(String arg){
        System.out.println("beforeWithParam "+arg);
    }
  • 12 min read

CONTRIBUTORS


  • 12 min read