我爱编程

Spring学习笔记(六、Spring AOP基本概念)

2017-06-18  本文已影响974人  鲁克巴克诗

上一篇:Spring学习笔记(五、Bean装配(下))

一、AOP概念

1. 什么是AOP?

2. AOP实现方式

3. AOP相关概念

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

4. Advice的类型

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

5. Spring框架中AOP的用途

6. Spring的AOP实现

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

二、配置切面aspect

1. 基于Schema-based的AOP实现

示例

  1. 创建切面类MoocAspect :
package test14;
/**
 * Created by amber on 2017/6/18.
 * 切面类
 */
public class MoocAspect {
}
  1. 创建目标类
package test14;
/**
 * Created by amber on 2017/6/18.
 */
public class AspectBiz {
}
  1. ApplicationContext中配置:
    在<beans>中声明命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-context.xsd"
 <aop:config>
        <aop:aspect id="myAspect" ref="moocAspect"></aop:aspect>
    </aop:config>
    <bean id="aspectBiz" class="test14.AspectBiz"></bean>
    <bean id="moocAspect" class="test14.MoocAspect"/>

2、配置切入点Pointcut

Paste_Image.png
execution(public **(..)) 
切入点为执行所有public方法时
execution(* set*(..)) 
切入点为执行所有set开始的方法时
execution(* com.xyz.service.AccountService.*(..)) 
切入点为执行AccountService类中所有方法时
execution(* com.xyz.service..(..)) 
切入点为执行 com.xyz.service包下所有方法时
execution(* com.xyz.service...(..)) 
切入点为执行 com.xyz.service包下及其子包下所有方法时
within(com.xyz.service.*) (仅SpringAOP)
within(com.xyz.service..*) (仅SpringAOP)
within用于匹配指定类型内的方法执行
this(com.xyz.service.AccountService) (仅SpringAOP)
this用于匹配当前AOP代理对象类型的执行方法
target(com.xyz.service.AccountService) (仅SpringAOP)
target用于匹配当前目标对象类型的执行方法
args(java.io.Serializable) (仅SpringAOP)
args用于匹配当前执行的方法传入的参数为指定类型的执行方法
@target(org.springframework.transacation.annotation.Transcactional) (仅SpringAOP)
@within(org.springframework.transacation.annotation.Transcactional) (仅SpringAOP)
@annotation(org.springframework.transacation.annotation.Transcactional) (仅SpringAOP)
按照注解类型匹配
@args(com.xyz.security.Classified)(仅SpringAOP)
bean(tradeService)(仅SpringAOP)
bean(*Service)(仅SpringAOP)

配置切入点:

   <aop:config>
        <aop:aspect id="myAspect" ref="moocAspect">
            <aop:pointcut id="moocPointCut" expression="execution(* test14.*Biz(..))"/>
        </aop:aspect>
    </aop:config>

三、advice

** Before Advice(前置通知)**
在写测试之前呢,要现在下载好需要的jar包。

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.10</version>
        </dependency>
  1. 创建了业务类AspectBiz:
package test14;
/**
 * Created by amber on 2017/6/18.
 * 业务类
 */
public class AspectBiz {
   public void biz(){
       System.out.println("AspectBiz biz.");
   }
}
  1. 创建切面类MoocAspect:
package test14;
/**
 * Created by amber on 2017/6/18.
 * 切面类
 */
public class MoocAspect {
    public void before(){
        System.out.println("MoocAspect 前置通知,before");
    }
}
  1. applicationContext中:
<bean id="aspectBiz" class="test14.AspectBiz"></bean>
 <bean id="moocAspect" class="test14.MoocAspect"/>
    <aop:config>
        <aop:aspect id="moocAspectAop" ref="moocAspect">
            <aop:pointcut id="moocPointCut" expression="execution(* test14.*Biz.*(..))"/>
            <aop:before method="before" pointcut-ref="moocPointCut"/>
        </aop:aspect>
    </aop:config>
  1. 测试类:
  @Test
    public void test14() {
        AspectBiz aspectBiz=super.getBean("aspectBiz");
        aspectBiz.biz();
    }
  1. 结果:


    Paste_Image.png

