Spring的AOP

一、概述

    因为软件系统需求变更是很频繁的事情,系统可能对前期设计的方法进行不断的更改,如增加事务控制、合法性验证、记录日志等。我们希望有一种特殊的方法,我们只要定义该方法,无须在原方法中显示调用它,系统会自动执行该特殊方法。这就是AOP,它专门用于处理系统中分布于各个模块中的交叉关注点的问题,处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等。

二、AspectJ 对AOP的实现

    AspectJ是一个基于Java语言的AOP框架,是Eclipse下面的一个开源子项目,主要包括两部分:第一部分定义了如何表达、定义AOP编程中的语法规范;另一部分是工具部分,包括编译、调试工具等。

1. 下载和安装AspectJ

1.1 登陆http://www.eclipse.org/aspectj/downloads.php#stable_release下载AspectJ的最新版本

1.2 AspectJ安装:启动命令行窗口,进入刚下载的aspectj-1.6.10.jar文件所在路径,输入如下命令:java -jar aspectj-1.6.10.jar,在弹出窗口中点击 Next --> 选择jdk安装路径 Next --> 选择安装目录 Install --> Next --> Finish。 

1.3 在Path环境变量中添加 ...\aspectj1.6\bin,在CLASSPATH环境变量中添加 ...\aspectj1.6\lib\aspectjrt.jar。

2. AspectJ基本使用

2.1 定义一个Java类 Hello.java

package lee;
public class Hello {
    //定义一个简单方法,模拟应用中的业务逻辑方法
    public void sayHello(){
        System.out.println("Hello AspectJ!");
    }
    public static void main(String[] args){
        Hello h = new Hello();
        h.sayHello();
    }
}

2.2 定义一个特殊的Java类(但并不是一个Java类),需要在执行sayHello()方法前启动事务,方法执行结束时关闭事务。

package lee;
public aspect TxAspect{
    //指定执行Hello.sayHello()方法时执行下面代码块
    void around():call(void Hello.sayHello()){
        System.out.println("开始事务...");
        proceed(); // 调用原来的sayHello()方法
        System.out.println("事务结束...");
    }
}

2.3 再定义一个特殊的Java类,在执行sayHello()方法后记录日志。

package lee;
public aspect LogAspect {
    //定义一个PointCut,其名为logPointcut
    //该PointCut对应于指定Hello对象的sayHello方法
    pointcut logPointcut() :execution(void Hello.sayHello());
    //在logPointcut之后执行下面代码块
    after():logPointcut(){
        System.out.println("记录日志...");
    }
}

注意:Java无法识别TxAspect.java文件的内容,所以我们要使用ajc.exe命令来编译上面的java内容:ajc -d . hello.java TxAspect.java。

三、Spring的AOP支持

    AOP从程序运行角度考虑程序的流程,提取业务处理过程的切面。AOP面向的是程序运行中各个步骤,希望以更好的方式来组合业务处理的各个步骤。框架具有两个特征:各步骤之间的良好隔离性;源代码无关性。

    Spring中AOP代理由Spring的IoC容器负责生成、管理,因此AOP代理可以直接使用容器中的其它Bean实例作为目标。Spring AOP使用纯Java实现,它不需要专门的编译过程,目前只支持将方法调用作为连接点。

1. 基于Annotation的零配置方式

1.1 定义一个Service接口

package test.service;
public interface Person {
    public String sayHello(String name);
    public void eat(String food);
}

1.2 实现Service接口

package test.service.impl;
import org.springframework.stereotype.*;
import test.service.Person;
@Component
public class Chinese implements Person {
    public String sayHello(String name) {
        return name + " Hello , Spring AOP";
    }
    public void eat(String food) {
        System.out.println("我正在吃:" + food);
    }
}

1.3 配置Spring配置文件 bean.xml

<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    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.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <!-- 指定自动搜索Bean组件、自动搜索切面类 -->
    <context:component-scan base-package="test.service,test.advice">
        <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
    </context:component-scan>
    <!-- 启动@AspectJ支持 -->
    <aop:aspectj-autoproxy/>
</beans>

1.4 定义一个用于Before增强处理的切面

package test.advice;
import org.aspectj.lang.annotation.*;
//定义一个切面
@Aspect
public class BeforeAdviceTest {
    // 匹配test.service.impl包下所有类的、所有方法的执行作为切入点
    @Before("execution(* test.service.impl.*.*(..))")
    public void authority() {
        System.out.println("1.模拟执行权限检查");
    }
}

1.5 测试

package test;
import org.springframework.context.*;
import org.springframework.context.support.*;
import test.service.*;

public class BeanTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        Person p = ctx.getBean("chinese", Person.class);
        System.out.println(p.sayHello("张三"));
        p.eat("西瓜");
    }
}

1.6 其它处理类型切面

AfterReturning增强处理:目标方法正常完成后被织入;

AfterThrowing增强处理:处理程序中未处理的异常;

After增强处理:目标方法正常和非正常结束都执行;

Around增强处理:等于Before增强处理和AfterReturning增强处理的总和;

2. 基于XML配置文件的管理方式

2.1 定义切面类1,Bean类同上

package test.advice;

import org.aspectj.lang.*;
import java.util.Arrays;

