spring相关spring aopspringboot

四、Spring的AOP

2017-03-10  本文已影响626人  数独题

AOP实现可分为两类(按AOP框架修改源代码的时机):

AOP的基本概念:

AOP框架具有如下两个特征:

关于面向切面编程的一些术语:

pointcut xxxPointcut():execution(void H*.say*())

如何使用表达式定义切入点是AOP的核心,Spring默认使用AspectJ切入点语法:

Spring的AOP支持:

Spring中的AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。AOP代理可以直接使用容器中的其他Bean实例作为目标,这种关系可以由IOC容器的依赖注入提供。Spring默认使用Java动态代理来创建AOP代理。Spring目前仅支持将方法调用作为连接点(Joinpoint),如果需要把对成员变量的访问和更新也作为增强处理的连接点,则可以考虑使用AspectJ。Spring侧重于AOP实现和IOC容器之间的整合,用于帮助解决企业级开发中常见问题。Spring AOP采用基于代理的AOP实现方案,而AspectJ则采用编译时增强的解决方案。
AOP编程中需要程序员参与的只有三个部分:

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

Spring有如下两种选择来定义切入点和增强处理:

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

Spring依然采用运行时生成动态代理的方式来增强目标对象,所以它不需要增加额外的编译,也不需要AspectJ的织入器支持;而AspectJ采用编译时增强,所以AspectJ需要自己的编译器来编译Java文件,还需要织入器。
Spring中启用对@AspectJ切面配置的支持:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
        
        <!-- 启动@AspectJ支持 -->
        <aop:aspectj-autoproxy/>
      
</beans>

以及

<!-- 启动@AspectJ支持 -->
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>

Spring应用中启动@AspectJ支持还需要在应用的类加载路径下添加AspectJ库:

1、定义切面:

当启动了@AspectJ支持后,只要在Spring容器中配置一个带@AspectJ注解的Bean,Spring将会自动识别该Bean,并将该Bean作为切面处理。

//使用@AspectJ定义一个切面类
@Aspect
public class LogAspect{
   //定义该类的其他类容
   .......
}

当使用@AspectJ来修饰一个Java类之后,Spring不会把该Bean当成组件Bea处理,因此负责增强后处理的Bean将会略过该Bean,不会对该Bean进行任何增强处理。

2、定义Before增强处理:

在一个切面类里使用@Before来修饰一个方法时,该方法将作为Before增强处理。使用@Before修饰时,通常需要指定一个value属性,该属性指定一个切入点表达式(既可以是一个已有的切入点,也可以直接定义切入点表达式),用于指定该增强处理将被织入哪些切入点。

AuthAspect.java

package entity;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AuthAspect {

    //匹配entity包下所有的类的所有方法作为切入点
    @Before("execution(* entity.*.*(..))")
    public void authority(){
        System.out.println("模拟执行权限检查!");
    }
}

HelloImpl.java

package entity;



import org.springframework.stereotype.Component;

import inter.Hello;
@Component("hello")
public class HelloImpl implements Hello{

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

}

beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
        <!-- 启动@AspectJ支持 -->
         <aop:aspectj-autoproxy/>
        <!-- 指定自动搜索Bean文件,自动搜索切面文件 -->
        <context:component-scan base-package="entity">
           <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
        </context:component-scan>
</beans>

AspectjTest.java

package test;





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

import entity.HelloImpl;

public class AspecjTest {
    public static void main(String[] args) {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
        HelloImpl hello=(HelloImpl) ctx.getBean("hello");
        hello.foo();
        hello.addUser("张三", "123456");
    }
    
}

输出:

模拟执行权限检查!
执行Hello组件的foo()方法
模拟执行权限检查!
执行Hello组件的addUser添加用户:张三

使用Before增强处理只能在目标方法执行之前织入增强,如果Before增强处理没有特殊处理,目标方法总会自动执行,如果Before处理需要阻止目标方法的执行,可通过抛出一个异常来实现。Before增强处理时,目标方法还未获得执行的机会,所以Before增强处理无法访问目标方法的返回值。

3、定义AfterReturning增强处理:

AfterReturning增强处理将在目标方法正常完成后被织入。
@AfterReturning注解可指定如下两个常用属性:

//定义一个切面
@Aspect
public class LogAspect{
  //匹配entity包下所有类的所有方法的执行作为切入点
  @AfterReturning(returning="rvt",pointcut="execution(* entity.*.*())")
  //声明rvt时指定的类型会限制目标方法必须返回指定类型的值或没有返回值
  //此处将rvt的类型声明为Object,意味着对目标方法的返回值不加限制
  public void log(Object rvt){
       System.out.println("获得目标方法返回值"+rvt);
       System.out.println("模拟记录日志功能....");
   }
}

@AfterReturning注解的returning属性所指定的形参名对应于增强处理中的一个形参名,当目标方法执行返回后,返回值作为相应的参数值传入增强处理方法。使用returning属性还有 一个额外的作用:它可用于限定切入点只匹配具有对应返回值类型的方法。

