Spring AOP官方文档学习笔记(四)之Spring AOP的其他知识点

1.选择哪种AOP

(1) 使用Spring AOP比使用完整版的AspectJ更方便简单,因为不需要在开发和构建过程中引入AspectJ编译器以及织入器,如果我们只希望通知能够在Spring Bean上执行,那么选用Spring AOP就可以了,如果我们希望通知能够在不由Spring所管理的对象上执行,那么就需要使用AspectJ,如果我们希望为除方法以外的连接点(比如成员变量)提供通知,那么也需要使用AspectJ

2.Spring AOP的代理机制

(1) Spring AOP使用Jdk动态代理或Cglib动态代理来为目标对象创建代理对象,Jdk动态代理由Jdk提供,而Cglib动态代理则是由一个开源类库提供,如果要代理的目标对象至少实现了一个接口,那么就会使用Jdk动态代理,否则如果目标对象没有实现任何接口,那么就会使用Cglib动态代理,创建一个Cglib代理对象

(2) Spring默认使用Jdk动态代理,但我们可以强制让Spring始终使用Cglib动态代理,但需注意,使用Cglib动态代理,无法对final修饰的方法织入通知,因为这些方法不能在子类中被重写,具体开启Cglib动态代理的方式如下

<!-- 方式一:在使用基于xml的配置时,设置<aop:config/>标签中的proxy-target-class属性为true -->
<aop:config proxy-target-class="true">
        <!-- ... -->
</aop:config>

<!-- 方式二:在混合使用基于xml和注解的配置时,设置<aop:aspectj-autoproxy/>标签中的proxy-target-class属性为true -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 方式三:在使用基于注解的配置时,设置@EnableAspectJAutoProxy注解中的proxyTargetClass属性为true -->
@EnableAspectJAutoProxy(proxyTargetClass = true)

(3) 当一个bean被代理后,我们从容器中获取到这个bean,并对其使用 .getClass().getName() 方法来输出它的类名称,可见如 cn.example.spring.boke.ExampleA$$EnhancerBySpringCGLIB$$ff6c22d2或com.sun.proxy.$Proxy18 这样的输出,而当我们关闭掉AOP后,得到的通常是形如 cn.example.spring.boke.ExampleA 这样的输出,这其实是因为我们从容器中获取的是该bean被增强过后的代理对象,而非它原始的目标对象,因而,对这个bean的方法调用就是对代理对象的方法调用,然后由代理对象委托调用原始对象上相关的方法以及该方法相关的拦截器(advice),如下

Spring AOP官方文档学习笔记(四)之Spring AOP的其他知识点

(4) 在目标对象中,使用this指针进行自调用不会触发通知的执行

//一个普通的bean,在它的a方法中使用this指针,自调用b方法
@Component
public class ExampleA{
    public void a() {
        System.out.println("a...");
        this.b();
    }

    public void b() {
        System.out.println("b...");
    }
}

//切面,切入ExampleA这个bean中的所有方法
@Component
@Aspect
public class Logger {
    @Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
    public void beforePrint() {
        System.out.println("beforePrint...");
    }
}

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "cn.example.spring.boke")
public class Config { }

//获取ExampleA,调用a方法,打印结果
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ExampleA exampleA = ctx.getBean(ExampleA.class);
exampleA.a();

//可见,Spring对a方法进行了织入,而b方法却没有,原因就是因为这里的this指向的是目标对象,一个普通的bean ExampleA,而非它的代理对象,自然而然无法进行织入了,因此关键的目标就是如何获取到代理对象
beforePrint...
a...
b...


//想要获取代理对象,首先要先将@EnableAspectJAutoProxy注解中的exposeProxy属性设置为true
@EnableAspectJAutoProxy(exposeProxy = true)

//接着修改ExampleA中的a方法,在调用b方法时不再使用this指针,而是AopContext.currentProxy(),即获取当前对象的代理对象
public void a() {
    System.out.println("a...");
//        this.b();
    ((ExampleA) AopContext.currentProxy()).b();
}

//接着,再执行打印,可见此时通知已被正确执行
beforePrint...
a...
beforePrint...
b...

Spring不推荐使用如上的方法,因为这会使Spring AOP与我们的代码强耦合,具有侵入性,最好的方式是重构我们的代码,避免发生自调用,此外,Spring AOP会产生这种问题的原因是Spring AOP是基于代理实现的,而AspectJ框架就不存在这种自调用问题,因为它不是一个基于代理的AOP框架

3.基于java代码的AOP

public class ExampleA{
    public void a() {
        System.out.println("a...");
    }
}

//切面必须由@Aspect注解标注,否则容器会抛出异常
@Aspect
public class Logger {
    @Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
    public void beforePrint() {
        System.out.println("beforePrint...");
    }
}