public class FourAdviceTest {
    public Object processTx(ProceedingJoinPoint jp) throws java.lang.Throwable {
        System.out.println("Around增强:执行目标方法之前,模拟开始事务...");
        // 访问执行目标方法的参数
        Object[] args = jp.getArgs();
        // 当执行目标方法的参数存在,
        // 且第一个参数是字符串参数
        if (args != null && args.length > 0
                && args[0].getClass() == String.class) {
            // 改变第一个目标方法的第一个参数
            args[0] = "被改变的参数";
        }
        // 执行目标方法,并保存目标方法执行后的返回值
        Object rvt = jp.proceed(args);
        System.out.println("Around增强:执行目标方法之后,模拟结束事务...");
        return rvt + " 新增的内容";
    }

    public void authority(JoinPoint jp) {
        System.out.println("②Before增强:模拟执行权限检查");
        // 返回被织入增强处理的目标方法
        System.out.println("②Before增强:被织入增强处理的目标方法为:"
                + jp.getSignature().getName());
        // 访问执行目标方法的参数
        System.out.println("②Before增强:目标方法的参数为:"
                + Arrays.toString(jp.getArgs()));
        // 访问被增强处理的目标对象
        System.out.println("②Before增强:被织入增强处理的目标对象为:" + jp.getTarget());
    }

    public void log(JoinPoint jp, Object rvt) {
        System.out.println("AfterReturning增强:获取目标方法返回值:" + rvt);
        System.out.println("AfterReturning增强:模拟记录日志功能...");
        // 返回被织入增强处理的目标方法
        System.out.println("AfterReturning增强:被织入增强处理的目标方法为:"
                + jp.getSignature().getName());
        // 访问执行目标方法的参数
        System.out.println("AfterReturning增强:目标方法的参数为:"
                + Arrays.toString(jp.getArgs()));
        // 访问被增强处理的目标对象
        System.out.println("AfterReturning增强:被织入增强处理的目标对象为:" + jp.getTarget());
    }

    public void release(JoinPoint jp) {
        System.out.println("After增强:模拟方法结束后的释放资源...");
        // 返回被织入增强处理的目标方法
        System.out.println("After增强:被织入增强处理的目标方法为:"
                + jp.getSignature().getName());
        // 访问执行目标方法的参数
        System.out.println("After增强:目标方法的参数为:" + Arrays.toString(jp.getArgs()));
        // 访问被增强处理的目标对象
        System.out.println("After增强:被织入增强处理的目标对象为:" + jp.getTarget());
    }
}

2.2 定义切面类2

package test.advice;

public class SecondAdviceTest {
    // 定义Before增强处理
    public void authority(String aa) {
        System.out.println("目标方法的参数为:" + aa);
        System.out.println("①号Before增强:模拟执行权限检查");
    }
}

2.3 Spring配置文件

<?xml version="1.0" encoding="GBK"?>
<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.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <aop:config>
        <!-- 将fourAdviceBean转换成切面Bean
            切面Bean的新名称为:fourAdviceAspect
            指定该切面的优先级为2 -->
        <aop:aspect id="fourAdviceAspect" ref="fourAdviceBean"
            order="2">
            <!-- 定义一个After增强处理,
                直接指定切入点表达式
                以切面Bean中的release()方法作为增强处理方法 -->
            <aop:after pointcut="execution(* org.crazyit.app.service.impl.*.*(..))"
                method="release"/>
            <!-- 定义一个Before增强处理,
                直接指定切入点表达式
                以切面Bean中的authority()方法作为增强处理方法 -->
            <aop:before pointcut="execution(* org.crazyit.app.service.impl.*.*(..))"
                method="authority"/>
            <!-- 定义一个AfterReturning增强处理,
                直接指定切入点表达式
                以切面Bean中的log()方法作为增强处理方法 -->
            <aop:after-returning pointcut="execution(* org.crazyit.app.service.impl.*.*(..))"
                method="log" returning="rvt"/>
            <!-- 定义一个Around增强处理,
                直接指定切入点表达式
                以切面Bean中的processTx()方法作为增强处理方法 -->
            <aop:around pointcut="execution(* org.crazyit.app.service.impl.*.*(..))"
                method="processTx"/>
        </aop:aspect>

        <!-- 将secondAdviceBean转换成切面Bean
            切面Bean的新名称为:secondAdviceAspect
            指定该切面的优先级为1,该切面里的增强处理将被优先织入 -->
        <aop:aspect id="secondAdviceAspect" ref="secondAdviceBean"
            order="1">
            <!-- 定义一个Before增强处理,
                直接指定切入点表达式
                以切面Bean中的authority()方法作为增强处理方法
                且该参数必须为String类型(由authority方法声明中msg参数的类型决定) -->
            <aop:before pointcut="execution(* org.crazyit.app.service.impl.*.*(..)) and args(aa)"
                method="authority"/>
        </aop:aspect>
    </aop:config>
    <!-- 定义一个普通组件Bean -->
    <bean id="chinese"
    class="org.crazyit.app.service.impl.Chinese"/>
    <!-- 定义一个普通Bean实例,该Bean实例将被作为Aspect Bean -->
    <bean id="fourAdviceBean"
    class="org.crazyit.app.advice.FourAdviceTest"/>
    <!-- 再定义一个普通Bean实例,该Bean实例将被作为Aspect Bean -->
    <bean id="secondAdviceBean"
    class="org.crazyit.app.advice.SecondAdviceTest"/>
</beans>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值