** After returning advice(返回后通知)**

  1. 更新MoocAspect类:
package test14;
/**
 * Created by amber on 2017/6/18.
 * 切面类
 */
public class MoocAspect {
    public void before(){
        System.out.println("MoocAspect 前置通知,before");
    }
    public void afterReturning(){
        System.out.println("MoocAspect 返回后通知,afterReturning");
    }
}
  1. 更新applicationContext:
 <aop:config>
        <aop:aspect id="moocAspectAop" ref="moocAspect">
            <aop:pointcut id="moocPointCut" expression="execution(* test14.*Biz.*(..))"/>
            <aop:before method="before" pointcut-ref="moocPointCut"/>
            <aop:after-returning method="afterReturning" pointcut-ref="moocPointCut"/>
        </aop:aspect>
    </aop:config>
  1. 结果:


    Paste_Image.png

** After throwing advice(抛出异常后通知)**
注:可使用throwing属性指定可被传递的异常参数名称

  1. 修改业务类AspectBiz:
package test14;
/**
 * Created by amber on 2017/6/18.
 * 业务类
 */
public class AspectBiz {
   public void biz(){
       System.out.println("AspectBiz biz.");
      throw  new RuntimeException();
   }
}
  1. 更新切面类MoocAspect :
package test14;
/**
 * Created by amber on 2017/6/18.
 * 切面类
 */
public class MoocAspect {
    public void before(){
        System.out.println("MoocAspect 前置通知,before");
    }
    public void afterReturning(){
        System.out.println("MoocAspect 返回后通知,afterReturning");
    }
    public void afterThrowing(){
        System.out.println("MoocAspect 抛出异常后通知,afterThrowing");
    }
}
  1. 更新applicationContext:
 <aop:config>
        <aop:aspect id="moocAspectAop" ref="moocAspect">
            <aop:pointcut id="moocPointCut" expression="execution(* test14.*Biz.*(..))"/>
            <aop:before method="before" pointcut-ref="moocPointCut"/>
            <aop:after-returning method="afterReturning" pointcut-ref="moocPointCut"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="moocPointCut"/>
        </aop:aspect>
    </aop:config>
  1. 结果:


    Paste_Image.png

** After(finally) advice(后通知)**

  1. 更新切面类MoocAspect:
package test14;
/**
 * Created by amber on 2017/6/18.
 * 切面类
 */
public class MoocAspect {
    public void before(){
        System.out.println("MoocAspect 前置通知,before");
    }
    public void afterReturning(){
        System.out.println("MoocAspect 返回后通知,afterReturning");
    }
    public void afterThrowing(){
        System.out.println("MoocAspect 抛出异常后通知,afterThrowing");
    }
    public void after(){
        System.out.println("MoocAspect 后通知,after");
    }
}
  1. 更新applicationContext:
  <aop:config>
        <aop:aspect id="moocAspectAop" ref="moocAspect">
            <aop:pointcut id="moocPointCut" expression="execution(* test14.*Biz.*(..))"/>
            <aop:before method="before" pointcut-ref="moocPointCut"/>
            <aop:after-returning method="afterReturning" pointcut-ref="moocPointCut"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="moocPointCut"/>
            <aop:after method="after" pointcut-ref="moocPointCut"/>
        </aop:aspect>
    </aop:config>
  1. 结果:
Paste_Image.png

** Around advice(环绕通知)**
注:通知方法的必须有参数,且第一个参数必须是ProceedingJoinPoint类型。

  1. 修改AspectBiz 业务类,讲抛出异常代码删掉。
package test14;
/**
 * Created by amber on 2017/6/18.
 * 业务类
 */
public class AspectBiz {
   public void biz(){
       System.out.println("AspectBiz biz.");
   }
}
  1. 更新MoocAspect类:
package test14;
import org.aspectj.lang.ProceedingJoinPoint;
/**
 * Created by amber on 2017/6/18.
 * 切面类
 */
