AOP切面编程

AOP介绍

思想及概念

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程面向切面编程/6016335),通过[预编译]方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

可以做什么?

主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。

主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码

AOP术语

![1560774375748](C:\Users\何进结\AppData\Roaming\Typora\typora-user-images\15607在这里插入图片描述

切面(Aspect)

我们将自己需要插入到目标业务逻辑中的代码模块化, 通过AOP使之可以横切多个类的模块,称之为切面。

在Spring AOP配置中切面通常包含三部分:

  • 切面模块本身
  • 通知
  • 切入点
<!-- 目标业务逻辑代码 -->
<bean id="user" class="testshop2.test_proxy.spring.UserSerivce2Impl2"/>
<!-- 切面模块化对象(代表我们要附加到原始业务逻辑中的代码) -->
<bean id="aspect" class="testshop2.test_proxy.spring.MyAspect2"/>
<!-- 示例说明: 将切面calcAspect中的代码插入到calc原始业务代码中 -->    
<aop:config>
    <!-- 定义公用的切入点表达式,如果aspect中有多个通知,都可以通过pointcut-ref复用 -->
        <aop:pointcut id="h1" expression="execution(* testshop2.test_proxy.spring.UserSerivce2Impl2.*(..))"/>
      <!-- 定义公用的切入点表达式,如果aspect中有多个通知,都可以通过pointcut-ref复用 -->
        <aop:aspect ref="aspect">
            <aop:around method="testProxySpring" pointcut-ref="h1"/>
            <aop:before method="beforeMethod" pointcut-ref="h1"/>
            <aop:after method="afterMethod" pointcut-ref="h1"/>
            <aop:after-returning method="afterReturningMethod" pointcut-ref="h1" returning="obj"/>
            <aop:after-throwing method="afterThrowingMethod" pointcut-ref="h1" throwing="throwable"/>
        </aop:aspect>
    </aop:config>
切入点(pointcut)

在 Spring AOP 中,需要使用 AspectJ 的切点表达式来定义切点

AspectJ 指示器描述
execution ()用于匹配连接点的执行方法 常用
args ()限制连接点的指定参数为指定类型的执行方法
@args ()限制连接点匹配参数类型由指定注解标注的执行方法 this () 限制连接点匹配 AOP 代理的 Bean 引用为指定类型的类
target ()限制连接点匹配特定的执行对象,目标对象是指定的类型
@target ()限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型注解
within()限制连接点匹配指定类型,比如哪个包下,或哪个类里面
@within()限制连接点匹配指定注释所标注的类型(当使用 Spring AOP 时,方法定义在由指定的注解所标注的类里)
@annotation限制匹配带有指定注释的连接点
通知(advice)

通知:(增强)要织入的代码

前通知(before)

在目标方法调用前通知切面, 什么参数也无法获取。也不能终止目标方法执行

<bean id="user" class="testshop2.test_proxy.spring.UserSerivce2Impl2"/>
<bean id="aspect" class="testshop2.test_proxy.spring.MyAspect2"/>
    <aop:config>
        <aop:pointcut id="h1" expression="execution(* testshop2.test_proxy.spring.UserSerivce2Impl2.*(..))"/>
        <aop:aspect ref="aspect">
            <aop:before method="beforeMethod" pointcut-ref="h1"/>
        </aop:aspect>
    </aop:config>
环绕通知(around)
  • 在目标方法执行前、后被通知, 可以获取连接点对象(ProceedingJoinPoint, 该对象可以获取被拦截方法的签名、参数、返回值、包括是否被调用)
  • 该方法的返回值,即代表了真正业务逻辑代码的返回值
  • 可以选择终止或正常执行目标方法
<aop:around method="testProxySpring" pointcut-ref="h1"/>  
后置通知(after returning)

只有在目标方法 正常 执行结束后才能获取方法返回值的结果!

<aop:after-returning method="afterReturningMethod" pointcut-ref="h1" returning="obj"/>
最终通知(after)

在目标方法执行结束后通知切面,无法获取任何参数。无论目标方法是正常执行结束还是抛出异常终止,都会被通知

           <aop:after method="afterMethod" pointcut-ref="h1"/>
异常通知(after throwing)

只有在目标方法 出现异常 才会通知, 在通知方法中可以获取到抛出的异常信息

<aop:after-throwing method="afterThrowingMethod" pointcut-ref="h1" throwing="throwable"/>
连接点(JoinPoint)
  • 定义:连接点是一个应用执行过程中能够插入一个切面的点。
  • 连接点可以是调用方法时、抛出异常时、甚至修改字段时、
  • 切面代码可以利用这些点插入到应用的正规流程中
  • 程序执行过程中能够应用通知的所有点。
Spring仅支持方法创建连接点
织入
  • 织入是将切面应用到目标对象来创建的代理对象过程。
  • 切面在指定的连接点被织入到目标对象中,在目标对象的生命周期中有多个点可以织入

编译期——切面在目标类编译时期被织入,这种方式需要特殊编译器。AspectJ的织入编译器就是以这种方式织入切面。
类加载期——切面在类加载到
JVM ,这种方式需要特殊的类加载器,他可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5 的 LTW 就支持这种织入方式

运行期——切面在应用运行期间的某个时刻被织入。一般情况下,在织入切面时候,AOP 容器会为目标对象动态的创建代理对象。Spring AOP 就是以这种方式织入切面。

使用步骤

添加依赖包(maven演示)
    <!-- Spring IOC最小依赖是beans、context,我们引入context依赖,maven会自动将beans依赖一并引入 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.framework.version}</version>
    </dependency>
