Spring学习笔记-第四天:AOP基于cglib的动态代理/AOP的相关概念-通知切点等/XML注解方式实现AOP

cglib的动态代理

需要的角色:

  • 目标对象
  • 增强方法
  • 最终测试
    public static void main(String[] args) {
        //目标对象
        final Target target=new Target();

        //获得增强对象
        Advice advice=new Advice();

        //返回值 就是动态生成的代理对象 基于cglib
        //1.创建增强器-cglib提供
        Enhancer enhancer=new Enhancer();
        //2.设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3.设置回调函数
        //MethodInterceptor是Callback的一个子接口
        enhancer.setCallback(new MethodInterceptor() {
            //intercept方法相当于invoke方法,内部四个参数,
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                advice.before();//执行前置
                Object invoke = method.invoke(target, args);
                advice.afterReturning();//执行后置
                return invoke;
            }
        });
        //在此刻赋值成功
        //4.创建代理对象
        Target proxy =(Target) enhancer.create();
        proxy.save();

    }

AOP

AOP的相关概念

AOP的相关术语

  • Target:代理的目标对象
  • Proxy:代理对象
  • Joinpoint:连接点,指那些被拦截到的点。在Spring中,这些点指方法,因为Spring只支持方法类型的连接点(可以被增强的方法)
  • Pointcut:切入点是指我们要对哪些Joinpoint进行拦截的定义
  • Advice:(通知/增强)是指拦截到Joinpoint之后要做的事情就是通知
  • Aspect:切面是切入点和通知(增强)的结合
  • Weaving:织入实质把将切点和增强结合的过程是织入过程,Spring是动态代理织入,AspectJ是采用编译期织入和类装载期织入

连接点是可以被增强的方法,切点是实际上被增强过的方法,连接点范围更大

AOP开发明确的事项

  1. 需要编写的内容
  • 编写核心业务代码(Target的目标方法-切点)
  • 编写切面类,切面类中有通知(增强功能方法)
  • 在配置文件当中,配置织入关系,即将那些通知与哪些连接点进行结合
  1. AOP技术实现的内容
    Spring框架会监控切入点方法的执行--通过配置文件决定哪些方法是切点;一旦监控到切入点方法被运行,即使用代理机制,动态创建目标对象(切点)的代理对象。然后根据配置文件配置的增强类型(通知类别),在代理对象(切点)对应的位置,将通知对应的功能织入,完成完整的代码逻辑运行。
    配置文件-执行切点方法-监控到后动态创建代理对象,调用代理对象同名方法-内部调用目标方法,进行增强方法的介入。
  2. AOP底层使用哪种代理方式
    Spring会根据目标类是否实现了接口来决定采用哪种动态代理。

知识要点

  • aop:面向切面编程
  • aop底层实现: 基于jdk/Cglib的动态代理
  • aop的重点概念:
    • Pointcut:切入点是指我们要对哪些Joinpoint进行拦截的定义
    • Advice:(通知/增强)是指拦截到Joinpoint之后要做的事情就是通知
    • Aspect:切面是切入点和通知(增强)的结合
    • Weaving:织入实质把将切点和增强结合的过程是织入过程,Spring是动态代理织入,AspectJ是采用编译期织入和类装载期织入
  • 开发明确事项
    • 谁是切点
    • 谁是通知
    • 将切点和通知进行织入配置(配置文件)

基于XML的AOP开发

快速入门

  1. 导入AOP相关坐标
  2. 创建目标接口和目标类(内部有切点)
  3. 创建切面类(内部有增强方法)
  4. 将目标类和切面类的对象创建权交给Spring
  5. 在applicationContext.xml中配置织入关系(最难)
  6. 测试代码

导入AOP相关坐标

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.6</version>
        </dependency>
<!--        aspectj更轻量,更高效实现aop-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>

创建目标接口和目标类
首先创建目标接口和实现。

然后创建切面类
切面类不完全等同之前的直接设置增强类,而是内部有增强的方法

public class MyAspect {
    public void before(){
        System.out.println("前置增强...");
    }
}

将目标类和切面类的对象创建权交给Spring容器

<!--        配置目标对象-->
    <bean id="target" class="com.itheima.aop.Target"></bean>
<!--    配置切面对象-->
    <bean id="myAspect" class="com.itheima.aop.MyAspect"></bean>

在配置文件当中,告诉Spring框架哪些方法需要进行哪些增强
首先,要引用AOP命名空间和aop配置的位置

<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
">

然后配置织入

<aop:aspect ref="myAspect"></aop:aspect>

