Spring中的AOP(三)——基于Annotation的配置方式(一)

    AspectJ允许使用注解用于定义切面、切入点和增强处理,而Spring框架则可以识别并根据这些注解来生成AOP代理。Spring只是使用了和AspectJ 5一样的注解,但并没有使用AspectJ的编译器或者织入器,底层依然使用SpringAOP来实现,依然是在运行时动态生成AOP代理,因此不需要增加额外的编译,也不需要AspectJ的织入器支持。而AspectJ采用编译时增强,所以AspectJ需要使用自己的编译器来编译Java文件,还需要织入器。

    为了启用Spring对@AspectJ切面配置的支持,并保证Spring容器中的目标Bean被一个或多个切面自动增强,必须在Spring配置文件中配置如下内容(第4、9、10、15行):

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<?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"

    xmlns:context="http://www.springframework.org/schema/context"

    xsi:schemaLocation="

        http://www.springframework.org/schema/beans 

        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

        http://www.springframework.org/schema/aop 

        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

        http://www.springframework.org/schema/context

            http://www.springframework.org/schema/context/spring-context-3.0.xsd">

             

    <!-- 启动@AspectJ支持 -->

    <aop:aspectj-autoproxy/>

</beans>

    所谓自动增强,指的是Spring会判断一个或多个切面是否需要对指定的Bean进行增强,并据此自动生成相应的代理,从而使得增强处理在合适的时候被调用。如果不打算使用XML Schema的配置方式,则应该在Spring配置文件中增加如下片段来启用@AspectJ支持(即上面的<aop:aspectj-autoproxy />和下面创建Bean的方式选择一种即可启用@AspectJ支持):

?


1

<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />

    上面配置的是一个Bean后处理器,该处理器将会为容器中Bean生成AOP代理。

    为了在Spring应用中启动@AspectJ支持,还需要在用用的类加载路径下增加两个AspectJ库:aspectweaver.jar和aspectjrt.jar,直接使用AspectJ安装路径下的lib目录下的这两个Jar文件即可,当然,也可以在Spring解压缩文件夹的lib/aspectj路径下找到它们。下面是项目内容的截图:

定义切面Bean

    当启用了@AspectJ支持后,只要我们在Spring容器中配置一个带@AspectJ注释的Bean,Spring将会自动识别该Bean,并将该Bean作为切面处理。下面是一个例子:

?


1

2

3

4

@Aspect

public class LogAspect {

 

}

    切面类(用@Aspect修饰的类)和其他类一样可以有方法和属性的定义,还可能包括切入点、增强处理的定义。当我们使用@Aspect来修饰一个Java类后,Spring将不会把该Bean当成组件Bean处理,因此当Spring容器检测到某个Bean使用了@AspectJ标注之后,负责自动增强的后处理Bean将会忽略该Bean,不会对该Bean进行任何增强处理。

使用Before增强处理

    当我们在一个切面类里使用@Before来标注一个方法时,该方法将作为Before增强处理。使用@Before标注时,通常需要指定一个value属性值,该属性值指定一个切入点表达式(既可以是一个已有的切入点,也可以直接定义切入点表达式),用于指定该增强处理将被织入哪些切入点。看例子:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

package com.abc.advice;

 

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

 

@Aspect

public class BeforeAdviceTest {

    //匹配com.abc.service下的类中以before开始的方法

    @Before("execution(* com.abc.service.*.before*(..))")

    public void permissionCheck() {

        System.out.println("模拟权限检查");

    }

}

    上面的程序使用@Aspect修饰了BeforeAdviceTest类,这表明该类是一个切面类,在该贴面里定义了一个permissionCheck方法——这个方法本来没有什么特殊之处,但因为使用了@Before来标注该方法,这就将该方法转换成一个Before增强处理。这个@Before注解中,直接指定了切入点表达式,指定com.abc.service包下的类中以before开始的方法的执行作为切入点。现假设我们在com.abc.service下有一个这样一个类:

?


1

2

3

4

5

6

7

8

9

10

package com.abc.service;

import org.springframework.stereotype.Component;

 

@Component

public class AdviceManager {

