Spring核心概念<二>:AOP面向切面编程


Spring核心概念<二>:AOP面向切面编程

概述

AOP和OOP的区别:面向对象编程将程序分解为各个层次的对象,而面向切面编程将程序运行过程分解为各个切面。可以理解为oop是从静态角度考虑程序结构的,而aop是从动态角度考虑程序结构的

AOP的来源:当有多个对象同时复用一段相同的代码段时,传统的使用方法调用的方式无法避免耦合,所以出现了aop实现不同调用方法与复用代码块之间的解耦合。Aop专门用于处理系统中分布于各个模块中的交叉关注点的问题,常常使用AOP来处理一些具有横切性质的系统级服务,如事务管理,安全检查,缓存,对象池管理

AOP的目标:在不修改源代码的前提下,为系统多个组件的多个方法添加某种通用的功能。

AOP分类:静态aop(AspectJ),编译时增强/动态aop(Spring AOP)运行时增强

基本概念

aop代理:aop框架创建的对象,简单来说,代理就是对目标对象的增强。

织入(weaving):将增强处理添加到目标对象。根据使用框架的不同可以细分为运行时增强和编译时增强

AOP代理方法 = 增强处理 + 目标对象的方法

对于开发者来说,主要参与

  1. 定义普通业务组件

  2. 定义切入点,一个切入点可横切多个页面

  3. 定义增强处理

基于注解的“零配置”方式/xml配置方式

step1 启动Spring对@AspectJ的支持,需要在Spring配置文件中添加如下片段:

<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 配置@AspectJ支持 -->
<aop:aspecj-autoproxy>
</beans>

step2 定义切面

@Aspect
public class logPrint{
……
}

step3 增强处理

  • 定义before增强处理:每次指向目标方法之前都会执行before增强处理方法

定义一个切面类

import org.aspectj.lang.annotation.*;
import org.aspectj.lang.*;
@Aspect
public class AuthAspect
{
// 所有方法的执行作为切入点
@Before("execution(* org.crazyit.app.service.impl.*.*(..))")
public void authority()
{
    System.out.println("模拟执行权限检查");
}
}

定义一个普通方法

package org.crazyit.app.service.impl;

import org.springframework.stereotype.Component;

import org.crazyit.app.service.*;

public class HelloImpl implements Hello
{
// 定义一个简单方法,模拟应用中的业务逻辑方法
public void foo()
{
    System.out.println("执行Hello组件的foo()方法");
}
// 定义一个addUser()方法,模拟应用中的添加用户的方法
public int addUser(String name , String pass)
{
    System.out.println("执行Hello组件的addUser添加用户:" + name);
    return 20;
}
}

配置beans.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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 指定自动搜索Bean组件、自动搜索切面类 -->
<context:component-scan base-package="org.crazyit.app.service
    ,org.crazyit.app.aspect">
    <context:include-filter type="annotation"
        expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
<!-- 启动@AspectJ支持 -->
<aop:aspectj-autoproxy/>

通过以上三个步骤,即:定义切面类,切入点和增强方法;定义普通组件;配置beans,就可以实现一个简单的before增强处理的aop配置

  • 定义AfterReturning增强处理

  • 定义AfterThrowing增强处理

  • After增强处理(注:与AfterReturning的区别在于,后者无论正常结束或者意外结束方法都会进行后处理)

-Aroud增强处理

可以看作是Before增强处理和AfterRerturning增强处理的总和,但是更强大,强大之处在于Around增强处理可以决定目标方法在什么时候处理,如何处理,甚至完全阻止目标方法的执行。它也可以改变目标方法的参数值和返回值

package org.crazyit.app.aspect;

import org.aspectj.lang.annotation.*;
import org.aspectj.lang.*;


// 定义一个切面
@Aspect
public class TxAspect
{
// 匹配org.crazyit.app.service.impl包下所有类的、
// 所有方法的执行作为切入点
@Around("execution(* org.crazyit.app.service.impl.*.*(..))")
public Object processTx(ProceedingJoinPoint jp)
    throws java.lang.Throwable
{
    System.out.println("执行目标方法之前,模拟开始事务...");
    // 获取目标方法原始的调用参数
    Object[] args = jp.getArgs();
    if(args != null && args.length > 1)
    {
        // 修改目标方法的第一个参数
        args[0] = "【增加的前缀】" + args[0];
    }
    // 以改变后的参数去执行目标方法,并保存目标方法执行后的返回值
    Object rvt = jp.proceed(args);
    System.out.println("执行目标方法之后,模拟结束事务...");
    // 如果rvt的类型是Integer,将rvt改为它的平方
    if(rvt != null && rvt instanceof Integer)
        rvt = (Integer)rvt * (Integer)rvt;
    return rvt;
    }
}

如上所示,Spring的第一个参数一定是ProceedingJoinPoint jp,通过他可以取得目标对象[包括通过getArgs()取得参数,通过proceed()取得返回值];调用它的procees()方法,目标方法才会被执行 。

  • 取得目标方法的参数 主要包括三个方法Object[] getArgs()/Signature getSignature()/Object getTarget() 分别表示取得目标对象的参数,取得目标对象的签名,取得被增强的目标对象

    package org.crazyit.app.aspect;
    
    import org.aspectj.lang.annotation.*;
    import org.aspectj.lang.*;
    import java.util.Arrays;
    @Aspect
    public class FourAdviceTest
    {
    // 定义Around增强处理
    @Around("execution(* org.crazyit.app.service.impl.*.*(..))")
    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] = "【增加的前缀】" + args[0];
        }
        //执行目标方法,并保存目标方法执行后的返回值
        Object rvt = jp.proceed(args);
        System.out.println("Around增强:执行目标方法之后,模拟结束事务...");
        // 如果rvt的类型是Integer,将rvt改为它的平方
        if(rvt != null && rvt instanceof Integer)
            rvt = (Integer)rvt * (Integer)rvt;
        return rvt;
    }
    // 定义Before增强处理
    @Before("execution(* org.crazyit.app.service.impl.*.*(..))")
    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());
    }
    //定义AfterReturning增强处理
    @AfterReturning(pointcut="execution(* org.crazyit.app.service.impl.*.*(..))"
        , returning="rvt")
    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());
    }
    
    // 定义After增强处理
    @After("execution(* org.crazyit.app.service.impl.*.*(..))")
    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());
        }
    }
    

step4 指定不同切面类里增强处理的优先级

  • 让切面实现org.springframework.core.Ordered接口,实现给该接口只需实现一个int getOrder()方法

  • 直接时候Order注解来修饰一个切面类,指定一个int型的value属性最为优先级,该属性越小优先级越高

step5 定义切入点:切入点的定义包含两个部分 切入点表达式+包含任意名字和参数的方法签名

@PointCut("execution(* transfer(..))") 
private void anyOldTransfer(){}

上面定义了一个名为anyOlderTransfer的切入点,可以匹配任何以transfer结尾的方法

@AfterReturning(pointcut="anyOldTransfer()",returning="retval")
public void writing(String msg,Object retval)
{
...
}

step6 切入点指示符

  1. execution匹配执行方法连接点

  2. within用于限定匹配特定类型的连接点

  3. this用于限定aop代理必须是制定类型的实例

  4. target用于限定目标对象必须是指定类型的实例

  5. args用于对连接点参数类型进行限制

step7 组合切入点 :&& || ! 分别表示与或非

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值