Spring AOP的使用以及案例


     SpringAOP:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率,在springAOP中业务逻辑仅仅只关注业务本身,将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
       AOP有两种实现方式一种是基于XML的文件配置方式另一种是基于注解的方式下面大概总结下两种方式的使用:
   一.基于注解方式的使用
   1.在切面类(为切点服务的类)前用@Aspect注释修饰,声明为一个切面类。
   2.用@Pointcut注释声明一个切点,目的是为了告诉切面,谁是它的服务对象。(此注释修饰的方法的方法体为空,不需要写功能比如 public void say(){};就可以了,方法名可以被候命的具体服务功能所以引用,它可以被理解为切点对象的一个代理对象方法)也可以不用这个直接在通知类型中声名。
   3.在对应的方法前用对应的通知类型注释修饰,将对应的方法声明称一个切面功能,为了切点而服务
   
   注解方式声明aop
    1.用@Aspect注解将类声明为切面(如果用@Component("")注解注释为一个bean对象,那么就要在spring配置文件中开启注解扫描,<context:component-scan base-package="com.package包名"/>
          否则要在spring配置文件中声明一个bean对象)
    2.在切面需要实现相应方法的前面加上相应的注释,也就是通知类型。
    3.此处有环绕通知,环绕通知方法一定要有ProceedingJoinPoint类型的参数传入,然后执行对应的proceed()方法,环绕才能实现。
    @Aspect:作用是把当前类标识为一个切面供容器读取
 
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
    
    @Around:环绕增强,相当于MethodInterceptor
    @AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
    @Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
    @AfterThrowing:异常抛出增强,相当于ThrowsAdvice   //配置抛出异常后通知,使用在切面方法上注册的切入点
    @After: final增强,不管是抛出异常或者正常退出都会执行
    
    
    Component("annotationTest")
     @Aspect
     public class AnnotationTest {
     //@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。

     //定义切点
     @Pointcut("execution(com.cj.*.*(..)))") //切入点表达式  (..)代表后面的子包方法
     public void sayings(){ //切入点签名
     }
     
     //前置通知   也可以不用切点直接用 @Before("execution(* *.saying(..))")和下面作用是一样的
     @Before("sayings()")
        public void sayHello(){
        System.out.println("注解类型前置通知");
    }
    //后置通知
     @After("sayings()")
     public void sayGoodbey(){
        System.out.println("注解类型后置通知");
     }
     
     //环绕通知。注意要有ProceedingJoinPoint参数传入。 环绕通知就是前置通知和后置通知都有,其中proceed为执行方法的过程
     @Around("sayings()")
     public void sayAround(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("注解类型环绕通知..环绕前");
        pjp.proceed();//执行方法
        System.out.println("注解类型环绕通知..环绕后");
     }
     
     下面是例2:
     public static final String POINT = "execution(* com.oppo.bot.voice.center.impl.*.*(..)) || execution(* com.oppo.bot.voice.asr.impl.*.*(..))"; 定义切面为两个不同包下面的所有class类都为切面

     
     @Around(POINT) 
     public Object timeAround(ProceedingJoinPoint joinPoint) { 
     // 定义返回对象、得到方法需要的参数     
     Object obj = null;    
     //得到方法需要的参数 ,调用切面方法传过来的参数
     Object[] args = joinPoint.getArgs();  
     long startTime = System.currentTimeMillis(); 
     System.out.println("切面运行之前");     
     try { 
     System.out.println("开始运行自身逻辑"); 
     obj = joinPoint.proceed(args);  //执行方法   
     System.out.println("切面运行之后"); 
     } catch (Throwable e) {
     logger.error("统计某方法执行耗时环绕通知出错{}", e);       
     throw new InternalSystemException(Constants.BOT_VOICE_CORE, "统计某方法执行耗时环绕通知出错...", e);     }  
     // 获取执行的方法名     
     MethodSignature signature = (MethodSignature) joinPoint.getSignature(); 
     long endTime = System.currentTimeMillis();   
    //signature.getDeclaringTypeName()获取类名     signature.getName()获取方法名
     String methodName = signature.getDeclaringTypeName() + "." + signature.getName();      
     // 打印耗时的信息    
          System.out.println(endTime-startTime+":运行时间");      
     return obj; }
     
     1.@PointCut 注解的方法不会被执行,只起到了一个把切面表达式抽象出来的作用。

     2.@AfterReturning是最常用的:
     3.@AfterReturning注解中的returning = "object"应该和形参的object名字一致 ,用来接收目标方法的返回值。目标方法返回值抽象为Object对象
     
     @AfterReturning(pointcut="execution(...) " returning="object")
      public void afterReturning(JointPoint jp,Object object){ 
     //注意这里的object 应该和returning="object"保持一致
      System.out.println(object); //object是目标方法返回的参数
      System.out.println(jp.getArgs() ); //通过这种方法可以获取目标方法的传入参数   
}

point也可以用注解实现

先声明一个注解方法:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface TraceRequest {}

然后在point中使用@annotation声明

private final static String POINT = "@annotation(com.oppo.bot.classifier.common.log.TraceRequest)";

@AfterReturning(pointcut = "contentFilterPointcut()", returning = "ret")
public void afterReturningContentFilter(Object ret) {
    Stopwatch stopwatch = Stopwatch.createStarted();
    FilteredContent.FilteredContentBuilder builder = null;
    String customerId = null

}

@AfterReturning为方法执行完之后执行,returning = "ret"代表方法返回结果,放在ret对象中

表示point为使用了该注解的所有方法。


  二:基于xml配置的AOP实现:
  
   <?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        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-3.0.xsd">


    <!-- 系统服务组件的切面Bean -->
    <bean id="serviceAspect" class="cn.ysh.studio.spring.aop.aspect.ServiceAspect"/>
    <!-- AOP配置 -->
    <aop:config>
        <!-- 声明一个切面,并注入切面Bean,相当于@Aspect -->
        <aop:aspect id="simpleAspect" ref="serviceAspect">
            <!-- 配置一个切入点,相当于@Pointcut -->
            <aop:pointcut expression="execution(* cn.ysh.studio.spring.aop.service..*(..))" id="simplePointcut"/>
            <!-- 配置通知,相当于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
            <aop:before pointcut-ref="simplePointcut" method="before"/>
            <aop:after pointcut-ref="simplePointcut" method="after"/>
            <aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
            <aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
        </aop:aspect>
    </aop:config>

</beans>

相关的学习链接为:

@Pointcut()的execution、@annotation等参数说明_java_green_hand0909的博客-CSDN博客

Spring AOP一个用于实现面向切面编程的框架。它可以通过配置来实现横切关注点的模块化,并将其应用到程序的不同部分。Spring AOP 使用 AspectJ 切入点指示符来定义切入点表达式,用于匹配方法执行连接点。Spring AOP 支持以下 AspectJ 切入点指示符: 1. execution:用于匹配方法执行连接点。这是使用 Spring AOP 时要使用的主要切入点指示符。 2. within:限制匹配以连接某些类型的点(使用 Spring AOP 时在匹配类型声明的方法的执行)。 3. this:限制匹配到连接点(使用 Spring AOP 时方法的执行),其 Bean 引用(Spring AOP 代理)是给定类型的实例。 4. target:限制匹配到连接点(使用 Spring AOP 时方法的执行),其目标对象(正在代理的应用程序对象)是给定类型的实例。 5. args:限制匹配到连接点(使用 Spring AOP 时方法的执行),其参数是给定类型的实例。 6. @target:限制匹配到连接点(使用 Spring AOP 时方法的执行),其执行对象的类具有给定类型的注释。 7. @args:限制匹配到连接点(使用 Spring AOP 时方法的执行),其传递的实际参数的运行时类型具有给定类型的注释。 8. @within:限制匹配以连接具有给定注释的类型的点(使用 Spring AOP 时在具有给定注释的类型声明的方法的执行)。 9. @annotation:限制匹配到连接点的主题(在 Spring AOP 运行的方法)具有给定注释的连接点。 在使用 Spring AOP 时,需要引入 Spring AOPSpring Context 相关的包,并在配置文件进行相应的配置。可以通过 Maven 或其他构建工具来引入相关依赖。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值