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

来源:互联网 发布:sopcast网络电视官网 编辑:程序博客网 时间:2024/06/06 23:58

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/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsd"><!-- 配置@AspectJ支持 --><aop:aspecj-autoproxy></beans>

step2 定义切面

@Aspectpublic class logPrint{……}

step3 增强处理

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

定义一个切面类

import org.aspectj.lang.annotation.*;import org.aspectj.lang.*;@Aspectpublic 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.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/aophttp://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.*;// 定义一个切面@Aspectpublic 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;@Aspectpublic 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 组合切入点 :&& || ! 分别表示与或非

原创粉丝点击