Spring AOP(面向切面编程)

来源:互联网 发布:北塔软件联系人 编辑:程序博客网 时间:2024/05/01 21:02
Spring   AOP
AOP(Aspect Orient Programming),也就是面向切面编程。可以这样理解,面向对象编程(OOP)是从静态角度考虑程序结构,面向切面编程(AOP)是从动态角度考虑程序运行过程


在日常生活中,会遇到各种各样的中介机构,比如猎头公司,律师事务所,婚姻介绍所,房产公司等。在这些单位工作的人员均可称为代理人。代理人的共同特征是可以代替委托人去和第三方通信。譬如:律师代替委托人打官司,猎头代替委托人物色人才,红娘代替委托人寻找对象,房产代理人代替委托人出租房屋。

 

代理人可以在第三方和委托人之间转发或过滤消息,但是不能取代委托人的任务。譬如你要找女朋友,委托你一要好的哥们去帮你物色,结果被他给物色了。这就不叫代理。


代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。


先介绍AOP的四种通知类型:

 

1、前置通知:在目标方法执行之前调用。

实现MethodBeforeAdvice接口,接口方法如下:

void before(Method method,Object[] args,Object target) throws Throwable;

       这个接口为我们提供了获得目标方法,参数,目标对象的机会。注意:我们没必要在这个方法里面调用目标方法(可以通过反射调用),因为,这个接口方法结束后目标方法将调用,这才叫前置嘛(在目标方法之前)。要想终止程序的继续运行,只有抛异常或调用System.exit()

 

  2、  后置通知:在目标方法执行之后调用。

实现AfterReturningAdvice接口,该接口方法如下:

void afterReturning(Object returnValue,Method method,
                    Object[] args, Object target) throws Throwable

这个方法多了一个目标方法的返回值参数。同理要结束程序流程只有抛异常和调用System.exit()方法。

      

3、环绕通知:拦截目标方法的执行,可在该类型通知里自行调用目标方法。

实现MethodInterceptor接口,该接口在aopalliance.jar包里,这个包是aop联盟(aop编程接口,为实现aop编程接口重用)的包,可以理解成aop编程规范接口。

接口方法如下:

Object  invoke(MethodInvocation  invocation)  throws  Throwable;

环绕通知和上述2个通知的区别:

1)由于环绕通知是拦截目标方法的执行,所以在这个方法里用户可以通过invocation.proceed()方法调用目标方法,这点和MethodBeforeAdvice不同,它的目标方法总是会执行,除非抛异常或执行System.exit()方法。

2)正常情况下这个方法返回的就是目标方法(invocation.proceed()返回的结果)的返回值,但可以在这个接口方法里改变目标方法的返回值,除非必须如此,否则最好不要这样使用。这个接口与AOP联盟的AOP框架兼容。

因为要显示的调用目标方法(invocation.proceed()),所以更推荐使用上述2种方式。

4、异常通知:当目标方法抛出异常时调用。

实现ThrowsAdvice接口,该接口是标记接口,没有定义任何一个必须实现的方法,但实现该接口的类必须至少有一个如下形式的方法:



下面看一个AOP例子
切面类TestAspect
package com.spring.aop;/** * 切面 * */public class TestAspect {    public void doAfter(JoinPoint jp) {        System.out.println("log Ending method: "                + jp.getTarget().getClass().getName() + "."                + jp.getSignature().getName());    }    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {        long time = System.currentTimeMillis();        Object retVal = pjp.proceed();        time = System.currentTimeMillis() - time;        System.out.println("process time: " + time + " ms");        return retVal;    }    public void doBefore(JoinPoint jp) {        System.out.println("log Begining method: "                + jp.getTarget().getClass().getName() + "."                + jp.getSignature().getName());    }    public void doThrowing(JoinPoint jp, Throwable ex) {        System.out.println("method " + jp.getTarget().getClass().getName()                + "." + jp.getSignature().getName() + " throw exception");        System.out.println(ex.getMessage());    }    private void sendEx(String ex) {        //TODO 发送短信或邮件提醒    }} 

package com.spring.service;/** * 接口A */public interface AService {        public void fooA(String _msg);    public void barA();}

package com.spring.service;/** *接口A的实现类 */public class AServiceImpl implements AService {    public void barA() {        System.out.println("AServiceImpl.barA()");    }    public void fooA(String _msg) {        System.out.println("AServiceImpl.fooA(msg:"+_msg+")");    }}

package com.spring.service;/** *   Service类B */public class BServiceImpl {    public void barB(String _msg, int _type) {        System.out.println("BServiceImpl.barB(msg:"+_msg+" type:"+_type+")");        if(_type == 1)            throw new IllegalArgumentException("测试异常");    }    public void fooB() {        System.out.println("BServiceImpl.fooB()");    }}

ApplicationContext.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:aop="http://www.springframework.org/schema/aop"    xsi:schemaLocation="            http://www.springframework.org/schema/beans            http://www.springframework.org/schema/beans/spring-beans-2.0.xsd            http://www.springframework.org/schema/aop            http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"    default-autowire="autodetect">    <aop:config>        <aop:aspect id="TestAspect" ref="aspectBean">            <!--配置com.spring.service包下所有类或接口的所有方法-->            <aop:pointcut id="businessService"                expression="execution(* com.spring.service.*.*(..))" />            <aop:before pointcut-ref="businessService" method="doBefore"/>            <aop:after pointcut-ref="businessService" method="doAfter"/>            <aop:around pointcut-ref="businessService" method="doAround"/>            <aop:after-throwing pointcut-ref="businessService" method="doThrowing" throwing="ex"/>        </aop:aspect>    </aop:config>        <bean id="aspectBean" class="com.spring.aop.TestAspect" />    <bean id="aService" class="com.spring.service.AServiceImpl"></bean>    <bean id="bService" class="com.spring.service.BServiceImpl"></bean></beans>

测试类AOPTest
public class AOPTest extends AbstractDependencyInjectionSpringContextTests {private AService aService;private BServiceImpl bService;protected String[] getConfigLocations() {String[] configs = new String[] { "/applicationContext.xml"};return configs;}/** * 测试正常调用 */public void testCall(){System.out.println("SpringTest JUnit test");aService.fooA("JUnit test fooA");aService.barA();bService.fooB();bService.barB("JUnit test barB",0);}/** * 测试After-Throwing */public void testThrow(){try {bService.barB("JUnit call barB",1);} catch (IllegalArgumentException e) {}}public void setAService(AService service) {aService = service;}public void setBService(BServiceImpl service) {bService = service;}}

  运行结果如下:
log Begining method: com.spring.service.AServiceImpl.fooAAServiceImpl.fooA(msg:JUnit test fooA)log Ending method: com.spring.service.AServiceImpl.fooAprocess time: 0 mslog Begining method: com.spring.service.AServiceImpl.barAAServiceImpl.barA()log Ending method: com.spring.service.AServiceImpl.barAprocess time: 0 mslog Begining method: com.spring.service.BServiceImpl.fooBBServiceImpl.fooB()log Ending method: com.spring.service.BServiceImpl.fooBprocess time: 0 mslog Begining method: com.spring.service.BServiceImpl.barBBServiceImpl.barB(msg:JUnit test barB type:0)log Ending method: com.spring.service.BServiceImpl.barBprocess time: 0 mslog Begining method: com.spring.service.BServiceImpl.barBBServiceImpl.barB(msg:JUnit call barB type:1)log Ending method: com.spring.service.BServiceImpl.barBmethod com.spring.service.BServiceImpl.barB throw exception测试异常
讲解:
我们先看配置文件中  有:before after  around   throw   
   测试类中顺序为      fooA  barA   fooB   barB   还有一个异常测试    barB

先从fooA开始,由于配置文件中为  before   after  around   throw     那么对于fooA,就可以从  dobefore   doafter   doaround   dothrow 开始

在做fooA之前  先  做before   做完fooA然后做  after      然后对于环绕通知共享前置后后置信息

所以对于fooA  输出为   

log Begining method: com.spring.service.AServiceImpl.fooAAServiceImpl.fooA(msg:JUnit test fooA)log Ending method: com.spring.service.AServiceImpl.fooAprocess time: 0 ms
至于为什么会没有throw因为在fooA执行过程中并没有抛出异常。

同理   其他的也这样理解


0 0
原创粉丝点击