Spring基础知识和SpringAOP

Posted 老了敲不动了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring基础知识和SpringAOP相关的知识,希望对你有一定的参考价值。

一、课程目标

Spring的概述Spring的环境搭建SpringIOC与AOP

二、Spring的概述

Spring框架,可以解决对象创建以及对象之间依赖关系的一种框架。且可以和其他框架一起使用;Spring与Struts, Spring与hibernate(起到整合(粘合)作用的一个框架)Spring提供了一站式解决方案:1) Spring Core spring的核心功能: IOC容器, 解决对象创建及依赖关系2) Spring Web Spring对web模块的支持。- 可以与struts整合,让struts的action创建交给spring- spring mvc模式3) Spring DAO Spring 对jdbc操作的支持 【JdbcTemplate模板工具类】4) Spring ORM spring对orm的支持:既可以与hibernate整合,【session】也可以使用spring的对hibernate操作的封装5)Spring AOP 切面编程6)SpringEE spring 对javaEE其他模块的支持

三、Spring的环境搭建

3.1spring环境需要maven 坐标

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itmayiedu</groupId> <artifactId>itmayiedu-spring</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- 引入Spring-AOP等相关Jar --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>3.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>3.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.5.3</version> </dependency><dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_2</version> </dependency> </dependencies></project>

3.2需要交给Spring管理注入类

public class UserEntity private String name; private Integer age; public String getName() return name; public void setName(String name) this.name = name; public Integer getAge() return age; public void setAge(Integer age) this.age = age;

3.3spring配置文件

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="userEntity" class="com.itmayiedu.entity.UserEntity" /></beans>

3.4测试类

public class SpringTest public static void main(String[] args) ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( "applicationContext.xml"); UserEntity userEntity = (UserEntity) applicationContext.getBean("userEntity"); System.out.println(userEntity);

四、Spring 加载过程

4.1 spring是单例还是多例?

4.2 spring作用域

4.2.1singleton 作用域当一个bean的 作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把 一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都 将返回被缓存的对象实例,这里要注意的是singleton作用域和GOF设计模式中的单例是完全不同的,单例设计模式表示一个ClassLoader中 只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时 候,spring的IOC容器中只会存在一个该bean。4.2.2singleton Prototypeprototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的 getBean()方法)都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。 清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用 bean的后置处理器,该处理器持有要被清除的bean的引用。)4.2.3singleton requestrequest表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,配置实例:request、session、global session使用的时候首先要在初始化web的web.xml中做如下配置:4.2.4singleton sessionsession作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效

五、SpringIOC 容器

SpringIOC容器,是spring核心内容。作用: 创建对象 & 处理对象的依赖关系

5.1 IOC容器创建对象:

创建对象, 有几种方式:1) 调用无参数构造器2) 带参数构造器3) 工厂创建对象工厂类,静态方法创建对象工厂类,非静态方法创建对象

<!-- 无参构造函数 --> <bean id="user1" class="com.itmayiedu.entity.UserEntity" scope="prototype" /> <!-- 有参构造函数 --> <bean id="user2" class="com.itmayiedu.entity.UserEntity"> <constructor-arg name="name" type="java.lang.String" value="张三"></constructor-arg> <constructor-arg name="age" type="java.lang.Integer" value="18"></constructor-arg> </bean> <bean id="factory" class="com.itmayiedu.entity.ObjectFactory"></bean> <!-- 通过实例工厂方法创建 --> <bean id="user3" factory-bean="factory" factory-method="getInstance"></bean> <!-- 通过静态工厂方法创建 --> <bean id="user4" class="com.itmayiedu.entity.ObjectFactory" factory-method="getStaticInstance"></bean>

5.2依赖注入:

Spring中,如何给对象的属性赋值? 【DI, 依赖注入】1) 通过构造函数2) 通过set方法给属性注入值3) p名称空间4) 注解

(常用)Set方法注入值

<!-- dao instance --> <bean id="userDao" class="cn.itmayiedu.UserDao"></bean> <!-- service instance --> <bean id="userService" class="cn.itmayiedu.UserService"> <property name="userDao" ref="userDao"></property> </bean> <!-- action instance --> <bean id="userAction" class="cn.itmayiedu.UserAction"> <property name="userService" ref="userService"></property> </bean>

p 名称空间注入属性值 (优化)

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- ###############对象属性赋值############### --> <!-- 给对象属性注入值: # p 名称空间给对象的属性注入值 (spring3.0以上版本才支持) --> <bean id="userDao" class="cn.itmayiedu.UserDao"></bean> <bean id="userService" class="cn.itmayiedu.UserService" p:userDao-ref="userDao"></bean> <bean id="userAction" class="cn.itmayiedu.UserAction" p:userService-ref="userService"></bean> <!-- 传统的注入: <bean id="user" class="cn.itmayiedu.User" > <property name="name" value="xxx"></property> </bean> --> <!-- p名称空间优化后 --> <bean id="user" class="cn.itmayiedu.User" p:name="Jack0001"></bean> </beans>

注解版本使用

注解方式可以简化spring的IOC容器的配置!

使用注解步骤:1)先引入context名称空间xmlns:context=“http://www.springframework.org/schema/context”2)开启注解扫描<context:component-scan base-package=“cn.itcast.e_anno2”></context:component-scan>3)使用注解通过注解的方式,把对象加入ioc容器。

创建对象以及处理对象依赖关系,相关的注解: @Component 指定把一个对象加入IOC容器

@Repository 作用同@Component; 在持久层使用@Service 作用同@Component; 在业务逻辑层使用@Controller 作用同@Component; 在控制层使用

@Resource 属性注入

总结:1) 使用注解,可以简化配置,且可以把对象加入IOC容器,及处理依赖关系(DI)2) 注解可以和XML配置一起使用。@Resource与@autiwer

五、代理模式

5.1 概述

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理访问目标对象。这样好处:可以在目标对象实现的基础上,增强额外的功能操作。(扩展目标对象的功能)。

举例:明星(邓紫棋)—经纪人<-------用户

目标(代理)

代理模式的关键点:代理对象与目标对象。

5.2 静态代理

静态代理,1) 代理对象,要实现与目标对象一样的接口;2) 举例:保存用户(模拟)Dao , 直接保存DaoProxy, 给保存方法添加事务处理

总结静态代理:1)可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。2)缺点:–》 因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。–》 一旦接口增加方法,目标对象与代理对象都要维护。

