Spring的组成
Spring由20个核心依赖组成,这20个核心依赖可以分为6个核心模块
本文主要讲解spring的AOP模块,其中包括spring-aop,spring-aspects
Spring AOP模块概述
AOP思想
AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。
面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。
AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。
主要作用是分离功能性需求和非功能性需求。使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性
。
简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。
AOP是在某个时期织入程序,实现程序功能的统一维护的一种技术
AOP织入时期
这里的某个时期,主要包括三种时期:
-
编译期:.java源代码文件预编译 为.class字节码文件的过程。
在编译时,由编译器把切面调用编译进字节码,这种方式需要定义新的关键字并扩展编译器,AspectJ就扩展了Java编译器,使用关键字aspect来实现织入;
-
类加载期:类加载器是JVM的一部分。类加载器将.class字节码文件转换为Java对象,实现class的加载和初始化,并实现Java对象和方法(除去构造方法,static方法以及final方法)的动态绑定 = C++的虚函数virtual = Java的多态
类加载器的分类,以及类加载器的双亲委派机制,读者可以自行学习
我们自定义的ClassLoader,可以自定义对类进行增强(在类加载时),也算是一种AOP思想的实现
- 运行期:
这种方式的目标类对象和切面都是普通Java类,通过JDK的动态代理或者第三方库(CGLIB)实现运行期动态织入。
这也是三种方式中最简单的方式,Spring AOP同时使用了JDK的动态代理和CGLIB的动态代理
AspectJ和Spring AOP的区别
事实上,AspectJ是Java AOP的完全解决方案,包含编译期织入,编译后织入,类加载期织入,不包含运行期织入(方法织入)
;而Spring AOP只包含了企业开发中使用最普遍的运行期织入(方法织入)
AspectJ
AspectJ 作为 AOP 编程的完全解决方案,提供了三种织入时机,分别为
- compile-time:编译期织入,在编译的时候一步到位,直接编译出包含织入代码的 .class 文件(需要指定aspectj-maven-plugin的maven插件以编译*.aj文件)
- post-compile:编译后织入,增强已经编译出来的类,如我们要增强依赖的 jar 包中的某个类的某个方法(也需要指定aspectj-maven-plugin的maven插件)
- load-time:在 JVM 进行类加载的时候进行织入(需要在java启动时添加参数,例如:
-javaagent:/Users/hongjie/.m2/repository/org/aspectj/aspectjweaver/1.8.13/aspectjweaver-1.8.13.jar;另外需要配置aop.xml配置文件配置需要织入的类)
AOP核心概念
JoinPoint:连接点
被AOP拦截到的点。在Spring AOP中指的是被拦截增强的目标类方法 ,因为Spring只支持方法类型的连接点(还有其他类型的连接点)
PointCut:切入点
要拦截的连接点的拦截规则(定义),比如拦截所有以ServiceImpl为结尾的类的findXXX()方法,这样的一条规则就叫切入点
Advice通知/增强
所谓通知是指拦截到Joinpoint之后所要做的事情,即对切入点的增强(功能增强/访问控制)
。
通知分为前置通知(如开启事务
),后置通知(如提交事务
),异常通知(如回滚事务
),最终通知(如关闭JDBC资源
),环绕通知(前置+后置+异常+最终,方法集合在一个方法内
)
Weaving(织入)
把增强应用到目标类对象来创建新的代理对象的过程
Proxy(代理)
一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面)
是切入点
和通知
的结合,切入点需要程序员手动定义和配置(配置文件),通知的方法需要程序员手动编写
spring-aop的pom依赖
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
<name>Spring AOP</name>
<description>Spring AOP</description>
<url>https://github.com/spring-projects/spring-framework</url>
<organization>
<name>Spring IO</name>
<url>http://projects.spring.io/spring-framework</url>
</organization>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>jhoeller</id>
<name>Juergen Hoeller</name>
<email>jhoeller@pivotal.io</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-framework</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-framework</developerConnection>
<url>https://github.com/spring-projects/spring-framework</url>
</scm>
<issueManagement>
<system>Jira</system>
<url>https://jira.springsource.org/browse/SPR</url>
</issueManagement>
<dependencies>
<dependency>
<groupId>com.jamonapi</groupId>
<artifactId>jamon</artifactId>
<version>2.81</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.3</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.2.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
aspectjweaver
AspectJWeaver作为AspectJ框架的核心组件,是一个用于实现AOP的Java库,是实现AOP的关键。
AspectJWeaver本身没有使用任何依赖,纯jdk代码(可能包含native的C代码,没看源码)编写
AspectJWeaver提供了切面(Aspect)、连接点(Join Point)、通知(Advice)等核心概念的实现。通过AspectJWeaver,开发者可以定义切面来横切多个类或方法,并在特定的连接点插入通知,从而在不修改原有代码的情况下增加新的功能或行为。
AspectJWeaver的工作原理:反射和代理模式
commons-pool2
开源对象池,决定哪些bean被spring aop代理增强
spring-beans
负责bean生命周期管理
spring-core
提供了Spring Ioc和DI的实现
spring-aspects的pom依赖
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.1.5</version>
<name>Spring Aspects</name>
<description>Spring Aspects</description>
<url>https://github.com/spring-projects/spring-framework</url>
<organization>
<name>Spring IO</name>
<url>https://spring.io/projects/spring-framework</url>
</organization>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>jhoeller</id>
<name>Juergen Hoeller</name>
<email>jhoeller@pivotal.io</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-framework</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-framework</developerConnection>
<url>https://github.com/spring-projects/spring-framework</url>
</scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/spring-projects/spring-framework/issues</url>
</issueManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.21.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
我们可以看到只有一个aspectjweaver
spring-aspectj是Spring对AspectJ的集成
spring-aop和spring-acpectj共同组成了spring的aop实现
Spring AOP的两种动态代理
基于JDK的动态代理和基于CGLIB的动态代理
JDK的动态代理适用于实现了接口的类,如果要代理的目标类没有实现任何一个接口,JDK的动态代理无法生效;此时我们可以用CGLIB实现动态代理(CGLIB是基于继承实现的代理)