<!--aop插件-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>
xml方式
添加aop schema摘要
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
<!--此为aop摘要配置
xmlns:aop="http://www.springframework.org/schema/aop"
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd"
-->
使用xml文件配置
  • 定义切面类,并且添加到xml配置文件中
配置aop:config

Spring 的 AOP 配置元素简化了基于 POJO 切面声明

AOP 配置元素描述
aop : advisor定义 AOP 通知器
aop : after定义 AOP 后置通知(不管被通知方法是否执行成功)
aop : after-returing定义 AOP after-returing 通知
aop : after-throwing定义 AOP after-throwing 通知
aop : around定义 AOP 环绕通知
aop : aspect定义切面
aop : before定义 AOP 前置通知
aop : config顶层的 AOP 配置元素,大多数 aop : * 元素必须包含在 元素内
aop : declare-parents为被通知的对象引入额外接口,并透明的实现
aop : pointcut定义切点
引用切面类(配置bean)
<!--代码示例-->
<bean id="user" class="testshop2.test_proxy.spring.UserSerivce2Impl2"/>
<bean id="aspect" class="testshop2.test_proxy.spring.MyAspect2"/>

定义切入点表达式
  • 作用

    通过表达式的方式定位一个或多个具体的连接点。

  • 语法细节

①切入点表达式的语法格式

execution([权限修饰符] [返回值类型] [简单类名/全类名] 方法名)

②在AspectJ中,切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。

表达式含义
execution(* .addAfter(int,…)) || execution( *.deleteAfter(int,…))任意类中第一个参数为int类型的addAfter方法或deleteAfter方法

③xml配置文件的示例代码

 <aop:pointcut id="h1" expression="execution(*testshop2.test_proxy.spring.UserSerivce2Impl2.*(..))"/>
<!--表示这个类的全部方法-->
定义通知
<aop:around method="testProxySpring" pointcut-ref="h1"/>
注解方式(Annotation)
定义Java类

定义切面类,并添加@Aspect注解,@Component注解

@Aspect//表示此类是个切面类
@Component//表示此切面也是一个bean
@Slf4j//logback日志
public class SayMethodAspect {
 // 指定该方法是一个环绕通知,通知注解的参数代表引用一个切入点
    @Around("testshop2.annotation_proxy.GlobalPointcut.sayMethod()")
    public Object testSay(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("大哥来了!");
        Object obj=joinPoint.proceed();
        System.out.println("大哥慢走!");
        log.debug("哈哈哈!哈哈哈!哈哈哈!");
        return obj;
    }
//要切入的类
@Component
public class IsayImpl implements Isay {
    @Override
    public void say1(){
        System.out.println("HELLO ,world!");
    }
    @Override
    public void say2(String name){
        System.out.println("Hello"+name);
    }
}
定义切入点

【官方建议从新写个类】

//通过@Pointcut注解定义切入点表达式
//此处表达式含义:拦截testshop2.annotation_proxy包下所有类(包括子包中所有类)中的所有方法
@Component
public class GlobalPointcut {
    @Pointcut("execution(* testshop2.annotation_proxy..*.*(..))")
    public void sayMethod(){}
}
开启自动织入支持
<!-- 开启aop注解支持 -->
<aop:aspectj-autoproxy />
<!-- 开启注解支持,同时强制指定代理机制为cglib -->
<aop:aspectj-autoproxy proxy-target-class="true" />
@Configuration
// 开启注解支持,同时强制指定代理机制为cglib 
@ComponentScan(basePackages = "扫描包路径")
@EnableAspectJAutoProxy(proxyTargetClass = true)
//(proxyTargetClass = true)强制代理机制为cglib
public class MyAOPConf {}
AOP方法上的注解
注解释意
@Before()这个是前置调用,在方法之前掉调用
@After()这个称为后置调用,不管方法执行是正常还是异常都会调用
@Around()这个表示的是环绕 在调用方法之前和之后调用 一般用来记录日志,需要joinPoint.proceed()推动目标进行
@AfterReturning()这个表示的是正常运行后,返回数据的时候调用
@AfterThrowing()这个表示的是抛出异常的时候调用
@Aspect告诉容器这是一个切面类
@Pointcut()切入点表达式的位置
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值