Spring(二)Spring AOP配置详解

来源:互联网 发布:js点击事件的写法 编辑:程序博客网 时间:2024/04/25 08:04

一、Spring AOP

        面向切面编程(AOP),是软件编程思想发展到一定阶段的产物,是对面向对象编程(OOP)的有益补充。AOP一般适用于具有横切逻辑的场合,如访问控制、事务管理、性能监测等。
        面向切面编程,简单的说,就是在不改变源程序的基础上为代码段增加新的功能,对代码进行增强处理,它的设计思想来源于代理设计模式。
        了解之后需要知道以下概念:
        1.增强类型(Advice,有的翻译为通知,个人感觉不太好理解):
                前置增强(通知),在在原方法执行之前进行处理,同理还有后置增强、环绕增强、异常抛出增强、最终增强等。
        2.连接点(Joinpoint):
                程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。
        3.切点(Pointcut):
                每个类都拥有多个连接点:类的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

下面编写一个HelloWorld:
需求:
在Student类中有一个说话的say方法,需要使用AOP对此方法进行增强处理。

package com.wzj.entity;public class Student {private int id;private String name;//省略get、set方法public void say(){System.out.println("大家好,我的名字叫:"+getName());}}

二、基于注解的方式实现spring aop增强

步骤:
1.创建普通的java项目,加入jar包(在上文中有jar包的下载地址),创建好spring-config.xml文件,另外还要导入AspectJ框架所需jar包:aspectjweaver.jar、aopalliance.jar,可以百度下载
2.创建一个增强类如:AdviceUtil
(1)@Aspect注解——将类声明为切面

@Aspectpublic class AdviceUtil {}
(2)@Before注解——声明为前置增强方法
/** * 使用@Before注解将方法声明为前置增强方法 * 参数中需要加入:匹配切点的表达式execution(public void com.wzj.entity.Student.say()) * 匹配切点的表达式:execution(修饰符  返回值  方法全名(..)) * 可以使用“*”和“..”通配符,* 表示匹配所有,.. 用在方法参数中,表示匹配任意个数、类型的参数 */@Before("execution(public void com.wzj.entity.Student.say()))")public void before(){System.out.println("----->前置增强操作");}
(3)@AfterReturning注解——声明后置增强方法
@AfterReturning("execution(public void com.wzj.entity.Student.say())")public void behind(){System.out.println("----->后置增强操作");}
【为了获取到代理对象以及方法的相关信息,可以再增强方法中声明一个JoinPoint类型参数,spring会自动注入该参数】
a-->getTarget():获取到代理对象
b-->getSignature:获取到目标方法,其中包含一些信息
c-->getArgs:获取到目标方法的参数列表

注意:对于后置增强还可以获取到目标方法的返回值,如:
/** * 1.在方法中声明一个目标方法的返回值参数 * 2.在AfterReturning注解中通过returning属性指定参数名称 * 3.spring会自动注入参数 * @param jp * @param returnValue */@AfterReturning(pointcut="execution(public * com.wzj.entity.Student.say())",returning="returnValue")public void behind(JoinPoint jp,String returnValue){System.out.println("目标方法返回值:"+returnValue);}
(4)@AfterThrowing注解——声明异常抛出增强方法
/** * 1.使用AfterThrowing注解将方法声明为异常抛出增强 * 2.可以在方法中定义对应异常类型的参数,不过需要通过注解的throwing属性指定参数名 * 3.spring会自动注入异常类型实例 * 4.异常增强方法可以有多个 * @param ex */@AfterThrowing(pointcut="execution(public * com.wzj.entity.Student.say())",throwing="ex")public void catchException(ArithmeticException ex){System.out.println("异常增强捕获到:"+ex.getMessage());}
(5)@Around注解——声明环绕增强方法
/** * 1.通过Around注解将方法声明为环绕增强 * 2.可以定义ProceedingJoinPoint类型的参数获取到连接点信息 * 3.ProceedingJpintPoint是JoinPoint的子接口,用法相同 * 4.注意:环绕增强不会自动调用目标方法,所以必须手动在方法中使用proceed()方法调用目标方法 * 从而达到对连接点的完全控制 * @param pj * @throws Throwable  */@Around("execution(public * com.wzj.entity.Student.say())")public void around(ProceedingJoinPoint pj) throws Throwable{System.out.println("前面的处理...");pj.proceed();//调用目标方法System.out.println("后面的处理...");}
(6)@After注解——声明最终增强方法
        我们知道,如果在目标方法中抛出了异常,就不会执行后续的后置增强。在AspectJ中为我们提供了一种最终增强,无论是否出了异常都会执行,类似于try-catch中的finally块,一般用于释放资源
/** * 使用After注解,其他用法相同 */@After("execution(public * com.wzj.entity.Student.say())")public void after(){System.out.println("----->最终增强操作");}
完整的AdviceUtil类:
package com.wzj.entity;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;/** * 使用@Aspect将此类定义为一个切面 */@Aspectpublic class AdviceUtil {/** * 匹配切点的表达式:execution(修饰符  返回值  方法全名(..)) * 可以使用“*”和“..”通配符,* 表示匹配所有,.. 用在方法参数中,表示匹配任意个数、类型的参数 */@Before("execution(void com.wzj.entity.Student.say())")public void before(){System.out.println("----->前置增强操作");}@AfterReturning(pointcut="execution(public * com.wzj.entity.Student.say())",returning="returnValue")public void behind(JoinPoint jp,String returnValue){System.out.println("----->后置增强操作");}@AfterThrowing(pointcut="execution(public * com.wzj.entity.Student.say())",throwing="ex")public void catchException(ArithmeticException ex){System.out.println("异常增强捕获到:"+ex.getMessage());}@Around("execution(public * com.wzj.entity.Student.say())")public void around(ProceedingJoinPoint pj) throws Throwable{System.out.println("前面的处理...");pj.proceed();//调用目标方法System.out.println("后面的处理...");}@After("execution(public * com.wzj.entity.Student.say())")public void after(){System.out.println("----->最终增强操作");}}
3.在spring-config.xml文件中进行配置
<?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:p="http://www.springframework.org/schema/p"xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-4.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.0.xsd"><!-- Student类的实例bean --><bean id="student" class="com.wzj.entity.Student" p:name="张三"/><!-- 切面的实例bean --><bean class="com.wzj.entity.AdviceUtil"></bean><!-- 使用注解方式需增加AspectJ的支持 --><aop:aspectj-autoproxy/></beans>
4.main方法:
public static void main(String[] args) {//通过ApplicationContext接口的实现类实例化Spring上下文对象ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");//通过getBean()方法来获取到Student类的对象Student student=(Student)context.getBean("student");student.say();}

三、使用xml文件配置spring aop增强

1.前置增强:

(1)创建AdviceBefore类,实现MethodBefore接口及其before方法:
public class AdviceBefore implements MethodBeforeAdvice{@Overridepublic void before(Method arg0, Object[] arg1, Object arg2)throws Throwable {System.out.println("前置增强的一些处理");}}

(2)配置xml文件:
需要导入aop的命名空间,配置(beans跟标签省略):
<!-- Student类的实例bean --><bean id="student" class="com.wzj.entity.Student" p:name="张三"/><!-- 前置增强类的实例 --><bean id="adviceBefore" class="com.wzj.advice.AdviceBefore"></bean><!-- 配置切面 --><aop:config><!-- 配置切点 --><aop:pointcut expression="execution(* com.wzj.entity.Student.say())" id="pt"/><!-- 将切点和具体的前置增强类实例粘合1.advice-ref:增强类实例的引用2.pointcut-ref:切入点的引用 --><aop:advisor advice-ref="adviceBefore" pointcut-ref="pt"/></aop:config>

2.后置增强:

(1)创建AdviceBehind类,实现AfterReturningAdvice接口中的afterReturning接口
(2)配置:

<!-- 后置增强类的实例 --><bean id="adviceBehind" class="com.wzj.advice.AdviceBehind"></bean><aop:config>标签中添加:<aop:advisor advice-ref="adviceBehind" pointcut-ref="pt"/>

3.异常抛出增强:

1)创建类,实现ThrowsAdvice接口,该接口中并没有提供方法,需要自己定义
        注意,方法的定义需要遵守下面的方法签名:

void afterThrowing([Method method,Object[] arguments,Object target,]Throwable ex)
a.-->方法名必须是afterThrowing
b.-->前三个参数要么都有,要么都没有,不能单个出现
c.-->Throwable类型的表示异常的类型
(2)配置:
<!-- 异常类型的实例 --><bean id="adviceException" class="com.wzj.util.AdviceException"></bean><aop:config>标签中添加:<aop:advisor advice-ref="adviceException" pointcut-ref="pt"/>

4.环绕增强:

         实现MethodInterceptor接口中的invoke方法,参数类型为MethodInvocation,调用参数的proceed()方法即可调用目标方法,其他配置上同。

四、基于schema配置定义切面

我们还可以在spring配置文件中通过aop命名空间将一个普通的JavaBean中的方法 声明为增强类型
1.创建一个普通的类,如:AdviceUtil

package com.wzj.entity.schema;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;public class AdviceUtil {public void before(){System.out.println("----->前置增强操作");}public void behind(JoinPoint jp,String returnValue){System.out.println("----->后置增强操作");}public void catchException(ArithmeticException ex){System.out.println("捕获到异常:"+ex.getMessage());}public void around(ProceedingJoinPoint pj) throws Throwable{System.out.println("前面的处理...");pj.proceed();//调用目标方法System.out.println("后面的处理...");}public void after(){System.out.println("----->最终增强操作");}}
2.配置xml文件:
<?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:p="http://www.springframework.org/schema/p"xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-4.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.0.xsd"><!-- Student类的实例bean --><bean id="student" class="com.wzj.entity.Student" p:name="张三"/><!-- 含有增强方法的Bean --><bean id="advice" class="com.wzj.schema.AdviceUtil"></bean><!-- 配置切面 --><aop:config><!-- 定义切入点 --><aop:pointcut expression="execution(* com.wzj.entity.Student.say())" id="pt"/><!-- 引用包含增强方法的Bean --><aop:aspect ref="advice"><!-- 将其中的before方法定义为增强方法,并引用切点,下同 --><aop:before method="before" pointcut-ref="pt"/><!-- 如果有需要,可以使用returning属性向增强方法中注入返回值 --><aop:after-returning method="behind" pointcut="pt" returning="returnValue"/><aop:after-throwing method="catchException" pointcut-ref="pt"/><aop:around method="around" pointcut-ref="pt"/><aop:after method="after" pointcut-ref="pt"/></aop:aspect></aop:config></beans>

五、配置建议:

        通过接口实现增强处理是较低版本的spring aop的做法,如果在一个使用较低版本的spring aop的项目上进行升级,可以考虑使用<aop:advisor>复用已经存在的增强类; 如果项目采用JDK5.0以上版本,可以使用注解的形式,减少配置工作量; 如果不想使用注解或者项目的JDK版本较低,可以使用aop:aspect配合javabean 的方式。
0 0
原创粉丝点击