//创建AOP工厂,生成代理对象
public static void main(String[] args) throws Exception {
    //1.使用AspectJProxyFactory工厂,用于生成目标对象的代理对象
    AspectJProxyFactory factory = new AspectJProxyFactory(new ExampleA());

    //2.添加一个切面,该切面必须由@Aspect注解标注
    factory.addAspect(Logger.class);

    //3.生成代理对象
    ExampleA proxy = factory.getProxy();
    proxy.a();
}

原文链接:https://www.cnblogs.com/shame11/p/17370702.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring AOP官方文档学习笔记(四)之Spring AOP的其他知识点 - Python技术站

(0)
上一篇 2023年5月10日
下一篇 2023年5月10日

相关文章

  • Spring Boot中lombok的安装与使用详解

    下面我来详细讲解“Spring Boot中lombok的安装与使用详解”的完整攻略。 什么是lombok? Lombok是一种Java库,它可以自动帮我们生成getter、setter、构造函数、日志记录等代码,让我们可以专注于业务代码的编写。 安装lombok Spring Boot默认并不包含lombok库,因此需要手动添加。 Maven中添加依赖 在m…

    Java 2023年5月19日
    00
  • Java14发布了,再也不怕NullPointerException了

    Java14发布了,再也不怕NullPointerException了 自从Java诞生以来,空指针异常NullPointerException就一直是开发者最常见的错误之一。但是,随着Java版本的更新,我们终于迎来了一次改变。Java14发布了,它带来了一系列的特性和改进,其中最引人注目的就是JDK Enhancement Proposal 358(JE…

    Java 2023年5月20日
    00
  • Java语言通过三种方法实现队列的示例代码

    下面是关于“Java语言通过三种方法实现队列”的详细攻略: 一、队列的定义 在计算机科学中,队列是一种特殊的线性数据结构,它只允许在一端进行插入操作,在另一端进行删除操作。在队列中,进行插入操作的一端被称为队尾,进行删除操作的一端被称为队头。 二、常见的队列实现方法 实现队列的方法有很多,其中比较常见的包括: 1、使用数组实现队列 使用数组来实现队列,可以通…

    Java 2023年5月18日
    00
  • mvc实现图片验证码功能

    MVC实现图片验证码功能 在Web应用程序中,图片验证码是一种常见的安全机制,用于防止机器人或恶意用户自动化攻击。在本文中,我们将介绍如何使用MVC框架来实现图片验证码功能。 步骤 以下是实现图片验证码功能的步骤: 创建一个Controller类,用于处理请求并生成验证码图片。 创建一个View类,用于显示验证码图片。 创建一个Model类,用于生成验证码字…

    Java 2023年5月18日
    00
  • java spring整合junit操作(有详细的分析过程)

    下面是详细讲解“Java Spring整合JUnit操作”的攻略,包含以下几个步骤: 添加JUnit依赖库 在项目中添加JUnit依赖库,以使用JUnit框架进行单元测试。在Maven项目中,可以在pom.xml文件中添加以下依赖库: <dependency> <groupId>junit</groupId> <ar…

    Java 2023年5月19日
    00
  • 5种Java中数组的拷贝方法总结分享

    下面是“5种Java中数组的拷贝方法总结分享”的完整攻略。 概述 在Java编程中,经常需要对数组进行拷贝或复制操作。Java中提供了多种数组拷贝方法供开发者使用。本文将总结并分享5种Java中数组的拷贝方法。 方法一:使用for循环进行拷贝 这是最常见的方法,也是最基础的方法。使用for循环对数组进行遍历并拷贝元素。 public static void …

    Java 2023年5月26日
    00
  • 详解SpringBoot实现fastdfs防盗链功能的示例代码

    以下是“详解SpringBoot实现fastdfs防盗链功能的示例代码”的完整攻略: 防盗链功能概念 防盗链技术可以防止其他站点盗链本站的内容,从而保证网站安全及资源不被滥用。在FastDFS中,通过配置nginx.conf文件实现防盗链。 安装配置FastDFS 首先,需要在本地或服务器上安装并配置FastDFS。可以参考FastDFS官网及论坛的相关文档…

    Java 2023年5月20日
    00
  • 微信小程序+后端(java)实现开发

    当使用微信小程序进行开发时,为了实现一些对数据的获取、修改、删除等操作,我们需要使用后端技术来支持。Java是一种常用的后端开发语言,下面将为大家详细讲解“微信小程序+后端(java)实现开发”的完整攻略。 1. 前置知识 在学习和使用微信小程序和后端(java)开发之前,需要具备以下基础知识: HTML、JavaScript、CSS基础知识。 Vue.js…

    Java 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部