解决:代理工厂? 可以使用动态代理。

5.3 动态代理

动态代理,1)代理对象,不需要实现接口;2)代理对象的生成,是利用JDKAPI, 动态的在内存中构建代理对象(需要我们指定创建 代理对象/目标对象 实现的接口的类型;);3) 动态代理, JDK代理, 接口代理;

JDK中生成代理对象的API:|-- Proxystatic Object newProxyInstance(ClassLoader loader, 指定当前目标对象使用类加载器Class<?>[] interfaces, 目标对象实现的接口的类型InvocationHandler h 事件处理器)

动态代理总结:代理对象不需要实现接口,但是目标对象一定要实现接口;否则不能用动态代理!(class $Proxy0 implements IuserDao)

思考:有一个目标对象,想要功能扩展,但目标对象没有实现接口,怎样功能扩展?Class UserDao// 子类的方式Class subclass extends UserDao

以子类的方式实现(cglib代理)

5.4 Cglib代理

Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。

JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。Cglib子类代理:1) 需要引入cglib – jar文件, 但是spring的核心包中已经包括了cglib功能,所以直接引入spring-core-3.2.5.jar即可。2)引入功能包后,就可以在内存中动态构建子类3)代理的类不能为final, 否则报错。4) 目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。

在Spring的AOP编程中,如果加入容器的目标对象有实现接口,用JDK代理;如果目标对象没有实现接口,用Cglib代理;

六、手动实现AOP编程

AOP 面向切面的编程,AOP可以实现“业务代码”与“关注点代码”分离

// 保存一个用户public void add(User user) Session session = null; Transaction trans = null; try session = HibernateSessionFactoryUtils.getSession(); // 【关注点代码】 trans = session.beginTransaction(); // 【关注点代码】 session.save(user); // 核心业务代码 trans.commit(); //…【关注点代码】 catch (Exception e) e.printStackTrace(); if(trans != null) trans.rollback(); //..【关注点代码】 finally HibernateSessionFactoryUtils.closeSession(session); ..【关注点代码】

分析总结:关注点代码,就是指重复执行的代码。业务代码与关注点代码分离,好处?– 关注点代码写一次即可;-开发者只需要关注核心业务;-运行时期,执行核心业务代码时候动态植入关注点代码; 【代理】

如何分离?过程式/对象式/代理模式分离

七、AOP编程

3.1 概述:

Aop, aspect object programming 面向切面编程功能: 让关注点代码与业务代码分离!关注点,重复代码就叫做关注点;切面,关注点形成的类,就叫切面(类)!面向切面编程,就是指 对很多功能都有的重复的代码抽取,再在运行的时候网业务方法上动态植入“切面类代码”。切入点,执行目标对象方法,动态植入切面代码。可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。

3.2 注解方式实现AOP编程

步骤:1) 先引入aop相关jar文件 (aspectj aop优秀组件)spring-aop-3.2.5.RELEASE.jar 【spring3.2源码】aopalliance.jar 【spring2.5源码/lib/aopalliance】aspectjweaver.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\\lib】aspectjrt.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\\lib】

注意: 用到spring2.5版本的jar文件,如果用jdk1.7可能会有问题。需要升级aspectj组件,即使用aspectj-1.8.2版本中提供jar文件提供。

2) bean.xml中引入aop名称空间

3) 开启aop注解

4) 使用注解@Aspect 指定一个类为切面类@Pointcut(“execution(* com.itmayiedu.service.UserService.add(…))”) 指定切入点表达式

@Before(“pointCut_()”) 前置通知: 目标方法之前执行@After(“pointCut_()”) 后置通知:目标方法之后执行(始终执行)@AfterReturning(“pointCut_()”) 返回后通知: 执行方法结束前执行(异常不执行)@AfterThrowing(“pointCut_()”) 异常通知: 出现异常时候执行@Around(“pointCut_()”) 环绕通知: 环绕目标方法执行

@Component@Aspectpublic class Aop @Before("execution(* com.itmayiedu.service.UserService.add(..))") public void begin() System.out.println("前置通知"); @After("execution(* com.itmayiedu.service.UserService.add(..))") public void commit() System.out.println("后置通知"); @AfterReturning("execution(* com.itmayiedu.service.UserService.add(..))") public void afterReturning() System.out.println("运行通知"); @AfterThrowing("execution(* com.itmayiedu.service.UserService.add(..))") public void afterThrowing() System.out.println("异常通知"); @Around("execution(* com.itmayiedu.service.UserService.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable System.out.println("我是环绕通知-前"); proceedingJoinPoint.proceed(); System.out.println("我是环绕通知-后");

3.3 XML方式实现AOP编程

Xml实现aop编程:1) 引入jar文件 【aop 相关jar, 4个】2) 引入aop名称空间3)aop 配置* 配置切面类 (重复执行代码形成的类)* aop配置拦截哪些方法 / 拦截到方法后应用通知代码

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- dao 实例 --> <bean id="userDao" class="com.itmayiedu.UserDao"></bean> <bean id="orderDao" class="com.itmayiedu.OrderDao"></bean> <!-- 切面类 --> <bean id="aop" class="com.itmayiedu.Aop"></bean> <!-- Aop配置 --> <aop:config> <!-- 定义一个切入点表达式: 拦截哪些方法 --> <aop:pointcut expression="execution(* com.itmayiedu.*.*(..))" id="pt"/> <!-- 切面 --> <aop:aspect ref="aop"> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pt"/> <!-- 前置通知: 在目标方法调用前执行 --> <aop:before method="begin" pointcut-ref="pt"/> <!-- 后置通知: --> <aop:after method="after" pointcut-ref="pt"/> <!-- 返回后通知 --> <aop:after-returning method="afterReturning" pointcut-ref="pt"/> <!-- 异常通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/> </aop:aspect> </aop:config></beans>

 

10. Spring AOP源码解析

10 Spring AOP源码解析

目录

10 Spring AOP源码解析

Pt1 Spring AOP基础知识

Pt1.1 AOP核心概念

Pt1.2 AOP切点配置规则

(1) execution

(2) within

(3) args

Pt2 Spring AOP使用示例

Pt2.1 AOP日志(XML)

pom.xml

spring.xml配置AOP信息

加载spring.xml

定义切面

定义Controller

运行Controller

Pt2.2 JDBC(Annotation)

pom.xml

AOP配置

Controller

测试请求

