Spring AOP:面向切面编程

4 篇文章 0 订阅
3 篇文章 0 订阅

常用术语

学习AOP之前,先了解AOP中常用术语

  1. 切面(Aspect)
    切面是一个关注点的模块化,如事务管理就是一个在JavaEE企业中应用最常见的界面。在企业应用编程中,首先需要通过分析,抽取出通用的功能,即“切面”。
    事务、日志、安全性的框架、权限都是切面
  2. 连接点(Joinpoint)
    连接点是执行程序过程中的特定的点。Spring框架只支持方法作为连接点,如方法调用之前、方法调用后、或者发生异常时等。
  3. 通知(Advice)
    通知就是切面的具体实现。通知将在切面的某个特定的连接点上执行动作,Spring中执行的动作往往就是调用某一个类的具体方法。例如:在保存订单的模块中,进行日志管理(一个切面),具体是在保存订单的方法执行之前(连接点)执行写日志(通知)的功能。其中,日志管理是很多模块中通用的功能,因此这就是一个切面,而具体是在保存订单之前执行日志保存,那么保存订单前这个点就是连接点,实现日志保存功能的类就是通知。
    就是切面中的方法。
  4. 切入点(Pointcut)
    切入点是连接点的集合,通知将在满足一个切入点表达式的所有连接点上运行。
    举例:
    在拦截器中,有一系列判断性的内容

    1
    if(method.equals("savePerson")||method.equals("updatePerson") ||method.equals("deletePerson")){ ... }

    满足了上面三个方法才能开启事务,这些判断条件就为切入点

  5. 引入(Introduction)
    引入的意思是在一个类中加入新的属性或者方法。
  6. 目标对象(Target Object)
    被一个或多个切面所通知的对象成为目标对象。
  7. AOP代理(AOP Proxy)
    AOP代理是AOP框架所生成的对象,该对象是目标对象的代理对象。代理对象能够在目标对象的基础上,在相应的连接点上调用通知。
  8. 织入(Weaving)
    把切面连接到其他的应用程序之上,创建一个被通知的对象的过程,被称为织入。

    AOP两种实现模式

    xml形式

    举例说明如何在applicationContext.xml中配置AOP(因为当前只负责介绍AOP实现模式,因此配置环境需要的jar包先不介绍)

    举例说明

    创建接口personDAO和实现类personDaoImpl
    1
    2
    3
    4
    5
    6
    public interface PersonDao {
    /**
    * 目标方法
    */

    public void savePerson();
    }
1
2
3
4
5
public class PersonDaoImpl implements PersonDao{
public void savePerson() {
System.out.println("save person");
}
}

事务处理类Transaction

1
2
3
4
5
6
7
8
9
public class Transaction {
public void beginTransaction(){
System.out.println("begin transaction");
}

public void commit(){
System.out.println("commit");
}
}

接下来最重要的就是AOP的配置文件,applicationContext.xml。通过配置AOP可以实现创建代理对象:代理对象的方法=目标方法+通知。将原本不相关的目标方法和通知结合起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="personDao" class="cn.zju.spring.PersonDaoImpl"></bean>
<bean id="transaction" class="cn.zju.spring.Transaction"></bean>

<aop:config>
<!--
切入点表达式
确定目标类
id 标示
-->

<aop:pointcut expression="execution(* cn.zju.spring.PersonDaoImpl.*(..))" id="perform"/>
<aop:aspect ref="transaction"> <!-- ref指向切面类 -->
<aop:before method="beginTransaction" pointcut-ref="perform"/> <!--前置通知 -->
<aop:after-returning method="commit" pointcut-ref="perform"/> <!--后置通知 -->
</aop:aspect>
</aop:config>

客户端代码

1
2
3
4
5
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
PersonDao personDao = (PersonDao)context.getBean("personDao");
personDao.savePerson();
}

实现原理

  1. 当启动Spring容器时,因为声明了两个bean,… 所以Spring容易会为这两个bean创建对象。
  2. 当Spring解析到aop:config的时候,解析切入点表达式,让切入点表达式和Spring容器中的bean做匹配
  3. 如果匹配成功则为该类创建代理对象:代理对象的方法=目标方法+通知
  4. 在客户端通过getBean在Spring容器中查找相应对象,如果有代理对象则返回代理对象,否则返回对象本身。

    切入点表达式

    Spring AOP中用户可能会经常使用execution切入点指示符。执行表达式的格式如下:
    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
    除了返回类型模式(上面代码片断中的ret-type-pattern),名字模式和参数模式以外, 所有的部分都是可选的。返回类型模式决定了方法的返回类型必须依次匹配一个连接点。 你会使用的最频繁的返回类型模式是*,它代表了匹配任意的返回类型。举例说明如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    execution(public * *(..))
    任意的公共方法

    execution(* set*(..))
    以set开头的所有的方法

    execution(* com.xyz.service.AccountService.*(..))
    com.xyz.service.AccountService这个类里的所有的方法

    execution(* com.xyz.service.*.*(..))
    com.xyz.service包下的所有的类的所有的方法

    execution(* com.xyz.service..*.*(..))
    com.xyz.service包及子包下所有的类的所有的方法

    excution(* cn.itheima06.spring.aop..*.*(String,?,Long))
    cn.itheima06.spring.aop包及子包下所有的类的有三个参数,第一个参数为String类型,第二个参数为任意类型,第三个参数为Long类型的方法