4、定义AfterThrowing增强处理:

AfterThrowing增强处理主要用于处理程序中未处理的异常。
使用@AfterThrowing注解时可指定如下两个常用属性:

//定义一个切面
@Aspect
public class RepairAspect{
  //匹配entity包下所有类的所有方法的执行作为切入点
  @AfterThrowing(throwing="ex",pointcut="execution(* entity.*.*())")
  //声明ex时指定的类型会限制目标方法必须抛出指定的类型的异常
  //此处将ex的类型声明为Throwable,意味着对目标方法抛出的异常不加限制
  public void doRecoveryActions(Throwable ex){
       System.out.println("目标方法中抛出的异常"+ex);
       System.out.println("模拟Advice对异常的修复....");
   }
}

使用throwing属性还用一个额外的作用:它可用于限定切入点只匹配指定类型的异常。

5、After增强处理:

After增强处理必须准备处理正常返回或异常返回两种情况,这种处理通常用于释放资源。使用@After注解修饰一个方法,即可将该方法转成After增强处理。使用@After注解时需要指定一个value属性,该属性值用于指定该增强处理被织入的切入点。

//定义一个切面
@Aspect
public class ReleaseAspect{
    //匹配entity包下所有的类的所有方法的执行作为切入点
    @After("execution(* entity.*.*())")
    public void release(){
        System.out.println("模拟方法结束后的释放资源...");
    }
}

After增强处理的作用非常类似于异常处理中的finally块的作用。

6、Around增强处理:

@Around注解用于修饰Around增强处理,Around增强处理是功能较强大得增强处理,它近似等于Before和AfterReturning增强处理的总和,Around增强处理即可在执行目标方法之前织入增强动作,也可以在执行目标方法之后织入增强处理。
Around增强处理可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标方法的执行。Around增强处理的功能虽然强大,但通常需要在线程安全的环境下使用。如果需要目标方法执行之前和执行之后共享某种状态数据,则该考虑使用Around增强处理;尤其是需要改变目标方法的返回值时,则只能使用Around增强处理。
当定义一个Around增强处理方法时,该方法的第一个形参必须是ProceedingJoinPoint类型(至少包含一个形参),在增强处理方法体内,调用ProceedingJoinPoint参数的proceed()方法才会执行目标方法---这就是Around增强处理可以完全控制目标方法的执行时机、如何执行的关键;如果程序没有调用proceedingJoinPoint参数的proceed()方法,则目标方法不会被执行。
使用Around增强处理可以取得目标方法最大的控制权,既可以完全控制目标方法的执行,一刻改变执行目标方法的参数,还可改变目标方法的返回值。

7、访问目标方法的参数:

访问目标方法最简单的做法是定义增强处理方法时将第一个参数定义为JoinPoint类型,当该增强处理方法被调用时,该JoinPoint参数就代表了织入增强的连接点。JoinPoint里包含如下几个常用的方法:

当使用Around增强处理是,需要将第一个参数定义为ProceedingJoionPoint类型,该类型是JoinPoint类型的子类。

Spring AOP 采用和AspectJ一样的优先顺序来织入增强处理:在“进入连接点时,具有最高优先级的增强处理将先被织入。在“退出”连接点时,具有最高优先级的增强处理会最后被织入。
当不同切面的两个增强处理需要在同一个连接点被织入时,Spring AOP将以随机的顺序来织入这两个增强处理。如果应用需要指定不同切面类里增强处理的优先级,Spring提供了如下两种解决方案:

8、定义切入点:

定义切入点,其实质就是为一个切入点表达式起一个名称,从而允许在多个增强处理中重用该名称。Spring AOP只支持将Spring Bean的方法执行作为连接点,所以可以吧切入点看成所有能和切入点表达式匹配的Bean方法,切入点定义包含两个部分:

//使用@Pointcut注解定义切入点
@Pointcut("execution(* transfer(..))")
//使用一个返回值为void、方法体为空的方法来命名切入点
private void anyOldTransfer(){...}

如果需要使用本切面中的切入点,则可在使用@Before、@After、@Around等注解来定义Advice时,使用pointcut或value属性值引入已有的切入点。

@AfterReturning(pointcut="myPointcut()",returning="retVal")
public void writeLog(String msg,Object retVal){...}

使用其他切面类中的切入点时,应该使用切面类作为前缀来限制切入点。

9、切入点指示符:

//在entity包中的任意连接点(在Spring AOP中只是方法执行的连接点)
within(entity.*)
//在entity包或子包中的任意连接点(在Spring AOP中只是方法执行的连接点)
within(entity..*)
//匹配tradeService Bean实例内方法执行的连接点
bean(tradeService)
//匹配名字以Service结尾的Bean实例内方法执行的连接点
bean(*Service)

10、组合切入点表达式:

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

上一篇下一篇

猜你喜欢

热点阅读