Pt3 Spring AOP核心类

Pt3.1 BeanPostProcessor

Pt3.2 TargetSource

Pt3.3 Advice

Pt3.4 Advisor

Pt3.5 Advised

(1) TargetClassAware

(2) Advised

(3) ProxyConfig

(4) AdvisedSupport

(5) ProxyCreatorSupport

(6) ProxyFactory

Pt3.6 Pointcut

Pt3.7 ClassFilter

Pt3.8 MethodMatcher

Pt3.9 Aware

Pt4 Spring AOP初始化剖析

Pt4.1 寻找入口

Pt4.2 选择代理策略

Pt4.3 生成AOP代理类

Pt4.4 执行代理方法

Pt4.5 触发AOP通知

Pt4.6 流程图

参考资料


Pt1 Spring AOP基础知识

AOP是Aspect Oriented Programming的缩写,意思是面向切面编程,通过预编译和运行时的动态代理,实现在不修改源代码的情况下给程序动态添加功能。同时AOP也为服务方和调用方实现解耦提供了很有效的方式。

在日常编码中,通常分为业务代码和非业务代码,比如事务、日志等,这些代码和业务代码混在一起。举个最常见的例子,就是JDBC的事务处理,有大量的模板代码是的事务处理代码,这些大量重复的、复制粘贴的、和业务处理无关的代码,为维护带来了风险和困难。AOP通过代理机制,可以将这些非业务代码剥离,使得他们可以独立于业务代码存在,从而降低维护的难度和风险。

所以,Spring AOP是一种编程范式,主要目的是将非功能性需求从功能型需求中分离出来,达到解耦的目标。

 


Pt1.1 AOP核心概念

  • Aspect(切面)

    Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。

     

  • Jointpoint(连接点)

    表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。

     

  • Pointcut(切点)

    表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

     

  • Advice(增强)

    Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。

    • @Before:该注解标注的方法在业务模块代码执行之前执行,其不能阻止业务模块的执行,除非抛出异常;

    • @AfterReturning:该注解标注的方法在业务模块代码执行之后执(正常返回)行;

    • @AfterThrowing:该注解标注的方法在业务模块抛出指定异常后执行;

    • @After:该注解标注的方法在所有的Advice执行完成后执行,无论业务模块是否抛出异常,类似于finally的作用;

    • @Around:该注解功能最为强大,其所标注的方法用于编写包裹业务模块执行的代码,其可以传入一个ProceedingJoinPoint用于调用业务模块的代码,无论是调用前逻辑还是调用后逻辑,都可以在该方法中编写,甚至其可以根据一定的条件而阻断业务模块的调用;

    • @DeclareParents:其是一种Introduction类型的模型,在属性声明上使用,主要用于为指定的业务模块添加新的接口和相应的实现。

    • @Aspect:严格来说,其不属于一种Advice,该注解主要用在类声明上,指明当前类是一个组织了切面逻辑的类,并且该注解中可以指定当前类是何种实例化方式,主要有三种:singleton、perthis和pertarget,具体的使用方式后面会进行讲解。

     

  • Target(目标对象)

    织入 Advice 的目标对象.。目标对象可以被一个或者多个切面织入。

     

  • Weaving(织入)

    将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程,就是执行method同时执行advice。

     

  • AOP代理(AopProxy)

    在Spring中有两种形式的代理:JDK动态代理和CGLib代理。后面会详细介绍。

 


Pt1.2 AOP切点配置规则

(1) execution

由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的。

 

如下是execution表达式的语法:

 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

这里问号表示当前项可以有也可以没有,其中各项的语义如下:

  • modifiers-pattern:方法的可见性,如public,protected;

  • ret-type-pattern:方法的返回值类型,如int,void等;

  • declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;

  • name-pattern:方法名类型,如buisinessService();

  • param-pattern:方法的参数类型,如java.lang.String;

  • throws-pattern:方法抛出的异常类型,如java.lang.Exception;

 

如下是一个使用execution表达式的例子:

 execution(public * com.spring.service.BusinessObject.businessService(java.lang.String,..))

上述切点表达式将会匹配使用public修饰,返回值为任意类型,并且是com.spring.BusinessObject类中名称为businessService的方法,方法可以有多个参数,但是第一个参数必须是java.lang.String类型的方法。上述示例中我们使用了..通配符,关于通配符的类型,主要有两种:

 

  • *通配符,该通配符主要用于匹配单个单词,或者是以某个词为前缀或后缀的单词。

如下示例表示返回值为任意类型,在com.spring.service.BusinessObject类中,并且参数个数为零的方法:

 execution(* com.spring.service.BusinessObject.*())

下述示例表示返回值为任意类型,在com.spring.service包中,以Business为前缀的类,并且是类中参数个数为零方法:

 execution(* com.spring.service.Business*.*())

 

  • ..通配符,该通配符表示0个或多个项,主要用于declaring-type-pattern和param-pattern中,如果用于declaring-type-pattern中,则表示匹配当前包及其子包,如果用于param-pattern中,则表示匹配0个或多个参数。

如下示例表示匹配返回值为任意类型,并且是com.spring.service包及其子包下的任意类的名称为businessService的方法,而且该方法不能有任何参数:

 execution(* com.spring.service..*.businessService())

这里需要说明的是,包路径service...businessService()中的..应该理解为延续前面的service路径,表示到service路径为止,或者继续延续service路径,从而包括其子包路径;后面的.businessService(),这里的*表示匹配一个单词,因为是在方法名前,因而表示匹配任意的类。

如下示例是使用..表示任意个数的参数的示例,需要注意,表示参数的时候可以在括号中事先指定某些类型的参数,而其余的参数则由..进行匹配:

 execution(* com.spring.service.BusinessObject.businessService(java.lang.String,..))

 

(2) within

within表达式的粒度为类,其参数为全路径的类名(可使用通配符),表示匹配当前表达式的所有类都将被当前方法环绕。如下是within表达式的语法:

 within(declaring-type-pattern)

 

within表达式只能指定到类级别,如下示例表示匹配com.spring.service.BusinessObject中的所有方法:

 within(com.spring.service.BusinessObject)

 

within表达式路径和类名都可以使用通配符进行匹配,比如如下表达式将匹配com.spring.service包下的所有类,不包括子包中的类:

within(com.spring.service.*)

 

如下表达式表示匹配com.spring.service包及子包下的所有类:

within(com.spring.service..*)

 

(3) args