AOP中的各种通知

  1. 前置通知
    在目标方法执行之前执行
    通知中有一个参数JoinPoint,可以获取目标方法的一些信息
  2. 后置通知
    在目标方法执行之后执行
    可以获取目标方法的返回值
    如果目标方法遇到异常,将不执行
  3. 异常通知
    获取目标方法抛出的异常信息
  4. 最终通知:相当于代码中的finally
  5. 环绕通知:
    能控制目标方法的执行,如果做权限控制,可以在这里进行判断。
    举例说明,在上述例子中,我们修改事务类transaction和配置文件来实现各种通知的说明,切面类和目标方法都不需要做改变,
    事务处理类transaction
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    public class Transaction {
    /**
    * 前置通知
    * JoinPoint连接点
    */

    public void beginTransaction(JoinPoint joinPoint){
    System.out.println("args:"+joinPoint.getArgs());//获取目标方法的参数
    //获取方法的名称
    System.out.println("methodName:"+joinPoint.getSignature().getName());
    System.out.println("begin transaction");
    }

    /**
    * 后置通知
    * @param joinPoint
    * @param val
    */

    public void commit(JoinPoint joinPoint,Object val){
    System.out.println(val);
    System.out.println("commit");
    }

    /**
    * 异常通知
    */

    public void throwingMethod(JoinPoint joinPoint,Throwable ex){
    System.out.println(ex.getMessage());
    }

    /**
    * 最终通知
    */

    public void finallyMethod(){
    System.out.println("finally method");
    }

    /**
    * 环绕通知
    * 控制目标方法的执行
    */

    public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
    String methodName = joinPoint.getSignature().getName();//获取目标方法的名称
    if(methodName.equals("savePerson")){
    joinPoint.proceed();//调用目标方法
    }else{
    System.out.println("权限不足");
    }
    }
    }

接下来最重要的就是AOP的配置文件,applicationContext.xml,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<bean id="personDao" class="cn.zju.spring.PersonDaoImpl"></bean>
<bean id="transaction" class="cn.zju.spring.Transaction"></bean>

<aop:config>
<!--
切入点表达式
确定目标类
id标示
-->

<aop:pointcut expression="execution(* cn.zju.spring.PersonDaoImpl.*(..))" id="perform"/>
<aop:aspect ref="transaction"><!-- ref指向切面类 -->
<!--
前置通知
1、在目标方法执行之前完成
2、有一个参数Joinpoint
该参数能够获取目标方法的一些信息
-->

<!--
<aop:before method="beginTransaction" pointcut-ref="perform"/>
-->

<!--
后置通知
1、在目标方法执行之后完成
2、有一个参数Joinpoint
3、returning属性可以获取目标方法的返回值
4、如果目标方法遇到异常,则后置通知将不再执行
-->

<!--
<aop:after-returning method="commit" pointcut-ref="perform" returning="val"/>
-->

<!--
异常通知
throwing
参数的名称 ex
获取目标方法抛出的异常信息
-->

<!--
<aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/>
-->

<!--
最终通知
无论目标方法是否遇到异常,最终通知都要执行
-->

<!--
<aop:after method="finallyMethod" pointcut-ref="perform"/>
-->

<!--
环绕通知
-->

<aop:around method="aroundMethod" pointcut-ref="perform"/>
</aop:aspect>
</aop:config>
在后续博客中会有实际案例,通过案例可以更加清楚各种的通知的用法。

注解形式

在上述例子中,我们修改事务类transaction和配置文件来实现各种通知的说明,切面类和目标方法都不需要做改变,
事务处理类transaction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class Transaction {
@Pointcut("execution(* cn.zju.spring.PersonDaoImpl.*(..))")
private void anyMethod(){}//定义一个切入点

@Before("anyMethod() && args(joinPoint)")
public void beginTransaction(JoinPoint joinPoint){
System.out.println("args:"+joinPoint.getArgs());//获取目标方法的参数
//获取方法的名称
System.out.println("methodName:"+joinPoint.getSignature().getName());
System.out.println("begin transaction");
}

@AfterReturning("anyMethod()")
public void commit(JoinPoint joinPoint,Object val){
System.out.println(val);
System.out.println("commit");
}

/**
* 异常通知
*/

@AfterThrowing("anyMethod()")
public void throwingMethod(JoinPoint joinPoint,Throwable ex){
System.out.println(ex.getMessage());
}

/**
* 最终通知
*/

@After("anyMethod()")
public void finallyMethod(){
System.out.println("finally method");
}

/**
* 环绕通知
* 控制目标方法的执行
*/

@Around("anyMethod()")
public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
String methodName = joinPoint.getSignature().getName();//获取目标方法的名称
if(methodName.equals("savePerson")){
joinPoint.proceed();//调用目标方法
}else{
System.out.println("权限不足");
}
}
}

然后下面的配置文件就比较简单了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">


<aop:aspectj-autoproxy/>
<bean id="personDao" class="cn.zju.spring.PersonDaoImpl"></bean>
<bean id="transaction" class="cn.zju.spring.Transaction"></bean>
</beans>
 

 original link: http://longliqiang88.github.io/2015/08/18/Spring%E5%AD%A6%E4%B9%A04%EF%BC%9ASpring%20AOP%EF%BC%88%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B%EF%BC%89/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值