用来表示myAspect是切面 method表示哪些方法是哪些增强,然后pointcut指定pointcut,其中需要execution调用切点方法的全限定名。

 <aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.save)"></aop:before>

这句代码表明,当访问这个save()方法时,这个方法需要前置增强,而这个前置增强是在myAspect这个切面的before方法当中封装。

<aop:config>
<!--        声明切面-->
        <aop:aspect ref="myAspect">
<!--          切面:切点+通知 -->
            <aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.save())"></aop:before>
        </aop:aspect>
    </aop:config>

测试
要进行Spring测试,需要引入Spring测试的包

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.6</version>
        </dependency>

然后指定RunWith,详见前文Spring整合Junit的过程
代码实现:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    //这里使用接口接住,因为jdk代理,代理对象与目标对象皆为接口类型,不是实现类型
    private TargetInterface target;

    @Test
    public void test1(){
        target.save();
    }
}

切点表达式的配置:
表达式语法:execution([修饰符] 返回值类型 包名 类名 方法名(参数))

execution(public void com.itheima.aop.Target.save())

[]代表可写可不写,包名可能有多层,倒数第一个是方法名,倒数第二个是类名

  • 访问修饰符可以省略
  • 返回值类型,包名,类名,方法名可以使用*代表任意
  • 包名与类名之间一个点代表当前包下面的类,两个点代表当前包及其子包下的类
  • 参数列表可以使用两个点..表示任意个数,任意类型的参数列表
execution(public void com.itheima.aop.Target.method())//指定某包下的方法
execution(void com.itheima.aop.Target.*(..))//代表Target下的所有方法,任意参数
execution(* com.itheima.aop.*.*(..))//常用的,aop包下的所有函数
execution(* com.itheima.aop..*.*(..))//aop包及其子包下的
execution(* *..*.*(..))

通知类型
通知的配置语法

<aop:通知类型 method="切面类中方法名" pointcut="切点表达式"/>

类型:

  • 前置通知 before 在切入点之前
  • 后置通知 after-returning 在切入点之后
  • 环绕通知 around 在切入点前后
  • 异常抛出通知 throwing 增强方法出现异常时通知
  • 最终通知 after 不管增强方法出不出现异常都通知

演示:

around需要加入参数(其他也可以加参数)

//ProceedingJoinPoint 正在进行的 连接点=切点
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强...");
        Object proceed = pjp.proceed();//切点方法
        System.out.println("环绕后增强...");
        return proceed;
    }

在验证过程中,发现出现异常时,只会有前置增强和环绕前增强

xml配置文件相关代码:

<aop:config>
<!--        声明切面-->
        <aop:aspect ref="myAspect">
<!--          切面:切点+通知 -->
<!--            <aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.save())"></aop:before>-->
            <aop:before method="before" pointcut="execution(* com.itheima.aop.*.*(..))"></aop:before>
            <aop:after-returning method="afterReturning" pointcut="execution(* com.itheima.aop.*.*(..))"></aop:after-returning>
            <aop:around method="around" pointcut="execution(* com.itheima.aop.*.*(..))"></aop:around>
            <aop:after-throwing method="afterThrowing" pointcut="execution(* com.itheima.aop.*.*(..))"></aop:after-throwing>
            <aop:after method="after" pointcut="execution(* com.itheima.aop.*.*(..))"></aop:after>
        </aop:aspect>
    </aop:config>

切面配置相关代码

public class MyAspect {

    public void before(){
        System.out.println("前置增强...");
    }
    public void afterReturning(){
        System.out.println("后置增强...");
    }
    //ProceedingJoinPoint 正在进行的 连接点=切点
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强...");
        Object proceed = pjp.proceed();//切点方法
        System.out.println("环绕后增强...");
        return proceed;
    }
    public void afterThrowing(){
        System.out.println("异常抛出异常....");
    }
    public void after(){
        System.out.println("最终增强...");
    }
}

切点表达式的抽取-进一步解耦

实际上它们的切点表达式可能是一样的,可以进行相应的抽取。在增强中使用pointcut-ref进行引用

    <aop:config>
<!--        声明切面-->
        <aop:aspect ref="myAspect">
<!--          切面:切点+通知 -->
<!--            抽取切点表达式-->
            <aop:pointcut id="mypointCut" expression="execution(* com.itheima.aop.*.*(..))"/>

            <aop:around method="around" pointcut-ref="mypointCut"></aop:around>
        </aop:aspect>
    </aop:config>

知识要点

  • aop织入的配置-xml文件修改
  • 通知的类型
  • 切点表达式的写法
posted @ 2021-04-30 22:31  东风应笑我  阅读(107)  评论(0)    收藏  举报