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

yizhihongxing

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日

相关文章

  • Java构造代码块,静态代码块原理与用法实例分析

    当我们创建Java对象时,会自动对对象进行初始化。除了对属性进行初始化外,我们还可以利用代码块来进行初始化操作。其中Java构造代码块和静态代码块都是常见的初始化方式。 构造代码块 构造代码块是一种在类中直接使用非静态代码块的方式来对实例进行初始化的机制。它只跟随构造函数一起执行,例如: public class CodeBlockExample { { S…

    Java 2023年5月23日
    00
  • SpringMVC后端返回数据到前端代码示例

    SpringMVC后端返回数据到前端代码示例的完整攻略如下: 1. 定义Controller类 首先要定义一个Controller类,用于处理前端的请求,然后返回数据给前端。以下是示例代码: @RestController @RequestMapping("/api") public class UserController { @Aut…

    Java 2023年6月15日
    00
  • Spring Security 核心过滤器链讲解

    对于Spring Security,核心过滤器链可以说是它的核心之一。本文将从什么是核心过滤器链、以及它包含哪些过滤器等方面进行详细讲解。 1. 什么是核心过滤器链? 核心过滤器链是Spring Security运作的基础。当一个请求进来时,它将会被一系列的过滤器处理,处理完成后才会交给真正的应用程序处理。核心过滤器链由一系列的过滤器组成,每个过滤器都有自己…

    Java 2023年5月20日
    00
  • JVM类加载机制原理及用法解析

    JVM类加载机制原理及用法解析 Java虚拟机是Java语言实现”Write Once, Run Anywhere”程序设计理念的一个关键组成部分,而Java虚拟机中最重要的一个子系统就是类加载子系统。该子系统负责对字节码文件(.class文件)中的类进行加载、验证、准备、解析、初始化等操作,从而在程序的运行中实现类的动态加载和管理。那么,下面我们就来详细讲…

    Java 2023年6月15日
    00
  • java实现简易的计算器界面

    下面就来详细讲解Java实现简易的计算器界面的完整攻略。 1. 界面设计 首先我们需要设计计算器的界面。常见的计算器界面有两种,一种是标准的计算器界面,另一种是科学计算器界面。我们以标准的计算器界面为例进行讲解。 1.1 界面元素 标准的计算器界面一般包含以下元素: 数字键:0~9十个数字键; 小数点键:用于输入小数; 运算符键:加、减、乘、除; 等于键:计…

    Java 2023年5月18日
    00
  • 浅谈web服务器项目中request请求和response的相关响应处理

    关于“浅谈web服务器项目中request请求和response的相关响应处理”,我们可以从以下几个方面展开: 一、HTTP request请求的相关处理 HTTP请求通常包括方法、URL、HTTP版本、请求头和请求体等部分,服务器在接收到请求后需要根据请求的不同部分进行处理。 方法(method) 请求方法指定了客户端希望服务器执行的操作。常用的方法有GE…

    Java 2023年6月15日
    00
  • 基于Java实现文件和base64字符串转换

    下面是基于Java实现文件和base64字符串转换的攻略: 1.前置知识 在进行文件和base64字符串转换时,需要了解以下知识: 文件读写的基本操作 Base64编码和解码的原理和实现方式 2.实现步骤 2.1 文件转base64字符串 文件转base64字符串的过程可以分解为以下几步: 步骤1:将文件转换为字节数组 首先,需要将文件读取到内存中并将其转换…

    Java 2023年5月27日
    00
  • Java获取当前系统事件System.currentTimeMillis()方法

    当我们需要在Java程序中获取系统时间时,常用的方法是使用System.currentTimeMillis()方法。该方法可以获取当前系统时间的毫秒数。 使用该方法的步骤如下: 步骤1:导入Java.util包 首先我们需要导入Java.util包,因为该包中提供了一些与日期和时间相关的类。 import java.util.*; 步骤2:获取系统时间 接下…

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