public class MoocAspect {
    public void before(){
        System.out.println("MoocAspect 前置通知,before");
    }
    public void afterReturning(){
        System.out.println("MoocAspect 返回后通知,afterReturning");
    }
    public void afterThrowing(){
        System.out.println("MoocAspect 抛出异常后通知,afterThrowing");
    }
    public void after(){
        System.out.println("MoocAspect 后通知,after");
    }
    public Object around(ProceedingJoinPoint pjp){
        Object obj= null;
        try {
            System.out.println("MoocAspect 环绕通知,around1");
            obj = pjp.proceed();
            System.out.println("MoocAspect 环绕通知,around2");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return obj;
    }
}
  1. applicationContext:
  <aop:config>
        <aop:aspect id="moocAspectAop" ref="moocAspect">
            <aop:pointcut id="moocPointCut" expression="execution(* test14.*Biz.*(..))"/>
            <aop:before method="before" pointcut-ref="moocPointCut"/>
            <aop:after-returning method="afterReturning" pointcut-ref="moocPointCut"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="moocPointCut"/>
            <aop:after method="after" pointcut-ref="moocPointCut"/>
            <aop:around method="around" pointcut-ref="moocPointCut"/>
        </aop:aspect>
    </aop:config>
  1. 结果:
Paste_Image.png

** Advice parameters(通知参数)**

  1. 更新AspectBiz 业务类,增加一个带参的方法,init。
package test14;
import test12.StringStore;
/**
 * Created by amber on 2017/6/18.
 * 业务类
 */
public class AspectBiz {
   public void biz(){
       System.out.println("AspectBiz biz.");
   }
    public void init(String bizName, int times){
        System.out.println("AspectBiz init: "+bizName+" "+times);
    }
}
  1. 更新MoocAspect 切面类,增加带参数环绕通知方法aroundInit
package test14;
import org.aspectj.lang.ProceedingJoinPoint;
import test12.StringStore;
/**
 * Created by amber on 2017/6/18.
 * 切面类
 */
public class MoocAspect {
    public void before(){
        System.out.println("MoocAspect 前置通知,before");
    }
    public void afterReturning(){
        System.out.println("MoocAspect 返回后通知,afterReturning");
    }
    public void afterThrowing(){
        System.out.println("MoocAspect 抛出异常后通知,afterThrowing");
    }
    public void after(){
        System.out.println("MoocAspect 后通知,after");
    }
    public Object around(ProceedingJoinPoint pjp){
        Object obj= null;
        try {
            obj = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return obj;
    }
    public Object aroundInit(ProceedingJoinPoint pjp, String bizName,int times){
        System.out.println("MoocAspect 环绕通知,bizName: "+bizName+" times: "+times);
        Object obj= null;
        try {
            System.out.println("MoocAspect 环绕通知,around1");
            obj = pjp.proceed();
            System.out.println("MoocAspect 环绕通知,around2");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return obj;
    }
}
  1. appclicationContext:
  <aop:config>
        <aop:aspect id="moocAspectAop" ref="moocAspect">
            <aop:pointcut id="moocPointCut" expression="execution(* test14.*Biz.*(..))"/>
            <aop:before method="before" pointcut-ref="moocPointCut"/>
            <aop:after-returning method="afterReturning" pointcut-ref="moocPointCut"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="moocPointCut"/>
            <aop:after method="after" pointcut-ref="moocPointCut"/>
            <aop:around method="around" pointcut-ref="moocPointCut"/>
            <aop:around method="aroundInit" pointcut="execution(* test14.AspectBiz.init(String,int))
            and args(bizName,times)"/>
        </aop:aspect>
    </aop:config>
  1. 测试类:
  @Test
    public void test14() {
        AspectBiz aspectBiz=super.getBean("aspectBiz");
        aspectBiz.init("advice parameters",1);
    }
  1. 结果:


    Paste_Image.png

四、Introductions

  1. 新增接口Fit:
package test15;
/**
 * Created by amber on 2017/6/18.
 */
public interface Fit {
    void filter();
}
  1. 新增实现类FitImpl :
package test15;
/**
 * Created by amber on 2017/6/18.
 */
public class FitImpl implements Fit {
    public void filter() {
        System.out.print("FitImpl filter");
    }
}
  1. 更新applicationContext:
  <bean id="aspectBiz" class="test14.AspectBiz"></bean>
   <bean id="moocAspect" class="test14.MoocAspect"/>
    <aop:config>
        <aop:aspect id="moocAspectAop" ref="moocAspect">
            <aop:declare-parents types-matching="test14.*+" 
                                 implement-interface="test15.Fit" 
                                 default-impl="test15.FitImpl"/>
        </aop:aspect>
    </aop:config>
在<aop:declare-parents>标签中,types-matching是test14包下的所有类,implement-interface是Fit接口。
default-impl是默认的实现类FitImpl。
在测试类中 Fit fit=super.getBean("aspectBiz");
意思是给AspectBiz类增加了一个父类FitImpl 。
上面“提供了一个接口实现类来代表这些对象”,即用实现Fit接口的FitImpl类代表AspectBiz对象,
  1. 测试类:
 @Test
    public void test15() {
        Fit fit=super.getBean("aspectBiz");
        fit.filter();
    }
Paste_Image.png

最后注意:所有基于配置文件的Aspect,只支持单例模式。

五、Advisors

package com.myspring.app.aop;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.springframework.aop.MethodBeforeAdvice;
/**
 * 方法前置通知
 * @author Michael
 */
@Component//如果是自动装配,在定义切面的时候直接写在ref属性里就可以了
public class MyAdvice implements MethodBeforeAdvice{
    //如果使用aop:advisor配置,那么切面逻辑必须要实现advice接口才行!否则会失败!
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置通知");
    }
    //如果是<aop:aspect>配置,编写一般的方法就可以了,然后在切面配置中指定具体的方法名称!
    public void doBefore(JoinPoint point) throws Throwable {
    }
}

applicationContext(重点!!可以和<aop:aspect>对比着看):