args表达式的作用是匹配指定参数类型和指定参数数量的方法,无论其类路径或者是方法名是什么。这里需要注意的是,args指定的参数必须是全路径的。如下是args表达式的语法:

args(param-pattern)

 

如下示例表示匹配所有只有一个参数,并且参数类型是java.lang.String类型的方法:

args(java.lang.String)

 

也可以使用通配符,但这里通配符只能使用..,而不能使用*。如下是使用通配符的实例,该切点表达式将匹配第一个参数为java.lang.String,最后一个参数为java.lang.Integer,并且中间可以有任意个数和类型参数的方法:

args(java.lang.String,..,java.lang.Integer)

 


Pt2 Spring AOP使用示例

Pt2.1 AOP日志(XML)

  • pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

 

  • spring.xml配置AOP信息

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 自动扫描装配 -->
    <context:component-scan base-package="com.example.springdemo"/>

    <!--启用spring的一些annotation -->
    <context:annotation-config/>

    <!-- AOP配置 -->
    <bean id="logAspect" class="com.example.springdemo.aop.log.LogAspect"></bean>

    <aop:config>
        <aop:aspect id="log" ref="logAspect">
            <aop:pointcut id="logPointcut" expression="execution(* com.example.springdemo.controller.LogController.LogDemo(..))"/>
            <aop:before method="logBefore" pointcut-ref="logPointcut"/>
            <aop:after method="logAfter" pointcut-ref="logPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

 

  • 加载spring.xml

@Configuration
@ImportResource(locations = {"classpath:spring.xml"})
public class ConfigClass {
}

 

  • 定义切面

public class LogAspect {

    public LogAspect() {
        System.out.println("init");
    }

    public void logBefore() {
        System.out.println("Before execution log.");
    }

    public void logAfter() {
        System.out.println("After execution log.");
    }
}

 

  • 定义Controller

@RestController
public class LogController {

    @RequestMapping(value = "logAop", method = RequestMethod.GET)
    public String LogDemo() {
        return "Execute Log Aop test.";
    }
}

 

  • 运行Controller

Before execution log.
After execution log.

 


Pt2.2 JDBC(Annotation)

  • pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

 

  • AOP配置

@Component
@Aspect
public class JdbcAspect {

    // 定义切点
    @Pointcut("execution(* com.example.springdemo.controller.JdbcController.jdbcDemo(..))")
    public void jdbcPointcut() {
    }

    @Before("JdbcAspect.jdbcPointcut()")
    public void jdbcBefore() {
        System.out.println("begin transaction");
    }

    @AfterReturning("JdbcAspect.jdbcPointcut()")
    public void jdbcAfter() {
        System.out.println("commit transaction");
    }

    @AfterThrowing("JdbcAspect.jdbcPointcut()")
    public void jdbcException() {
        System.out.println("rollback transaction");
    }
}

 

  • Controller

@RestController
public class JdbcController {

    @RequestMapping(value = "jdbcAop", method = RequestMethod.GET)
    public String jdbcDemo() {
        return "Execute jdbc Aop test.";
    }
}

 

  • 测试请求

​​​​​​​

begin transaction 
commit transaction

 

 


Pt3 Spring AOP核心类

Pt3.1 BeanPostProcessor

BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口,它可以监听容器触发的Bean生命周期事件。向容器中注册BeanPostProcessor之后,容器中管理的Bean就具备了接受IoC容器回调事件的能力。

源码如下:

/**
 * BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口,它可以监听容器触发的Bean生命周期事件。
 * 向容器中注册BeanPostProcessor之后,容器中管理的Bean就具备了接受IoC容器回调事件的能力。
 *
 * 他提供的两个回调方法和容器管理的Bean生命周期事件密切相关,可以为用户提供在Spring IoC容器初始化Bean过程中,去自定义的处理操作提供钩子事件。
 */
public interface BeanPostProcessor {

	// 在Bean的初始化之前提供回调入口
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	// 在Bean的初始化之后提供回调入口
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

 


Pt3.2 TargetSource

在Spring代理目标bean的时候,其并不是直接创建一个目标bean的对象实例的,而是通过一个TargetSource类型的对象将目标bean进行封装,Spring Aop获取目标对象始终是通过TargetSource.getTarget()方法进行的。

换句话说,proxy(代理对象)代理的不是target,而是TargetSource。

 

通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如: target pool(目标对象池)、hot swap(运行时目标对象热替换),等等。

 

源码实现:

/**
 * 在Spring代理目标bean的时候,其并不是直接创建一个目标bean的对象实例的,而是通过一个TargetSource类型的对象将目标bean进行封装,
 * Spring Aop获取目标对象始终是通过TargetSource.getTarget()方法进行的。
 * 换句话说,proxy(代理对象)代理的不是target,而是TargetSource。
 *
 * 通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,
 * 可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。
 * 这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如:target pool(目标对象池)、hot swap(运行时目标对象热替换),等等。
 */
public interface TargetSource extends TargetClassAware {

	// 本方法主要用于返回目标bean的Class类型
	@Override
	@Nullable
	Class<?> getTargetClass();

	// 这个方法用户返回当前bean是否为静态的,比如常见的单例bean就是静态的,而prototype就是动态的,
	// 这里这个方法的主要作用是,对于静态的bean,spring是会对其进行缓存的,在多次使用TargetSource
	// 获取目标bean对象的时候,其获取的总是同一个对象,通过这种方式提高效率
	boolean isStatic();

	// 获取目标bean对象,这里可以根据业务需要进行自行定制
	@Nullable
	Object getTarget() throws Exception;

	// Spring在用完目标bean之后会调用这个方法释放目标bean对象,对于一些需要池化的对象,这个方法是必须
	// 要实现的,这个方法默认不进行任何处理
	void releaseTarget(Object target) throws Exception;
}

 


Pt3.3 Advice

Advice表示的是在Pointcut切点上应该执行的方法,即切面Aspect中定义的方法逻辑。

这些方法可以在目标方法之前、之后、包裹、抛出异常等等任何地方执行。

 

源码:

// 标记通知方法
public interface Advice {

}

 


Pt3.4 Advisor

Advisor是 Pointcut 与 Advice 的组合,Advice 是执行的通知方法,而要知道方法何时执行,则 Advice 必需与 Pointcut 组合在一起,这就诞生了 Advisor 。

  • Advice表示通知的内容

  • Pointcut表示通知的时机

