Spring之AOP(上)

代理,指的是为一个目标对象提供一个代理对象, 并由代理对象控制对目标对象的引用. 使用代理对象, 是为了在不修改目标对象的基础上,
增强目标对象的业务逻辑。
在这里插入图片描述

动态代理

特点:字节流随用随创建,随用随加载。
作用:不修改源码的基础上对方法进行增强。
一、基于接口的动态代理
涉及的类:Proxy
提供者:JDK官方
如何创建代理对象:使用Proxy类中的newProxyInstance方法。
创建代理对象的要求:被代理类最多实现一个接口,如果没有则不能使用。
newProxyInstance方法的参数:
1、ClassLoader 类加载器,用于加载代理对象的字节码,和被代理对象使用相同的类加载器。写法固定。
2、Class[] 字节码数组 使得代理对象和被代理对象拥有相同的方法。
3、InvocationHandler 用于提供增强的代码,是让我们写如何代理。我们一般是写一个该接口的实现类,通常情况下是匿名内部类,但不是必须的。写法固定。

Proxy.newProxyInstance(被代理对象.getClass().getClassLoader(),
被代理对象.getClass().getInterfaces(),
new InvocationHandler(){...}
);

接口中仅有一个名为invoke的方法,执行被代理对象的任何接口方法都会经过该方法。方法中各个参数的含义如下:
proxy: 代理对象的引用
method: 当前执行的方法
args[]: 当前执行方法所需的参数
返回值:和被代理对象方法有相同的返回值
在该方法中提供增强的内容代码,如下图所示:
在这里插入图片描述
二、基于子类的动态代理
涉及的类:Enhancer
提供者:第三方cglib库
如何创建代理对象:使用Enhancer类中的create方法。
创建代理对象的要求:被代理类不能是最终类
方法的参数:
1、Class 字节码,用于指定被代理对象的字节码。
2、Callback 用于提供增强的代码,在里面写增强的过程。我们一般是写一个该接口的实现类,通常情况下是匿名内部类,但是不是必须的。
此接口的实现类都是谁用谁写。我们一般写的是该接口的子接口实现类:MethodInterceptor。

接口中仅有一个名为invoke的方法,执行被代理对象的任何接口方法都会经过该方法。方法中各个参数的含义如下:
proxy: 代理对象的引用
method: 当前执行的方法
args[]: 当前执行方法所需的参数
methodProxy:当前执行方法的代理对象
返回值:和被代理对象方法有相同的返回值
在这里插入图片描述

AOP的概念

全称:面向切面编程。将程序中重复的代码抽取出来,在需要执行的时候使用动态代理的技术,在不对源码进行修改的基础上对已有的方法进行增强。
连接点(joinpoint):被拦截到的点,这些点指的是方法,spring只支持方法类型的连接点。在业务层接口中,其中所有的方法都是连接点,连接业务和增强方法。帮助我们将增强事务控制的代码加到业务当中来,使得方法加上事务支持从而形成完整的业务逻辑。
切入点(pointout):被增强的方法。切入点是连接点的子集。
通知/增强(advice):拦截到连接点后要做的事情。动态代理中invoke方法具有拦截的功能,能拦截被代理对象中所执行的所有方法。拦截后要做的就是提供事务的支持,包括开启事务、执行操作、提交事务、返回结果。
通知的类型:前置通知、后置通知、异常通知、最终通知、环绕通知。
在这里插入图片描述
引介:一种特殊的通知,在不修改类代码的前提下,在运行期为类动态地添加一些方法或字段(field)。
目标对象(target):代理的目标对象。
织入(waving):原有的service无法实现事务的支持,于是我们用动态代理技术创建了一个新的对象,返回了一个代理对象。在返回代理对象时,从中加入了事务支持。加入支持的过程称为织入。
代理(proxy):织入后产生的对象。
切面(aspect):切入点和通知的结合。切入点指的是被增强过的方法,通知指的是提供了公共代码的方法。建立切入点方法和通知方法在执行调用的对应关系指的是切面。
在这里插入图片描述
哪些方法,在何时执行,需要进行配置,最终形成切面。

基于XML的AOP配置

1、新建新项目,在pom.xml文件当中新增加以下依赖:

    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>

2、新建bean.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:aop="http://www.springframework.org/schema/aop"
       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>

3、在上述文件当中配置ioc,当有一个类需要增强时,把类对象配置进来。

 <bean id="标识" class="全限定类名"></bean>

4、进行spring中基于xml的aop配置。首先把通知类bean也交给spring来管理。

 <bean id="标识" class="全限定类名"></bean>

5、使用aop:config标签表明开始aop的配置。
**使用aop:aspect标签表明配置切面。**其中id属性是给切面提供一个唯一标识,ref属性是指定通知类bean的id。
在aop:aspect标签的内部使用对应标签来配置通知的类型。同时建立通知方法和切入点方法之间的关联 如果让"logger"类中的方法“printlog"在切入点方法执行之前执行,则配置前置通知。aop:before表示前置通知,method属性用于指定Logger类中哪个方法是前置通知,pointcut属性用于指定切入点表达式,该表达式的含义是对业务层中哪些方法加强。
切入点表达式的写法:
关键字:execution
表达式: 访问修饰符 返回值 包名.包名…类名.方法名(参数列表)

    <aop:config>
        <aop:aspect id="给切面提供一个唯一标识" ref="Logger">
            <aop:before method="printlog" pointcut="..."/>
        </aop:aspect>
    </aop:config>

在这里插入图片描述
增强的过程就是创建代理对象的过程。配置全过程如下图所示:
在这里插入图片描述
测试:
在这里插入图片描述

注:
切入点表达式具有省略写法:
1、全通配写法 :在这里插入图片描述
每个方法都会被进行增强。
2、访问修饰符可以被省略。
3、返回值可以使用通配符表示任意返回值。void可以改为‘*’,此时任意返回值均可以用。
4、包名可以使用通配符表示任意包,但有几级包就要用几个‘.’。
5、可以使用‘…’表示当前的包及其子包。
6、类名和方法名都可以使用‘*’来实现通配。
7、参数列表可以直接写数据类型,基本类型直接写名称,引用类型写包名.类名的方式。可以使用通配符表示任何类型,但是必须有参数。可以使用两个点表示有无参数均可,参数可以是任意类型。
8、实际开发中,切入点表达式通常切到业务层实现类下所有方法。
在这里插入图片描述

常用通知类型

在这里插入图片描述
后置通知和异常通知永远只能执行一个。事务要么提交要么回滚,不可能两个同时发生。

环绕通知
问题:当我们配置好环绕通知后,切入点方法没有执行,而通知方法执行了。
分析:通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
解决:spring框架为我们提供了一个接口:ProceedingJoinPoint。在该接口中有一个方法proceed(),该方法就相当于明确接入点方法。该接口可以作为环绕通知的方法参数。在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值