Spring AOP 3.x 和 2.x

聊一聊AOP (面向切面编程)

AOP(Aspect Oriented Programming),采取的是横向抽取的机制,与OOP面向对象不同,取代了继承和委托,而是将分布于应用多处的功能(横切关注点)与业务逻辑相分离。
通俗点说,就是不必要再将一些通用的功能代码如日志,安全,事务管理这些与程序的主要代码放在一起,实现横切关注点与它们所影响的对象之间的解耦。

先介绍一些AOP术语
1、通知(Advice):定义了那些你在程序里作为通用功能代码(就是切面)的主要工作是做什么的,还有什么时候执行这个工作
一般按照执行时机来分:
Before:在业务逻辑方法调用之前调用通知
After:在业务逻辑方法调用之后调用通知(无论方法执行是否成功)
After-returning:方法执行成功后
After-throwing:方法抛出异常后
Around:包裹被通知方法,也就是说这一个通知可以实现上面四个通知的功能。

2、连接点(JoinPoint):应用过程中能够插入切面的一个点

3、切点(Pointcut):在何处调用通知,就是说,在哪个业务逻辑处。

4、切面(Aspect):通知和切点的结合

5、引入(Introduction):在不修改现有类的情况下,允许我们向现有的类添加新方法或属性

6、织入(Weaving):将切面应用到目标对象来创建新的代理对象
分为编译期—–比如AspectJ
类加载期—–用的较少
运行期—–SpringAop

下面将经典AOP与引入简单声明式AOP和基于注解的AOP对比起来说
先看下实例代码
Performer接口

package com.lz.springinaction.springidol;

public interface Performer {
    void perform();
}

实现了Performer接口的Singer类

package com.lz.springinaction.springidol;

public class Singer implements Performer {

    //业务逻辑核心功能
    public void perform() {
        System.out.println("singing....");
    }

}

实现了Perform接口的Dancer类

package com.lz.springinaction.springidol;

public class Dancer implements Performer {

    //业务逻辑核心功能
    public void perform() {
        System.out.println("dancing。。。");
    }

}

Audience类(这就是一个应用程序里的横切关注点,也就是会在多个地方都会用到的通用代码,这里模块化为一个类,即所谓的切面)

package com.lz.springinaction.springidol;

public class Audience {
    //每一位表演者表演之前
    public void takeSeats(){
        System.out.println("观众正在找座位");
    }
    //每一位表演者表演之前
    public void turnOffCellPhones(){
        System.out.println("观众将手机关机或调至静音");
    }
    ///每一位表演者表演成功之后
    public void applaud(){
        System.out.println("观众强烈鼓掌");
    }
    //每一位表演者表演失败之后
    public void demandRefund(){
        System.out.println("退钱");
    }
}

配置文件的相关内容

<bean id="dancer" class="com.lz.springinaction.springidol.Dancer"></bean>
    <bean id="singer" class="com.lz.springinaction.springidol.Singer"></bean>

    <bean id="audience" class="com.lz.springinaction.springidol.Audience"></bean>

    <aop:config>
        <!-- 声明切面,ref引用的作为切面的类 ,这里是Audience-->
        <aop:aspect ref="audience">
            <!-- 定义切点 ,属性expression即在何处发生-->
            <aop:pointcut
                expression="execution(* com.lz.springinaction.springidol.Performer.perform(..) )"
                id="performance" />
            <!-- 多个通知 ,method引用的是切面类里面的方法-->
            <aop:before method="takeSeats" pointcut-ref="performance"/>
            <aop:before method="turnOffCellPhones" pointcut-ref="performance"/>
            <aop:after-returning method="applaud" pointcut-ref="performance"/>
            <aop:after-throwing method="demandRefund" pointcut-ref="performance"/>
        </aop:aspect>
    </aop:config>

测试类及结果

package com.lz.springinaction.springidol;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Performer aa=(Performer) context.getBean("dancer");
        Performer bb=(Performer) context.getBean("singer");
        aa.perform();
        bb.perform();

    }
}

测试结果,在一个Singer唱歌和一个Dancer跳舞前后都会有相同的行为发生。

log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
观众正在找座位
观众将手机关机或调至静音
dancing。。。
观众强烈鼓掌
观众正在找座位
观众将手机关机或调至静音
singing....
观众强烈鼓掌

下面我们来看看经典SpringAop的过程,相同的例子

Singer,Dancer都一样
2.x的AOP的不同通知类型,是通过不同的接口来实现,有MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice,MethodInterceptor

这里的Audience(当然也可以将这里面的每个方法单独拿出来作为一个Advice来实现上述几个接口,比如写个TakeSeats类,TurnOffCellPhone类等,但这样单独写,在配置文件中需要每个分别配置,比较麻烦)

package com.lz.spring;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;

public class Audience implements MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice {

    public void before(Method arg0, Object[] arg1, Object arg2)
            throws Throwable {
        System.out.println("观众正在找座位");
        System.out.println("观众将手机关机或调至静音");

    }

    public void afterReturning(Object arg0, Method arg1, Object[] arg2,
            Object arg3) throws Throwable {
        System.out.println("观众们在鼓掌");

    }
}

配置文件

<!-- 目标对象 -->
    <bean id="singer" class="com.lz.spring.Singer"></bean>
    <bean id="dancer" class="com.lz.spring.Dancer"></bean>

    <!-- 定义通知 -->
    <bean id="audience" class="com.lz.spring.Audience"></bean>

    <!-- 定义Advisor -->
    <bean id="audienceAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <!-- 指定切入方法 -->
        <property name="mappedName" value="perform"></property>
        <!-- 指定Advice -->
        <property name="advice" ref="audience"/>

    </bean>
<!-- 定义AOP代理 -->
    <bean id="SingerProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目标对象 -->
        <property name="target" ref="singer"></property>
        <!-- 注入通知 -->
        <property name="interceptorNames">
            <list>
                <value>audienceAdvisor</value>
            </list>
        </property>

    </bean>

    <bean id="DancerProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目标对象 -->
        <property name="target" ref="dancer"></property>
        <!-- 注入通知 -->
        <property name="interceptorNames">
            <list>
                <value>audienceAdvisor</value>
            </list>
        </property>

    </bean>
</beans>

2.x的AOP,将目标对象和Advice或者Advisor封装到一个AOP代理中,这样客户端使用这个AOP代理对象,就与原来的目标对象没有任何区别

测试类:

package com.lz.spring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Performer pp1 = (Performer) context.getBean("SingerProxy");
        Performer pp2 = (Performer) context.getBean("DancerProxy");
        pp1.perform();
        pp2.perform();
    }
}

Performer pp1 = (Performer) context.getBean(“SingerProxy”);
Performer pp2 = (Performer) context.getBean(“DancerProxy”);
这两个类型是目标对象实现的接口Performer,不能返回类型Singer或者Dancer,而是目标对象实现的接口

相比较,Spring AOP3.x要比2.x简单方便,2.x这里要分别对Singer和Dancer分别进行AOP代理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值