  • Advisor表示通知人(它知道通知的时机,且知道是通知的内容)

 

下图是Advisor的类图:

 

Advisor是顶层抽象接口,定义了操作Advice的行为:

// Pointcut 与 Advice 的组合
public interface Advisor {

	// 空的Advice通知
	Advice EMPTY_ADVICE = new Advice() {};

	// 获取通知方法
	Advice getAdvice();

	// 判定当前Advice是否和特定目标值相关
	boolean isPerInstance();
}

 

PointcutAdvisor继承Advisor,定义了操作Pointcut行为,此时具体了可以操作Pointcut和Advice的行为:

public interface PointcutAdvisor extends Advisor {

	// 获取Advisor关联的Pointcut
	Pointcut getPointcut();
}

 

AbstractPointcutAdvisor新增了排序和序列化的能力:

// AbstractPointcutAdvisor是在PointcutAdvisor的基础上,增加Ordered和Serializable接口的能力。
// Spring就提供了Ordered这个接口,来处理相同接口实现类的优先级问题。
@SuppressWarnings("serial")
public abstract class AbstractPointcutAdvisor implements PointcutAdvisor, Ordered, Serializable {
	// order用于标记执行的优先级。
	@Nullable
	private Integer order;

	// 调用者可以手动来指定Order
	public void setOrder(int order) {
		this.order = order;
	}

	// 获取执行顺序
	@Override
	public int getOrder() {
		if (this.order != null) {
			return this.order;
		}

		// 若调用者没有指定Order,那就拿advice的order为准(若有),否则LOWEST_PRECEDENCE表示最后执行
		Advice advice = getAdvice();
		if (advice instanceof Ordered) {
			return ((Ordered) advice).getOrder();
		}
		return Ordered.LOWEST_PRECEDENCE;
	}

	// 该属性还没有被Spring使用,永远返回TURE
	@Override
	public boolean isPerInstance() {
		return true;
	}


	@Override
	public boolean equals(@Nullable Object other) {
		if (this == other) {
			return true;
		}
		if (!(other instanceof PointcutAdvisor)) {
			return false;
		}
		PointcutAdvisor otherAdvisor = (PointcutAdvisor) other;
		return (ObjectUtils.nullSafeEquals(getAdvice(), otherAdvisor.getAdvice()) &&
				ObjectUtils.nullSafeEquals(getPointcut(), otherAdvisor.getPointcut()));
	}

	@Override
	public int hashCode() {
		return PointcutAdvisor.class.hashCode();
	}
}

 

AbstractGenericPointcutAdvisor保存Advice对象:

public abstract class AbstractGenericPointcutAdvisor extends AbstractPointcutAdvisor {
	// 保存Advisor关联的Advice
	private Advice advice = EMPTY_ADVICE;

	// setter and getter
	public void setAdvice(Advice advice) {
		this.advice = advice;
	}
	@Override
	public Advice getAdvice() {
		return this.advice;
	}

	@Override
	public String toString() {
		return getClass().getName() + ": advice [" + getAdvice() + "]";
	}
}

 

DefaultPointcutAdvisor是功能最强大的Advisor,保存了Advice和Pointcut对象,同时具备操作这两个对象的能力:

public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
	// 保存Advisor关联的Pointcut对象
	private Pointcut pointcut = Pointcut.TRUE;

	// 获取空的Advisor,在使用前必须指定Pointcut对象和Advice对象。
	public DefaultPointcutAdvisor() {
	}

	// 根据指定Advice和默认的Pointcut生成Advisor对象,使用默认Pointcut时,Advice将匹配所有方法。
	public DefaultPointcutAdvisor(Advice advice) {
		this(Pointcut.TRUE, advice);
	}

	// 根据指定Pointcut和Advice生成Advisor对象
	public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
		this.pointcut = pointcut;
		setAdvice(advice);
	}

	// setter and getter
	public void setPointcut(@Nullable Pointcut pointcut) {
		this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
	}
	@Override
	public Pointcut getPointcut() {
		return this.pointcut;
	}

	@Override
	public String toString() {
		return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
	}
}

 


Pt3.5 Advised

Advised是对整个代理执行的抽象,创建代理的工厂ProxyFactory,Bean工厂BeanFactory都在该接口中保存。该接口维护了一些代理必要的属性,以及对 Advice,Advisor,Interceptor 的操作,任何被 Spring 代理的对象都可以强转为 Advised。

 

从Advised类图中可以看到,实现类ProxyFactory是创建AOP代理类的工厂。

 

(1) TargetClassAware

接口TargetClassAware表明代理类拥有对目标代理类的操作权限git,具体可以看Aware接口的介绍。

 

(2) Advised

Advised是核心接口,拥有对代理工厂、目标工厂、目标类的操作能力。

// Advised 接口的实现着主要是代理生成的对象与AdvisedSupport (Advised的支持器)
public interface Advised extends TargetClassAware {

	// 这个 frozen 决定是否 AdvisedSupport 里面配置的信息是否改变
	boolean isFrozen();

	// 是否代理指定的类, 而不是一些 Interface
	boolean isProxyTargetClass();

	// 返回代理的接口
	Class<?>[] getProxiedInterfaces();

	// 判断这个接口是否是被代理的接口
	boolean isInterfaceProxied(Class<?> intf);

	// 设置代理的目标对象
	void setTargetSource(TargetSource targetSource);

	// 获取代理的对象
	TargetSource getTargetSource();

	// 判断是否需要将 代理的对象暴露到 ThreadLocal中, 而获取对应的代理对象则通过 AopContext 获取
	void setExposeProxy(boolean exposeProxy);

	// 返回是否应该暴露 代理对象
	boolean isExposeProxy();

	// 设置 Advisor 是否已经在前面过滤过是否匹配 Pointcut (极少用到)
	void setPreFiltered(boolean preFiltered);

	// 获取 Advisor 是否已经在前面过滤过是否匹配 Pointcut (极少用到)
	boolean isPreFiltered();

	// 获取所有的 Advisor
	Advisor[] getAdvisors();

	// 增加 Advisor 到链表的最后
	void addAdvisor(Advisor advisor) throws AopConfigException;

	// 在指定位置增加 Advisor
	void addAdvisor(int pos, Advisor advisor) throws AopConfigException;

	// 删除指定的 Advisor
	boolean removeAdvisor(Advisor advisor);

	// 删除指定位置的 Advisor
	void removeAdvisor(int index) throws AopConfigException;

