JAVA语言—AOP基础

1、AOP概述

AOP:AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。

场景:案例部分功能运行较慢,定位执行耗时较长的方法,此时需要统计每一个业务方法的执行耗时。

模板方法:即将计算业务执行的时间的功能抽取为一个模板,这个模版可以计算每一个业务的运行时间。

实现:动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层动态代理机制,对特定的方法进行编程。

2、AOP快速入门

2.1 导入依赖

在pom.xml中导入AOP的依赖

        <!--aop相关依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.2 编写AOP程序

针对于特定的方法根据业务需要进行编程,即创建一个相关类,将此类通过注解 @Component 声明为IOC容器的Bean对象,通过 @Aspect 告知项目此类为AOP类。

package com.itheima.tliaswebmanagement.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Slf4j //日志注解
@Component //声明为IOC容器的Bean对象
@Aspect //告知项目此类为AOP类
public class TimeAspect {

    @Around("execution(* com.itheima.tliaswebmanagement.service.*.*(..))" ) //切入点表达式
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
        //1.记录开始时间
        long begin = System.currentTimeMillis();

        //2.运行原始方法
        Object result = joinPoint.proceed();

        //3.记录结束时间,计算方法执行耗时
        long end = System.currentTimeMillis();
        log.info(joinPoint.getSignature() + "方法执行耗时: {}ms",end-begin);

        return result;
    }
}

2.3 AOP核心概念

连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)

通知:Advice,指哪些重复的逻辑,也就是共性功能

切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用

切面:Aspect,通知与切入点的对应关系

目标对象:Target,通知所应用的对象

3、AOP进阶

3.1 通知类型

@Around:环绕通知,此注解标注的通知方法在目标前、后都被执行

@Before:前置通知,此注解标注的通知方法在目标前被执行

@After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会被执行

@AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行

@AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行

(源代码如下)

package com.itheima.tliaswebmanagement.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
public class MyAspect1 {

    //解决代码冗余问题
    @Pointcut("execution(* com.itheima.tliaswebmanagement.service.impl.DeptServiceImpl.*(..))")
    private void pt(){}

    @Before("pt()")
    public void before(){
        log.info("before...");
    }

    @Around("execution(* com.itheima.tliaswebmanagement.service.impl.DeptServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("around before ...");

        //调用目标对象的原始方式执行
        Object result = proceedingJoinPoint.proceed();

        log.info("around after ...");
        return result;
    }

    @After("execution(* com.itheima.tliaswebmanagement.service.impl.DeptServiceImpl.*(..))")
    public void after(){
        log.info("after ...");
    }

    //原始方法正常的执行并且返回
    @AfterReturning("execution(* com.itheima.tliaswebmanagement.service.impl.DeptServiceImpl.*(..))")
    public void afterReturn(){
        log.info("afterReturn ...");
    }

    //原始方法返回异常
    @AfterThrowing("execution(* com.itheima.tliaswebmanagement.service.impl.DeptServiceImpl.*(..))")
    public void afterThrowing(){
        log.info("afterThrowing ...");
    }


}

3.2 通知顺序

当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行,那么切面的切入点方法执行顺序是怎样的?

答案:

        没有注解;首先是根据切面类的类名排序。

        有注解@Order(数字);根据注解的数字大小排序。

图3.2-1 注解@Order(数字)

3.3 切入点表达式

切入点表达式:描述切入点方法的一种表达式

作用:主要用来决定项目中的哪些方法需要加入通知

常见形式:

        1. execution(...) :根据方法的签名来匹配

        2. @annotation(...) :根据注解匹配       

3.3.1 切入点表达式-execution
图3.3.1-1 切入点表达式

图3.3.1-2 切入点表达式通配符
3.3.2 切入点表达式-annotation
图3.3.2-1 切入点表达式

3.4 连接点

在Spring中用JoinPoint抽象了连接点,用它可以获取方法执行时的相关信息,例如目标类名、方法名、方法参数等。

1. 对于@Around 通知,获取连接点信息只能使用 ProceedingJoinPoint

2. 对于其他四种通知,获取连接点信息只能使用 JointPoint,它是ProceedingJoinPoint 的父类型

图3.4-1 连接点项目代码