    //这个方法将被BeforeAdviceTest类的permissionCheck匹配到

    public void beforeAdvice() {

        System.out.println("方法: beforeAdviceTest");

    }

}

    从上面的代码来看,这个AdviceManager是一个纯净的Java类,它丝毫不知道将被谁来增强,也不知道将被进行怎样的增强——正式因为AdviceManager类的这种“无知”,才是AOP的最大魅力:目标类可以被无限的增强。

    在Spring配置文件中配置自动搜索Bean组件,配置自动搜索切面类,SpringAOP自动对Bean组件进行增强,下面是Spring配置文件代码:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

<?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"

    xmlns:context="http://www.springframework.org/schema/context"

    xsi:schemaLocation="

        http://www.springframework.org/schema/beans 

        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

        http://www.springframework.org/schema/aop 

        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

             

    <!-- 启动@AspectJ支持 -->

    <aop:aspectj-autoproxy/>

     

    <!-- 指定自动搜索Bean组件,自动搜索切面类 -->

    <context:component-scan base-package="com.abc.service,com.abc.advice">

        <context:include-filter type="annotation" 

            expression="org.aspectj.lang.annotation.Aspect" />

    </context:component-scan>

</beans>

    主程序非常简单,通过Spring容器获取AdviceManager Bean,并调用Bean的beforeAdvice方法:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package com.abc.main;

 

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

import com.abc.service.AdviceManager;

 

@SuppressWarnings("resource")

public class AOPTest {

    public static void main(String[] args) {

        ApplicationContext context = 

            new ClassPathXmlApplicationContext("applicationContext.xml");

        AdviceManager manager = context.getBean(AdviceManager.class);

        manager.beforeAdvice();

    }

}

    执行主程序,将看到以下结果:

    使用Before增强处理只能在目标方法执行之前织入增强,使用Before增强处理无需理会目标方法的执行,所以Before处理无法阻止目标方法的执行。Before增强处理执行时,目标方法还未获得执行机会,所以Before增强处理无法访问目标方法的返回值。

使用AfterReturning增强处理

    和使用@Before注解的使用类似,使用@AfterReturning来标注一个AfterReturning增强处理,该处理将在目标方法正常完成后被织入。使用@AfterReturning时可以指定两个属性:

  • pointcut/value:这两个属性的作用是一样的,都用于指定该切入点对应的切入表达式。同样的,既可以是一个已有的切入点,也可以是直接定义的切入点。当指定了pointcut属性后,value的属性值将会被覆盖
  • returning:指定一个返回值形参名,增强处理定义的方法可以通过该形参名来访问目标方法的返回值。

    在com.abc.advice包下面增加AfterReturningAdviceTest,这个类定义了一个AfterReturning增强处理:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

package com.abc.advice;

 

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Aspect;

 

@Aspect

public class AfterReturningAdviceTest {

    //匹配com.abc.service下的类中以afterReturning开始的方法

    @AfterReturning(returning="returnValue"

        pointcut="execution(* com.abc.service.*.afterReturning(..))")

    public void log(Object returnValue){

        System.out.println("目标方法返回值:" + returnValue);

        System.out.println("模拟日志记录功能...");

    }

}

    并在AdviceManager类中增加以下内容:

?


1

2

3

4

5

//将被AfterReturningAdviceTest的log方法匹配

public String afterReturning() {

    System.out.println("方法:afterReturning");

    return "afterReturning方法";

}

    正如上面程序中看到的,程序中使用@AfterReturning注解时,指定了一个returning属性,该属性的返回值是returnValue,这表明允许在增强方法log中使用名为returnValue的形参,该形参代表目标方法的返回值。在测试类AOPTest的main方法中增加调用本方法的语句,运行测试类,可以看到以下结果:

    @AfterReturning注解的returning属性所指定的形参名必须对应增强处理中的一个形参名,当目标方法执行以后,返回值作为相应的参数传入给增强处理方法。

    需要注意的是,使用@AfterReturning属性还有一个额外的作用,它可用于限定切入点之匹配具有对应返回值类型的方法——假设上面的log方法的参数returnValue的类型为String,那么该切入点只匹配com.abc.service.impl包下的返回值为String的所有方法。当然,上面的log方法返回值类型为Object,表明该切入点可匹配任何返回值的方法。除此之外,虽然AfterReturning增强处理可以访问到目标方法的返回值,但它不可改变这个返回值。