	// 返回 Advisor 所在位置de index
	int indexOf(Advisor advisor);

	// 将指定的两个 Advisor 进行替换
	boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;

	// 增加 Advice <- 这个Advice将会包裹成 DefaultPointcutAdvisor
	void addAdvice(Advice advice) throws AopConfigException;

	// 在指定 index 增加 Advice <- 这个Advice将会包裹成 DefaultPointcutAdvisor
	void addAdvice(int pos, Advice advice) throws AopConfigException;

	// 删除给定的 Advice
	boolean removeAdvice(Advice advice);

	// 获取 Advice 的索引位置
	int indexOf(Advice advice);

	// 将 ProxyConfig 通过 String 形式返回
	String toProxyConfigString();
}

 

(3) ProxyConfig

保存了创建代理类的配置属性。

// 创建AOP代理类的配置
public class ProxyConfig implements Serializable {

	/** use serialVersionUID from Spring 1.2 for interoperability. */
	private static final long serialVersionUID = -8409359707199703185L;

	// 标记是否直接对目标类进行代理,而不是通过接口产生代理
	// 或者说,标记是否使用CGLIB动态代理,true,表示使用CGLIB的方式产生代理对象,false,表示使用JDK动态代理
	private boolean proxyTargetClass = false;

	// 标记是否进行优化。启动优化通常意味着在代理对象被创建后,增强的修改将不会生效,因此默认值为false。
	// 如果exposeProxy设置为true,即使optimize为true也会被忽略。
	private boolean optimize = false;

	// 标记是否需要阻止通过该配置创建的代理对象转换为Advised类型,默认值为false,表示代理对象可以被转换为Advised类型
	boolean opaque = false;

	// 标记代理对象是否应该被aop框架通过AopContext以ThreadLocal的形式暴露出去。
	// 当一个代理对象需要调用它自己的另外一个代理方法时,这个属性将非常有用。默认是是false,以避免不必要的拦截。
	boolean exposeProxy = false;

	// 标记该配置是否需要被冻结,如果被冻结,将不可以修改增强的配置。
	// 当我们不希望调用方修改转换成Advised对象之后的代理对象时,这个配置将非常有用。
	private boolean frozen = false;

	// setter and getter
	public void setProxyTargetClass(boolean proxyTargetClass) {
		this.proxyTargetClass = proxyTargetClass;
	}
	public boolean isProxyTargetClass() {
		return this.proxyTargetClass;
	}

	public void setOptimize(boolean optimize) {
		this.optimize = optimize;
	}
	public boolean isOptimize() {
		return this.optimize;
	}

	public void setOpaque(boolean opaque) {
		this.opaque = opaque;
	}
	public boolean isOpaque() {
		return this.opaque;
	}

	public void setExposeProxy(boolean exposeProxy) {
		this.exposeProxy = exposeProxy;
	}
	public boolean isExposeProxy() {
		return this.exposeProxy;
	}

	public void setFrozen(boolean frozen) {
		this.frozen = frozen;
	}
	public boolean isFrozen() {
		return this.frozen;
	}

	// 浅拷贝
	public void copyFrom(ProxyConfig other) {
		Assert.notNull(other, "Other ProxyConfig object must not be null");
		this.proxyTargetClass = other.proxyTargetClass;
		this.optimize = other.optimize;
		this.exposeProxy = other.exposeProxy;
		this.frozen = other.frozen;
		this.opaque = other.opaque;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("proxyTargetClass=").append(this.proxyTargetClass).append("; ");
		sb.append("optimize=").append(this.optimize).append("; ");
		sb.append("opaque=").append(this.opaque).append("; ");
		sb.append("exposeProxy=").append(this.exposeProxy).append("; ");
		sb.append("frozen=").append(this.frozen);
		return sb.toString();
	}
}

 

(4) AdvisedSupport

AdvisedSupport是用于生成Aop代理类的辅助类,保存了目标类、Advisor和method之间的关系。

public class AdvisedSupport extends ProxyConfig implements Advised {

	/** use serialVersionUID from Spring 2.0 for interoperability. */
	private static final long serialVersionUID = 2651364800145442165L;

	// 默认空的TargetSource
	public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE;
	TargetSource targetSource = EMPTY_TARGET_SOURCE;

	/** Whether the Advisors are already filtered for the specific target class. */
	// 当前Advisor是否已经过滤了目标类
	private boolean preFiltered = false;

	/** The AdvisorChainFactory to use. */
	AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();

	/** Cache with Method as key and advisor chain List as value. */
	// 缓存method和Advisor之间关系
	private transient Map<MethodCacheKey, List<Object>> methodCache;

	// 目标代理类继承的接口
	private List<Class<?>> interfaces = new ArrayList<>();

	// 关联的Advisor
	private List<Advisor> advisors = new ArrayList<>();
	private Advisor[] advisorArray = new Advisor[0];

    // 方法基本都是对于属性的操作
    // ......
}

 

(5) ProxyCreatorSupport

生成代理类的辅助类ProxyCreatorSupport。

public class ProxyCreatorSupport extends AdvisedSupport {

	// AOP代理类生成工厂,用于创建代理类。
	private AopProxyFactory aopProxyFactory;

	// 监听Advisor的变化
	private final List<AdvisedSupportListener> listeners = new ArrayList<>();

	// 第一个AopProxyu对象生成时置为True
	private boolean active = false;

	public ProxyCreatorSupport() {
		this.aopProxyFactory = new DefaultAopProxyFactory();
	}
	public ProxyCreatorSupport(AopProxyFactory aopProxyFactory) {
		Assert.notNull(aopProxyFactory, "AopProxyFactory must not be null");
		this.aopProxyFactory = aopProxyFactory;
	}

	// aopProxyFactory的setter/getter
	public void setAopProxyFactory(AopProxyFactory aopProxyFactory) {
		Assert.notNull(aopProxyFactory, "AopProxyFactory must not be null");
		this.aopProxyFactory = aopProxyFactory;
	}
	public AopProxyFactory getAopProxyFactory() {
		return this.aopProxyFactory;
	}

	// 操作AdvisedSupportListener
	public void addListener(AdvisedSupportListener listener) {
		Assert.notNull(listener, "AdvisedSupportListener must not be null");
		this.listeners.add(listener);
	}
	public void removeListener(AdvisedSupportListener listener) {
		Assert.notNull(listener, "AdvisedSupportListener must not be null");
		this.listeners.remove(listener);
	}