    <aop:config>
        <aop:pointcut expression="(execution(* com.myspring.app.aop.TestPoint.*(..)))"  id="mypoint"/>
        <aop:advisor advice-ref="myAdvice " pointcut-ref="mypoint"/>
    </aop:config>

六、验证可以通过切面控制目标执行次数的示例:

创建ConrrentOperationExecutor 切面类。

package schema.advisors;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.dao.PessimisticLockingFailureException;
import test12.StringStore;
/**
 * Created by amber on 2017/6/18.
 */
public class ConrrentOperationExecutor {
    private int maxRetries;
    public void setMaxRetries(int maxRetries) {
        this.maxRetries = maxRetries;
    }
    public Object doCurrentOperation(ProceedingJoinPoint pjp)throws Throwable{
        int numAttempts=0;
        PessimisticLockingFailureException lockingFailureException;
        do{
            numAttempts++;
            System.out.println("Try times : "+numAttempts);
            try {
                return pjp.proceed();
            } catch (PessimisticLockingFailureException throwable) {
                lockingFailureException=throwable;
            }
        }while (numAttempts<this.maxRetries);
        System.out.println("Try error : "+numAttempts+"\n");
        throw lockingFailureException;
    }
}

创建InvokeService业务类:

package schema.advisors.service;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.stereotype.Service;
/**
 * Created by amber on 2017/6/18.
 */
@Service
public class InvokeService {
    public void invoke() {
        System.out.print("InvokeService invoke...");
    }
    public void invokeException() {
     throw new PessimisticLockingFailureException("");
    }
}

在ApplicationContext:

   <context:component-scan base-package="schema"></context:component-scan>

    <aop:config>
        <aop:aspect id="conrrentOperationRetry" ref="conrrentOperationExecutor">
            <aop:pointcut id="idempotentOperation" expression="execution(* schema.advisors.service.*.*(..))"/>
            <aop:around method="doCurrentOperation" pointcut-ref="idempotentOperation"/>
        </aop:aspect>

    </aop:config>

    <bean id="conrrentOperationExecutor" class="schema.advisors.ConrrentOperationExecutor">
        <property name="maxRetries" value="3"/>
    </bean>

测试方法:

  @Test
    public void schema() {
        InvokeService service=super.getBean("invokeService");
        service.invoke();
        System.out.println();
        service.invokeException();
    }

结果:


Paste_Image.png

下一篇:Spring学习笔记(七、Spring AOP API)

上一篇下一篇

猜你喜欢

热点阅读