使用AfterThrowing增强处理

    使用@AfterThrowing注解可用于标注一个AfterThrowing增强处理,这个处理主要用于处理陈旭中未处理的异常。使用这个注解时可以指定两个属性:

  • pointcut/value:这两个属性的作用是一样的,都用于指定该切入点对应的切入表达式。同样的,既可以是一个已有的切入点,也可以是直接定义的切入点。当指定了pointcut属性后,value的属性值将会被覆盖
  • throwing:指定一个返回值形参名,增强处理定义的方法可通过该形参名来访问目标方法中所抛出的异常对象。

     在com.abc.advice包下面增加AfterThrowingAdviceTest,这个类定义了一个AfterThrowing增强处理:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

package com.abc.advice;

 

import org.aspectj.lang.annotation.AfterThrowing;

import org.aspectj.lang.annotation.Aspect;

 

@Aspect

public class AfterThrowingAdviceTest {

    @AfterThrowing(throwing="ex",

        pointcut="execution(* com.abc.service.*.afterThrow*(..))")

    public void handleException(Throwable ex) {

        System.out.println("目标方法抛出异常:" +ex);

        System.out.println("模拟异常处理");

    }

}

    并在AdviceManager类中增加以下内容:

?


1

2

3

4

5

6

7

8

9

10

11

//将被AfterThrowingAdviceTest的handleException方法匹配

