Spring Aop基础使用

说到Spring,想必大家一定就马上想到了,哦Spring不就是帮助管理Bean对象,封装数据源,提供事务管理的东西么。的确,平常在使用Spring的时候,用到最多的就是Spring提供的这些功能了,其实Spring还有一个挺好的东西,或许大家平常用得少一些,他就叫AOP。 

什么AOP, 这是什么玩意儿, 我只知道OOP, AOP是谁,他舅么? 哈哈,开个玩笑,OOP是面向对象编程, AOP其实叫面向切面编程,反正都是编程,就表示你都要写代码就好了. IT屌丝,反正都是搬砖打熬气力的活。

至于AOP的详细介绍,小弟这里就不多说了,到处一搜就出来了, 我这里想说的一点就是,通常使用AOP的目的,都是我们想在某一个方法执行前,或者执行后 再额外的执行一些代码,但是我们又不像去修改已经写好的业务代码。

好了,在开始AOP之前,我们先来了解一下与AOP相关的几个名词,当然如果你是第一次使用AOP的话,你不妨先跳过这些名词,先试着把下面的例子,完整的录入你的项目,跑起来看看效果,然后再回过头来理解这些名词。

1. Aspect 切面: 哦哟,真巧哦, AOP是面向切面编程,Aspect又是切面,那不就是面向Aspect编程么..当然你要这么理解也行,只要记得,我们代码要编写的就是Aspect就行了. Aspect就是关注点的模块化,一个Aspect可以切多个对象,想切谁就切谁...

2.JointPoint 连接点:这代表程序执行中的某一行为, 通常我们把他定位到业务中的某个具体的方法,也就是我们想在这个方法的执行前,或者执行后额外的再执行一些代码,那么我们现在关注的这个方法,就是JointPoint

3.Advice 通知:通知就是切面对于某个连接点所产生的动作,这里动作就相当于我们额外执行的代码。

4.PointCut 切入点:这只要记得PointCut就是一个表达式,这个表达式的作用就是把通知(Advice)和连接点(JointPoint)关联起来,让Advice作用到JointPoint上就OK了.

OK,话不多说,上代码了(似乎已经说了很多了.....),假设这段例子我们想实现一个核心功能叫play(), 这个功能一调用,我们就能玩小霸王游戏机,

这个功能已经写好了, 但是现在我想在玩小霸王之前,检查一下周围的环境,看是否适合玩小霸王,为什么要检查呢? 嘿,问得好,万一我打算玩小霸王

中的一个游戏叫电什么狼什么(当然小霸王中没有这个电什么狼什么的游戏,有的话小霸王估计就不会没落了,哈哈,你懂的)的,你认为你能在大厅广众之下毫无顾忌的玩起来?? 总之我就想在玩之前检查一下环境, 在玩之后在收拾一下战场,收拾一下电源,4合1,3合1的卡,纸巾什么的..但是又不想在已经写好的play代码中来修改,

那怎么办呢,好吧我们先写一个Aspect

public class AspectForPlay {

    public void doBefore(JoinPoint joinPoint) {
        System.out.println("开玩之前检查环境:" + joinPoint.getTarget().getClass().getName()
                + "." + joinPoint.getSignature().getName());
    }

    public void doAfter(JoinPoint joinPoint) {
        System.out.println("结束之后收拾战场:" + joinPoint.getTarget().getClass().getName()
                + "." + joinPoint.getSignature().getName());
    }

    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("一切就绪,开始Happing:" + start);

        Object proceed = pjp.proceed();

        System.out.println("顺利通关,打完收工:" + (System.currentTimeMillis() - start));
        return proceed;
    }
}


当然还有我们的核心逻辑play()功能,先来个play的接口

public interface PlayXbwService {

    public void play();
}

再来实现这个接口,展示我们玩的内容:

public class PlayXbwServiceImple implements PlayXbwService {


    @Override
    public void play() {
        System.out.println("playing:小霸王其乐无穷");
    }
}

当然啦,最后还有Spring的配置文件

<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-3.1.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
    <bean id="xbwService" class="com.andy.springaop.PlayXbwServiceImple"></bean>
    <bean id="aspectBean" class="com.andy.springaop.AspectForPlay" ></bean>
    <aop:config>
        <aop:aspect id="playAspect" ref="aspectBean">
            <aop:pointcut id="playPointCut" expression="execution(* com.andy.springaop.*.*(..))"></aop:pointcut>
            <aop:before method="doBefore" pointcut-ref="playPointCut"></aop:before>
            <aop:after method="doAfter" pointcut-ref="playPointCut"></aop:after>
            <aop:around method="doAround" pointcut-ref="playPointCut"></aop:around>
            <aop:after-throwing method="doThrowing" pointcut-ref="playPointCut"></aop:after-throwing>
        </aop:aspect>
    </aop:config>