	// 获取AopProxy,用于生成Bean的代理类
	protected final synchronized AopProxy createAopProxy() {
		// 第一个AopProxy被创建时,执行以下激活操作。
		if (!this.active) {
			activate();
		}

		// 获取AopProxy,用于生成Bean的代理类
		return getAopProxyFactory().createAopProxy(this);
	}

	// 激活AopProxy配置。
	private void activate() {
		this.active = true;
		for (AdvisedSupportListener listener : this.listeners) {
			listener.activated(this);
		}
	}

	// 监听advice变更事件
	@Override
	protected void adviceChanged() {
		super.adviceChanged();
		synchronized (this) {
			if (this.active) {
				for (AdvisedSupportListener listener : this.listeners) {
					listener.adviceChanged(this);
				}
			}
		}
	}

	protected final synchronized boolean isActive() {
		return this.active;
	}
}

 

(6) ProxyFactory

ProxyFactory是生成目标AOP代理类的工厂类。

public class ProxyFactory extends ProxyCreatorSupport {

	public ProxyFactory() {
	}

	// 根据目标类生成代理类
	public ProxyFactory(Object target) {
		setTarget(target);
		setInterfaces(ClassUtils.getAllInterfaces(target));
	}

	// 根据目标接口生成代理类
	public ProxyFactory(Class<?>... proxyInterfaces) {
		setInterfaces(proxyInterfaces);
	}

	public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {
		addInterface(proxyInterface);
		addAdvice(interceptor);
	}

	public ProxyFactory(Class<?> proxyInterface, TargetSource targetSource) {
		addInterface(proxyInterface);
		setTargetSource(targetSource);
	}

	// 生成代理类
	public Object getProxy() {
		return createAopProxy().getProxy();
	}

	// 基于指定的ClassLoader和Factory设置,创建AOP代理类。
	public Object getProxy(@Nullable ClassLoader classLoader) {
		// createAopProxy():获取AOP代理类,有JdkDynamicAopProxy和CglibAopProxy两种。
		// getProxy:使用AOP代理类,生成目标Bean的代理类。
		// 这里假定使用JdkDynamicAopProxy的方式执行getProxy方法
		return createAopProxy().getProxy(classLoader);
	}

	// 生成代理类
	public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) {
		return (T) new ProxyFactory(proxyInterface, interceptor).getProxy();
	}

	// 生成代理类
	@SuppressWarnings("unchecked")
	public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource) {
		return (T) new ProxyFactory(proxyInterface, targetSource).getProxy();
	}

	// 生成代理类
	public static Object getProxy(TargetSource targetSource) {
		if (targetSource.getTargetClass() == null) {
			throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class");
		}
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.setTargetSource(targetSource);
		proxyFactory.setProxyTargetClass(true);
		return proxyFactory.getProxy();
	}
}

 


Pt3.6 Pointcut

Pointcut是AOP中非常重要的一个类,它定义了AOP切点逻辑,约定了Advice通知的时机。

 

我们先来看看其中一个实现类AspectJExpressionPointcut类的相关类图。

 

从类图上可以看出,AspectJExpressionPointcut主要有4个顶层接口:

  • Pointcut:定义和切点相关的能力。

  • Aware:标记当前类具备某种行为的感知能力。

  • MethodMatcher:用来匹配mehtod是否匹配切点的规则,从而决定是否需要通知Advice。

  • ClassFilter:用来匹配Class是否匹配切点规则。

ClassFilter限定在类级别上,MethodMatcher限定在方法级别上。

 

首先来看Pointcut顶层接口的能力:

// 切点的顶层抽象接口
public interface Pointcut {

	// 返回当前Pointcut的ClassFilter,用于匹配Class是否符合切点逻辑。
	ClassFilter getClassFilter();

	// 返回当前Pointcut的MethodMatcher对象,用于匹配method规则。
	MethodMatcher getMethodMatcher();

	// 默认值,如果返回此结果表明所有method都会被匹配成功。
	Pointcut TRUE = TruePointcut.INSTANCE;
}

 

// 匹配规则使用String表达式的Pointcut
public interface ExpressionPointcut extends Pointcut {

	// 返回切点规则表达式
	@Nullable
	String getExpression();
}

 

public abstract class AbstractExpressionPointcut implements ExpressionPointcut, Serializable {

	// 切点位置
	@Nullable
	private String location;

	// 切点匹配规则表达式
	@Nullable
	private String expression;


	// setter and getter
	public void setLocation(@Nullable String location) {
		this.location = location;
	}
	@Nullable
	public String getLocation() {
		return this.location;
	}

	public void setExpression(@Nullable String expression) {
		this.expression = expression;
		try {
			onSetExpression(expression);
		}
		catch (IllegalArgumentException ex) {
			// Fill in location information if possible.
			if (this.location != null) {
				throw new IllegalArgumentException("Invalid expression at location [" + this.location + "]: " + ex);
			}
			else {
				throw ex;
			}
		}
	}
	protected void onSetExpression(@Nullable String expression) throws IllegalArgumentException {
	}
	@Override
	@Nullable
	public String getExpression() {
		return this.expression;
	}
}

 

最终实现类是AspectJExpressionPointcut类:

// Pointcut实现类。
@SuppressWarnings("serial")
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
		implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {

	private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
    
    ......
        
}

 


Pt3.7 ClassFilter

比较简单,直接看源码:

// ClassFilter限定在类级别上,判断是否符合Pointcut规则。
@FunctionalInterface
public interface ClassFilter {

	// true表示能够匹配。那就会进行织入的操作
	boolean matches(Class<?> clazz);

	// 默认值。会匹配所有的类,TrueClassFilter不是public得class,所以只是Spring内部自己使用的。
	ClassFilter TRUE = TrueClassFilter.INSTANCE;
}

 


Pt3.8 MethodMatcher

MethodMatcher用于定义Pointcut匹配method的逻辑:

// Pointcut匹配逻辑:判断方法是否符合Pointcut规则,执行Advice通知。
public interface MethodMatcher {

	// 这个称为静态匹配:在匹配条件不是太严格时使用,可以满足大部分场景的使用
	boolean matches(Method method, Class<?> targetClass);

	// 这个称为动态匹配(运行时匹配): 它是严格的匹配。在运行时动态的对参数的类型进行匹配
	boolean matches(Method method, Class<?> targetClass, Object... args);