public void afterThrowing() {

    System.out.println("方法: afterThrowing");

    try {

        int a = 10 0;

    catch (ArithmeticException ae) {

        System.out.println("算术异常已被处理");

    }

    String s = null;

    System.out.println(s.substring(0,3));

}

    正如上面程序中看到的,程序中使用@AfterThrowing注解时,指定了一个throwing属性,该属性的值是ex,这表明允许在增强方法log中使用名为ex的形参,该形参代表目标方法的抛出的异常对象。运行测试类,可以看到以下结果:

    需要注意的是:如果一个异常在程序内部已经处理,那么Spring AOP将不会处理该异常。只有当目标方法抛出一个未处理的异常时,该异常将会作为对应的形参传给增强处理的方法。和AfterReturning类似的是,正确方法的参数类型可以限定切点只匹配指定类型的异常——假如上面的handleException方法的参数类型为NullPointerException,那么如果目标方法只抛出了ArithmaticException,则Spring AOP将不会处理这个异常。当然,handleException的参数类型为Throwable,则匹配了所有的Exception。

    从测试结果中可以看到,AfterThrowing处理虽然可以对目标方法的异常进行处理,但这种处理与直接使用catch捕捉不同:catch捕捉意味着完全处理该异常,如果catch块中没有重新抛出新异常,则该方法可以正常结束;而AfterThrowing处理虽然处理了该异常,但它不能完全处理该异常,这个异常依然会传播到上一级调用者(本例中为JVM,故会导致程序终止)。

时间: 2024-09-02 14:12:02

Spring中的AOP(三)——基于Annotation的配置方式(一)的相关文章

Spring中的AOP(四)——基于Annotation的配置方式(二)

使用After增强处理     Spring还提供了一个After增强处理,它与AfterReturning优点类似,但也有区别: AfterReturning增强处理只有在目标方法正确完成后才会被织入 After增强处理不管目标方法如何结束(正确还是异常),它都会被织入     正是因为这个特点,因此After增强处理必须准备处理正常返回和异常返回两种情况,这种增强处理通常用于释放资源.使用@After注解标注一个方法,即可将该方法转换为After增强处理.使用@After注解是需要指定一个v

Spring中的AOP(七)——基于XML配置文件方式的AOP

    除了前面介绍的基于JDK1.5的注解方式来定义切面,切入点和增强处理外,Spring AOP也允许直接使用XML配置文件来管理它们.在JDK1.5之前,只能使用配置文件的方式来管理,在Spring2.X后提供了一个新的aop命名空间来定义切面.切入点和增强处理.     相比之下,使用XML配置文件方式有如下优点: 如果没有使用JDK1.5以上版本,只能使用XML配置文件的方式 对早期的Spring用于来说更加习惯,而且这种方式允许使用纯粹的POJO来支持AOP 采用XML配置方式时,我

Spring中的AOP(二)——AOP基本概念和Spring对AOP的支持

AOP的基本概念     AOP从运行的角度考虑程序的流程,提取业务处理过程的切面.AOP面向的是程序运行中的各个步骤,希望以更好的方式来组合业务逻辑的各个步骤.AOP框架并不与特定的代码耦合,AOP框架能处理程序执行中特定切入点,而不与具体某个类耦合(即在不污染某个类的情况下,处理这个类相关的切点).下面是一些AOP的一些术语:     切面(Aspect):业务流程运行的某个特定步骤,也就是应用运行过程的关注点,关注点通常会横切多个对象,因此常被称为横切关注点     连接点(JoinPoi

Spring中的AOP(一)——AspectJ的基本使用

    AOP(Aspect Orient Programming),也就是面向切面编程,作为面向对象编程的一种补充,当前已经成为一种比较成熟的编程思想,其实AOP问世的时间并不长,甚至在国内的翻译还不太统一(另有人翻译为"面向方面编程").AOP和OOP(Object Orient Programming,面向对象编程)互为补充,OOP将程序分解成各个层次的对象,而AOP则将程序运行过程分解成各个切面.可以这样理解:OOP是从静态角度考虑程序结构,而AOP则从动态角度考虑程序运行过程

Spring3.2中Bean定义之基于Annotation和Java Code配置方式的源码解析

基于 Annotation 配置 Bean Definition 的源码解读 本系列文章第一部分分析了 Spring 解析 XML 配置文件中 <bean /> 元素的源码,这是 Spring 最原始的一种配置方式,同时也使 XML 中的节点具有命名空间特性.参考 Spring 相关文档,如果有如下的配置方式: <context:component-scan base-package="com.colorcc.spring.sample" /> 则可知:其一,该

Spring中的AOP(六)——定义切入点和切入点指示符

定义切入点     在前文(点击查看)中使用到的AdviceTest类中同一个切点(即* com.abc.service.*.advice*(..)匹配的连接点)却重复定义了多次,这显然不符合软件设计的原则,为了解决这个问题,AspectJ和Spring都提供了切入点的定义.所谓定义切入点,其实质就是为一个切入点表达式起一个名称,从而允许在多个增强处理中重用该名称.     Spring AOP只支持以Spring Bean的方法执行组作为连接点,所以可以把切入点看作所有能和切入表达式匹配的Be

Spring中的AOP(五)——定义切入点和切入点指示符

定义切入点     在前文(点击查看)中使用到的AdviceTest类中同一个切点(即* com.abc.service.*.advice*(..)匹配的连接点)却重复定义了多次,这显然不符合软件设计的原则,为了解决这个问题,AspectJ和spring都提供了切入点的定义.所谓定义切入点,其实质就是为一个切入点表达式起一个名称,从而允许在多个增强处理中重用该名称.     Spring AOP只支持以Spring Bean的方法执行组作为连接点,所以可以把切入点看作所有能和切入表达式匹配的Be

Spring中的AOP详解

本文摘自pandonix的博客.   此前对于AOP的使用仅限于声明式事务,除此之外在实际开发中也没有遇到过与之相关的问题.最近项目中遇到了以下几点需求,采用AOP来解决.一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容.本文是权当本人的自己AOP学习笔记,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 面对需求: ①对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况. ②监控部分重要函数,若抛出指定的异

Spring中多配置文件及引用其他bean的方式_java

Spring多配置文件有什么好处? 按照目的.功能去拆分配置文件,可以提高配置文件的可读性与维护性,如将配置事务管理.数据源等少改动的配置与配置bean单独分开. Spring读取配置文件的几种方式: 1.使用Spring自身提供的ApplicationContext方式读取 在Java程序中可以使用ApplicationContext两个实现类ClassPathXmlApplicationContext以及FileSystemXmlApplicationContext来读取多个配置文件,他们的