</beans>
可以看到AOP的配置,主要是在<aop:config>的标签中来实现的。接下来,写个类,让我们写个类试一试吧:

public class AspectTest {

    ClassPathXmlApplicationContext ctx;

    @Before
    public void prepare() {
        ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    }

    @Test
    public void testPlayXbw() {
        PlayXbwService service = (PlayXbwService) ctx.getBean("xbwService");
        service.play();
    }
}


OK, 当我们运行这个测试类之后,结果如下:

开玩之前检查环境:com.andy.springaop.PlayXbwServiceImple.play
一切就绪,开始Happing:1401286273603
playing:小霸王其乐无穷
结束之后收拾战场:com.andy.springaop.PlayXbwServiceImple.play
顺利通关,打完收工:2

怎么样,是不是在我们play()的前后,都插入了一些额外的处理呢..  这些处理就是我们AspectForPlay类中编写的方法
可以看到在Aspect类中,我们编写了三个方法, doAfter, doBefore, doAround,其实他们分别对应的是“后置通知”, “前置通知”,“环绕通知”

通知Advise也不止这么几种, 下面就是全部的通知类型了:

1. 后置通知(After Advice) : 后置通知表示在连接点执行之后 额外执行的操作,在Spring中,我们用<aop:after>来配置他

2. 前置通知(Before Advice):前置通知表示在连接点执行之前 额外执行的操作,在Spring中,我们用<aop:before>来配置他

3. 环绕通知(Around Advice):表示在连接点 执行前后,额外执行的操作,另外注意环绕通知可以控制连接点的执行哦,如代码中的:Object proceed = pjp.proceed(); 如果没有这一句,连接点就不会执行了, 在Spring中,我们用<aop:around>来配置他

4. 返回后通知(after return advice):表示在连接点正常执行完之后,额外执行的操作, 注意是正常执行完哦, 在Spring中,我们用<aop:after-returning>来配置他

5. 跑出异常通知(after throwing advice):表示在程序抛出异常后,额外执行的操作,在Spring中,我们用<aop:after-throwing>来配置他


示例程序中,我们只写了前三种通知,后两种鼓励读者自行尝试, 另外在示例程序中的Advice我们可以看到,都有一个JointPoint对象作为参数, 当然环绕通知是ProceedingJointPoint对象, 这个jointPoint对象包含了连接点的信息以及代理对象的信息, 是很有用的哦。。 当然我们在写advice的时候,也可以不要这个参数。。


另外对于PointCut,我们前面提到 他是一个连接通知与连接点的表达式,那么这个表达式的书写规范如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 

modifiers-pattern: 方法权限 如private public等

ret-type-pattern: 方法返回值类型 如void String等

declaring-type-pattern: 方法所在包 如com.andy.springaop等

name-pattern:方法名 如play等

throws-pattern: 异常类型 比如IOException等


OK, 到这里Spring AOP的基础使用就差不多了, 另外Spring还支持注解式的Aop配置,这样我们在Spring的配置文件中,就不用再配置<aop:config>了,只需要一句:

<aop:aspectj-autoproxy />配置就完事了,当然既然是基于注意,自然就还要开启Spring的注解扫描功能了:    <context:component-scan base-package="com.andy.*" />


好了,下面附上基于注解的, Aspect文件的示例:

@Aspect
public class AspectForPlay {

    @Before("execution(* com.andy.springaop.*.*(..))")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("开玩之前检查环境:" + joinPoint.getTarget().getClass().getName()
                + "." + joinPoint.getSignature().getName());
    }

    @After("execution(* com.andy.springaop.*.*(..))")
    public void doAfter(JoinPoint joinPoint) {
        System.out.println("结束之后收拾战场:" + joinPoint.getTarget().getClass().getName()
                + "." + joinPoint.getSignature().getName());
    }

    @Around("execution(* com.andy.springaop.*.*(..))")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("一切就绪,开始Happing:" + start);

        Object proceed = pjp.proceed();

        System.out.println("顺利通关,打完收工:" + (System.currentTimeMillis() - start));
        return proceed;
    }
}

最后, 我们看到例子中,用的AOP是基于接口的, 那基于类的情况下,AOP能否使用呢,答案是肯定的.. 

只是基接口和基于类时,Spring所使用的动态代理不一样而已,具体不一样的地方,有条件可以自行翻阅Spring源码研究哦。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值