上图使用了参数:ProceedingJoinPoint 的各种接口,用来获取目标对象的各种信息,具体的介绍在图中已经做了注解。

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目前整个开发社区对AOP(Aspect Oriented Programing)推崇备至,也涌现出大量支持AOP的优秀Framework,--Spring, JAC, Jboss AOP 等等。AOP似乎一时之间成了潮流。Java初学者不禁要发出感慨,OOP还没有学通呢,又来AOP。本文不是要在理论上具体阐述何为AOP, 为何要进行AOP . 要详细了解学习AOP可以到它老家http://aosd.net去瞧瞧。这里只是意图通过一个简单的例子向初学者展示一下如何来进行AOP.   为了简单起见,例子没有没有使用任何第三方的AOP Framework, 而是利用Java语言本身自带的动态代理功能来实现AOP.   让我们先回到AOP本身,AOP主要应用于日志记录,性能统计,安全控制,事务处理等方面。它的主要意图就要将日志记录,性能统计,安全控制等等代码从商业逻辑代码中清楚的划分出来,我们可以把这些行为一个一个单独看作系统所要解决的问题,就是所谓的面向问题的编程(不知将AOP译作面向问题的编程是否欠妥)。通过对这些行为的分离,我们希望可以将它们独立地配置到商业方法中,而要改变这些行为也不需要影响到商业方法代码。   假设系统由一系列的BusinessObject所完成业务逻辑功能,系统要求在每一次业务逻辑处理时要做日志记录。这里我们略去具体的业务逻辑代码。 Java代码 public interface BusinessInterface {  public void processBusiness(); } public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   try {    logger.info("start to processing...");    //business logic here.    System.out.println(“here is business logic”);    logger.info("end processing...");   } catch (Exception e){    logger.info("exception happends...");    //exception handling   }  } } public interface BusinessInterface {  public void processBusiness(); } public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   try {    logger.info("start to processing...");    //business logic here.    System.out.println(“here is business logic”);    logger.info("end processing...");   } catch (Exception e){    logger.info("exception happends...");    //exception handling   }  } }   这里处理商业逻辑的代码和日志记录代码混合在一起,这给日后的维护带来一定的困难,并且也会造成大量的代码重复。完全相同的log代码将出现在系统的每一个BusinessObject中。   按照AOP的思想,我们应该把日志记录代码分离出来。要将这些代码分离就涉及到一个问题,我们必须知道商业逻辑代码何时被调用,这样我们好插入日志记录代码。一般来说要截获一个方法,我们可以采用回调方法或者动态代理。动态代理一般要更加灵活一些,目前多数的AOP Framework也大都采用了动态代理来实现。这里我们也采用动态代理作为例子。   JDK1.2以后提供了动态代理的支持,程序员通过实现java.lang.reflect.InvocationHandler接口提供一个执行处理器,然后通过java.lang.reflect.Proxy得到一个代理对象,通过这个代理对象来执行商业方法,在商业方法被调用的同时,执行处理器会被自动调用。   有了JDK的这种支持,我们所要做的仅仅是提供一个日志处理器。 Java代码 public class LogHandler implements InvocationHandler {  private Logger logger = Logger.getLogger(this.getClass().getName());   private Object delegate;   public LogHandler(Object delegate){    this.delegate = delegate;   }  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   Object o = null;   try {    logger.info("method stats..." + method);    o = method.invoke(delegate,args);    logger.info("method ends..." + method);   } catch (Exception e){    logger.info("Exception happends...");    //excetpion handling.   }   return o;  } }   现在我们可以把BusinessObject里面的所有日志处理代码全部去掉了。 public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   //business processing   System.out.println(“here is business logic”);  } } public class LogHandler implements InvocationHandler {  private Logger logger = Logger.getLogger(this.getClass().getName());   private Object delegate;   public LogHandler(Object delegate){    this.delegate = delegate;   }  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   Object o = null;   try {    logger.info("method stats..." + method);    o = method.invoke(delegate,args);    logger.info("method ends..." + method);   } catch (Exception e){    logger.info("Exception happends...");    //excetpion handling.   }   return o;  } }   现在我们可以把BusinessObject里面的所有日志处理代码全部去掉了。 public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   //business processing   System.out.println(“here is business logic”);  } }   客户端调用商业方法的代码如下: Java代码 BusinessInterface businessImp = new BusinessObject(); InvocationHandler handler = new LogHandler(businessImp); BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(  businessImp.getClass().getClassLoader(),  businessImp.getClass().getInterfaces(),  handler); proxy.processBusiness(); BusinessInterface businessImp = new BusinessObject(); InvocationHandler handler = new LogHandler(businessImp); BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(  businessImp.getClass().getClassLoader(),  businessImp.getClass().getInterfaces(),  handler); proxy.processBusiness();   程序输出如下: INFO: method stats... here is business logic INFO: method ends...   至此我们的第一次小尝试算是完成了。可以看到,采用AOP之后,日志记录和业务逻辑代码完全分开了,以后要改变日志记录的话只需要修改日志记录处理器就行了,而业务对象本身(BusinessObject)无需做任何修改。并且这个日志记录不会造成重复代码了,所有的商业处理对象都可以重用这个日志处理器。   当然在实际应用中,这个例子就显得太粗糙了。由于JDK的动态代理并没有直接支持一次注册多个InvocationHandler,那么我们对业务处理方法既要日志记录又要性能统计时,就需要自己做一些变通了。一般我们可以自己定义一个Handler接口,然后维护一个队列存放所有Handler, 当InvocationHandler被触发的时候我们依次调用自己的Handler。所幸的是目前几乎所有的AOP Framework都对这方面提供了很好的支持.这里推荐大家使用Spring。
Spring AOP依赖的 jar 包是指在使用 Spring AOP 时所需要的相关的 jar 文件。根据引用,Spring AOP 版本为 2.5.6,如果需要可以下载并使用该版本的 jar 包。请注意,这里的 jar 包指的是 Spring AOP 的依赖库,而非 Spring 框架本身。 另外,引用提到了 AOP 编程思想不是 Spring 独有的,而是 Spring 支持的一种编程思想。所以在理解 Spring AOP 时,要注意区分 AOP 编程思想与 Spring 框架的关系。 对于 Spring AOP 底层的实现方式,引用中提到,默认情况下,Spring 使用 Java 的动态代理来创建 AOP 代理。如果需要代理的是类而不是接口,Spring 会自动切换成 CGLIB 代理。当然,也可以强制使用 CGLIB 代理。此外,Spring 的 AOP 还需要由 IOC 容器来生成和管理。因此,Spring AOP 可以直接使用 IOC 容器中的其他 bean 作为目标。这种依赖关系由 IOC 容器提供的依赖注入来实现。 总结起来,Spring AOP 依赖的是相关的 jar 包,其版本为 2.5.6。同时,要注意理解 AOP 编程思想与 Spring 框架的关系。另外,在使用 Spring AOP 时,要注意底层使用的是 Java 的动态代理或 CGLIB 代理,并且依赖 IOC 容器来生成和管理 AOP 相关的 bean。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [spring aop依赖jar包](https://download.csdn.net/download/solid_j/10799765)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【spring】Spring的aop](https://blog.csdn.net/qq_43418737/article/details/122704568)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值