	// 两个方法的分界线就是boolean isRuntime()方法,步骤如下
	// 1、先调用静态匹配,若返回true。此时就会继续去检查isRuntime()的返回值
	// 2、若isRuntime()还返回true,那就继续调用动态匹配
	// (若静态匹配都匹配上,动态匹配那铁定更匹配不上得~~~~)
	boolean isRuntime();

	// 默认值
	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

 

实现接口:

// 校验方法是否匹配Pointcut规则
public interface IntroductionAwareMethodMatcher extends MethodMatcher {
   boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions);
}

 


Pt3.9 Aware

我们在介绍下Aware接口。

打开Aware接口可以看到,他是一个空接口,没有任何实现。他标识的其实是一种能力,Spring容器中的Bean能够具备感知某一对象的能力。

// Aware是顶层超类,用于标识是否具备一种能力。
public interface Aware {

}

 

比如,BeanFactoryAware是继承了Aware的接口,它就代表Bean对归属Spring IoC容器的感知能力。

// 实现了BeanFactoryAware接口的类,能够感知到他所属的BeanFactory。直白点,就是Bean能够获得他所属的BeanFactory对象。
// 这就是Aware接口的能力,他使得继承类能够具备某一种感知的能力。
public interface BeanFactoryAware extends Aware {

	// 设置当前Bean所属的BeanFactory实例
	void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

 

还有TargetClassAware接口:

// 该接口使得,对应的代理类拥有感知TargetClass的能力,就是能够操作目标代理类。
public interface TargetClassAware {
	// 获得目标代理类。
	@Nullable
	Class<?> getTargetClass();
}

 


Pt4 Spring AOP初始化剖析

Pt4.1 寻找入口

在AOP基础部分中,介绍过AOP的原理是生成原始Bean对象的代理类,从而在执行过程中通过动态代理完成逻辑横切。

所以AOP的初始化是在Bean初始化完成之后发生的。在DI部分,通过调用BeanFactory.getBean()触发Bean的初始化和依赖注入,当整个Bean初始化逻辑之心完成后,会继续执行AOP初始化处理,为Bean创建AOP代理类。

 

DI操作是通过调用#getBean()触发的,其核心逻辑是在AbstractAutowireCapableBeanFactory#doCreateBean()执行。我们跳过IoC、DI和MVC部分的详细逻辑介绍,直接从doCreateBean()来找AOP的入口。

先来看#doCreateBean()实例化逻辑:

// org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(
    String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    throws BeansException {

    // ......
    
	// 跳过DI前后其他逻辑,这部分在DI部分已经详细介绍过。
    // 不管Bean的Scope是那种,最终都是通过#createBean()·。
    return createBean(beanName, mbd, args);
    
    // ......
}

 

还没到AOP的地方,继续往下:

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
// 真正完成指定Bean对象创建的方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException {

    // ......
    
    // 跳过DI前后其他逻辑,这部分在DI部分已经详细介绍过。
    // Bean的处理只要分为三个步骤:
    //    1. 实例化,执行构造器;
    //    2. 依赖注入;
    //    3. 调用init-method;
    // 到这里,DI已经完成Bean的实例化操作,这是第一步,我们继续往下看。
    
    // 2、对Bean进行依赖注入
    populateBean(beanName, mbd, instanceWrapper);

    // 3、Bean实例对象依赖注入完成后,开始对Bean实例对象进行初始化,为Bean实例对象应用BeanPostProcessor后置回调处理
    // AOP的逻辑在这里执行
    exposedObject = initializeBean(beanName, exposedObject, mbd);
       
    // ......
}

 

找到#initializeBean,这里才是真正开始的地方:

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
// 初始化容器创建的Bean实例对象,为其添加BeanPostProcessor后置处理器
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    // JDK安全机制验证权限
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        // 为Bean实例对象包装相关属性:类名称、类加载器、所属容器等。
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // 1、调用BeanPostProcessor的回调方法,执行Bean初始化之前的操作。
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        // 2、调用Bean的Init-Method方法,完成Bean的初始化操作。
        // 这个Bean的初始化方法是在Spring Bean定义配置文件中通过init-method属性指定的。
        // 这里不是新建Bean的实例操作,此时的Bean已经完成实例化处理,生成了Bean的实例对象,也完成了属性的设置。
        // 如果Bean定义中配置了init-method,这里将执行该方法完成Bean配置中所有init动作。
        invokeInitMethods(beanName, wrappedBean, mbd);
        // 到此为止,Bean实例的初始化操作已经完成,创建了Bean对象,完成了属性的依赖注入,执行了自定义的init-method方法。
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        // 3、调用BeanPostProcessor的回调方法,执行Bean初始化之后的操作。
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

代码里分为三步:

  1. 调用BeanPostProcessor的回调方法,执行Bean初始化之前的操作。

  2. 调用Bean的Init-Method方法,完成Bean的初始化操作。

  3. 调用BeanPostProcessor的回调方法,执行Bean初始化之后的操作。

 

AOP是在Bean完成初始化之后触发的,前两部Bean还没有完成初始化,所以真正跟AOP相关的操作,是在第3步开始的:

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
// 调用BeanPostProcessor的回调方法,执行Bean初始化之后的操作。
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    // 遍历容器为所创建的Bean添加的所有BeanPostProcessor后置处理器。
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 调用Bean实例所有定义的BeanPostProcessor初始化之后回调操作,完成初始化后的自定义操作。
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

 


Pt4.2 选择代理策略

执行BeanPostProcessor的后置回调方法,AOP的BeanPostProcessor实现类很多,其中AbstractAutoProxyCreator是非常重要的一个。

// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

 

wrapIfNecessary()是非常核心的方法,负责处理AOP代理。

// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
/**
 * 如果给定的Bean符合AOP条件,则生成代理类。
 * 
 * @param bean         原始Bean实例对象
 * @param beanName     Bean的名称
 * @param cacheKey     从Bean信息得到的唯一缓存Key
 */
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }

    // 判断是否应该代理这个Bean
    if (Boolean

以上是关于Spring基础知识和SpringAOP的主要内容,如果未能解决你的问题,请参考以下文章

Spring 基础知识学习

前端与spring交互数据的处理和接收基础方式

SpringBoot 学习笔记01—Spring基础入门

Spring部分基础知识点

Spring的一些基础知识

Spring基础(十七):Spring日志框架和测试支持