当前位置: 首页 > news >正文

1688网站怎么样哈尔滨门户网站是什么

1688网站怎么样,哈尔滨门户网站是什么,免费图片素材高清,网站建设实战文章目录 一、Spring 基础 - 控制反转#xff08;IOC#xff09;1.1. 引入1.2. 如何理解 IOCSpring Bean 是什么#xff1f;IoC 是什么#xff1f;IoC 能做什么#xff1f;IoC 和 DI 是什么关系#xff1f; 1.3. IoC 配置的三种方式xml 配置Java 配置注解配置 1.4. 依赖注… 文章目录 一、Spring 基础 - 控制反转IOC1.1. 引入1.2. 如何理解 IOCSpring Bean 是什么IoC 是什么IoC 能做什么IoC 和 DI 是什么关系 1.3. IoC 配置的三种方式xml 配置Java 配置注解配置 1.4. 依赖注入的三种方式setter 方式构造函数注解注入 1.5. IoC 和 DI 使用问题小结为什么推荐构造器注入方式Autowired 和 Resource 以及 Inject 等注解注入有何区别 二、Spring 基础 - 面向切面编程AOP2.1. 引入2.2. 如何理解 AOPAOP 是什么AOP 术语Spring AOP 和 AspectJ 是什么关系 2.3. AOP 的配置方式XML Schema 配置方式AspectJ 注解方式 2.4. AOP 使用问题小结切入点pointcut的申明规则多种增强通知的顺序Spring AOP 还是完全用 AspectJ 三、Spring 进阶 - IOC 体系结构设计3.1 站在设计者的角度考虑设计 IOC 容器3.2 Spring IoC 的体系结构设计BeanFactory 和 BeanRegistryIOC 容器功能规范和 Bean 的注册BeanDefinition各种 Bean 对象及其相互的关系 3.3 ApplicationContextIOC 接口设计和实现ApplicationContext 接口的设计**ApplicationContext 接口的实现** 四、Spring 进阶 - IOC 初始化流程4.1 引入4.2 如何将 Bean 从 XML 配置中解析后放到 IoC 容器中的初始化的入口设置资源解析器和环境设置配置路径初始化的主体流程 4.3 总结 五、Spring 进阶 - Bean 实例化声明周期、循环依赖等5.1 引入5.2 BeanFactory 中 getBean 的主体思路初步的思考Spring 中 getBean 的主体思路 5.3 重点Spring 如何解决循环依赖问题Spring 单例模式下的属性依赖Spring 为何不能解决非单例属性之外的依赖循环那么其它循环依赖如何解决 5.4 重点Spring 中 Bean 的生命周期Spring Bean 的生命周期流程Spring Bean 生命周期案例 六、Spring 进阶 - AOP 切面的实现6.1 引入6.2 AOP 配置标签的解析config 配置标签的解析aspectj-autoproxy 配置标签的解析 6.3 注解切面代理创建类AnnotationAwareAspectJAutoProxyCreatorpostProcessorBeforeInstantiationpostProcessAfterInitialization 6.4 总结 七、Spring 进阶 - AOP 代理的创建7.1 引入7.2 代理的创建 八、Spring 进阶 - Cglib 代理实现8.1 引入动态代理要解决什么问题 8.2 Cglib 代理的例子pom 包依赖定义实体类被代理的类Cglib 代理使用代理简单测试 8.3 Cglib 代理的流程8.4 Spring AOP 中 Cglib 代理的实现 九、Spring 进阶 - JDK 代理实现9.1 引入什么是 JDK 代理 9.2 JDK 代理的例子定义实体被代理的类和接口JDK 代理类使用代理简单测试 9.3 JDK 代理的流程ProxyGenerator 生成代码从生成的 Proxy 代码看执行流程 9.4 Spring AOP 中 JDK 代理的实现Spring AOP Jdk 代理的创建Spring AOP Jdk 代理的执行 本文围绕 Spring 的 IOC 和 AOP 两大核心思想展开学习从基础到进阶一步步使用并了解 Spring 对其实现方式。 一、Spring 基础 - 控制反转IOC 1.1. 引入 Spring 框架管理这些 Bean 的创建工作即用户管理 Bean 转变为框架管理 Bean这个就叫做控制反转 - Inversion of ControlIoCSpring 框架托管创建的 Bean 放在哪里这便是 IoC ContainerSpring 框架为了更好让用户配置 Bean必然会引入不同方式来配置 Bean这便是 xml 配置、Java 配置、注解配置等支持Spring 框架既然接管了 Bean 的生成必然需要管理整个 Bean 的生命周期等应用程序代码从 IoC Container 中获取依赖的 Bean注入到应用程序中这个过程叫依赖注入Dependency InjectionDI所以说控制反转是通过依赖注入实现的其实它们是同一个概念的不同角度描述。通俗来说就是 IoC 是涉及思想DI 是实现方式在依赖注入时有哪些方式呢这就是构造器方式Autowired、Resource、Qualifier…同时 Bean 之间存在依赖可能存在先后顺序问题以及循环依赖问题等 ​ 本章节将在此基础上进一步解读 IOC 的含义以及 IOC 的使用方式。 1.2. 如何理解 IOC Spring Bean 是什么 IOC Container 管理的是 Spring Bean那么 Spring Bean 是什么呢 ​ Spring 里面的 bean 就类似是定义的一个组件而这个组件的作用就是实现某个功能的这里所定义的 bean 就相当于给了你一种更为简便的方法来调用这个组件去实现要完成的功能。 IoC 是什么 IoC - Inversion of Control即 “控制反转”不是什么技术而是一种设计思想。在 Java 开发中IoC 意味着将设计好的对象交给容器控制而不是传统的在对象内部控制。 ​ 我们来深入分析一下 谁控制谁控制什么 ​ 传统 Java SE 程序设计我们直接在对象内部通过 new 进行创建对象是程序主动去创建依赖对象而 IoC 是有专门一个容器来创建这些对象即由 IoC 容器来控制对象的创建谁控制谁当然是 IoC 容器控制了对象控制什么那就是主要控制了外部资源获取不只是对象包括比如文件等。 为何是反转哪方面反转了 ​ 有反转就有正转传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象也就是正转而反转则是由容器来帮忙创建及注入依赖对象为何是反转因为由容器帮我们查找及注入依赖对象对象只是被动的接受依赖对象所以是反转哪些方面反转了依赖对象的获取被反转了。 ​ 如下图例所示 ​ 首先是传统程序设计下都是主动去创建相关对象然后再进行组合 ​ 当有了 Ioc / DI 的容器后在客户端类中不再主动去创建这些对象了如下图 IoC 能做什么 IoC 不是一种技术只是一种思想一个重要的面向对象编程的法则它能指导我们如何设计出松耦合、更优良的程序。 ​ 传统应用程序都是由我们在类内部主动创建依赖对象从而导致类与类之间高耦合难以测试有了 IoC 容器后 把创建和查找依赖对象的控制权交给了容器由容器进行注入组合对象所以对象之间是松耦合这样也方便测试利于功能复用更重要的是使得程序的整个体系结构变得非常灵活。 ​ 其实 IoC 对编程带来的最大改变不是从代码上而是从思想上发生了 “主从换位” 的变化。应用程序原本是老大要获取什么资源都是主动出击但是在 IoC/DI 思想中应用程序就变成被动的了被动的等待 IoC 容器来创建并注入它所需要的资源了。 ​ IoC 很好地体现了面向对象编程法则之一 —— 好莱坞法则“别找我们我们找你”即由 IoC 容器帮对象找响应的依赖对象并注入而不是由对象主动去找。 IoC 和 DI 是什么关系 控制反转是通过依赖注入实现的其实它们是同一个概念的不同角度描述。通俗来说就是 IoC 是设计思想DI 是实现方式。 ​ DI - Dependency Injection即依赖注入组件之间依赖关系由容器在运行期决定形象的说即由容器动态地将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能而是为了提升组件重用的频率并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制我们只需要通过简单的配置而无需任何代码就可以指定目标需要的资源完成自身的业务逻辑而不需要关心具体的资源来自何处由谁实现。 1.3. IoC 配置的三种方式 xml 配置 ​ 顾名思义就是将 bean 的信息配置到 .xml 文件里通过 Spring 加载文件为我们创建 bean。这种方式出现很多早期的 SSM 项目中将第三方类库或者一些配置工具都以这种方式进行配置主要原因是由于第三方类不支持 Spring 注解。 优点可以适用于任何场景结构清晰通俗易懂。缺点配置繁琐不宜维护枯燥无味拓展性差。 举例 配置 xx.xml 文件声明命名空间和配置 bean ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbean iduserDao classio.xinghuo.spring.UserDao/bean iduserService classio.xinghuo.spring.UserServiceImplproperty nameuserDao refuserDao//bean /beansJava 配置 ​ 将类的创建交给我们配置的 JavaConfig 类来完成Spring 只负责维护和管理采用纯 Java 创建方式。其本质上就是把 XML 上的配置声明移到到 Java 配置类中。 优点适用于任何场景配置方便因为是纯 Java 代码拓展性高十分灵活。缺点由于是采用 Java 类的方式声明不明显如果大量配置可读性比较差。 举例 创建一个配置类添加 Configuration 注解声明为配置类创建方法方法上加上 Bean该方法用于创建实例并返回该实例创建后会交由 Spring 管理方法名建议与实例名相同首字母小写。注实力类不需要加任何注解。 Configuration public class BeansConfig {Bean(userDao)public UserDao userDao() {return new UserDao();}Bean(userService)public UserServiceImpl userService() {UserServiceImpl userService new UserServiceImpl();userService.setUserDao(userDao());return userService;} }注解配置 ​ 通过在类上加注解的方式来声明一个类交给 Spring 管理Spring 会自动扫描带有 Component、Controller、Service、Repository 这四个注解的类然后帮我们创建并管理前提是需要先配置 Spring 的注解扫描器。 优点开发便捷通俗易懂方便维护。缺点具有局限性对于一些第三方资源无法添加注解。只能采用 XML 或 JavaConfig 的方式配置。 举例 对类添加 Component 相关的注解设置 ComponentScan 的 basePackage比如 context:component-scan base-packageio.xinghuo.spring/ 或者 ComponentScan(io.xinghuo.spring) 注解 等。 Service public class UserServiceImpl {Autowiredprivate UserDao userDao;public ListUser findAll() {return userDao.getAll();} }1.4. 依赖注入的三种方式 常用的依赖注入主要有三种方式构造方法注入Construct 注入、setter 注入、基于注解的注入接口注入。 setter 方式 在 XML 配置方式中property 都是 setter 方式注入比如下面的 xml ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbean iduserDao classio.xinghuo.spring.UserDao/bean iduserService classio.xinghuo.spring.UserServiceImplproperty nameuserDao refuserDao//bean /beans本质上包含两步 第一步需要 new UserServiceImpl() 创建对象所以需要默认构造函数第二步调用 setUserDao() 函数注入 userDao 的值所以需要 setUserDao() 的函数 所以对应的 service 类是这样的 public class UserServiceImpl {private UserDao userDao;public UserServiceImpl() {}public ListUser findAll() {return userDao.getAll();}public void setUserDao(UserDao userDao) {this.userDao userDao;} }在注解和 Java 配置方式下 public class UserServiceImpl {private UserDao userDao;public ListUser findAll() {return userDao.getAll();}Autowiredpublic void setUserDao(UserDao userDao) {this.userDao userDao;} }在 Spring3.X 刚推出的时候推荐使用注入的就是这种但是这种方式比较麻烦所以在 Spring4.X 版本中推荐构造函数注入。 构造函数 在 XML 配置方式中constructor-org 是通过构造函数参数注入比如下面的 xml ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbean iduserDao classio.xinghuo.spring.UserDao/bean iduserService classio.xinghuo.spring.UserServiceImplconstructor-arg nameuserDao refuserDao//bean /beans本质上是 new UserServiceImpl(userDao) 创建对象所以对应的 service 类是这样的 public class UserServiceImpl {private final UserDao userDao;public UserServiceImpl(UserDao userDao) {this.userDao userDao;}public ListUser findAll() {return userDao.getAll();} }在注解和 Java 配置方式下 Service public class UserServiceImpl {private final UserDao userDao;Autowiredpublic UserServiceImpl(UserDao userDao) {this.userDao userDao;}public ListUser findAll() {return userDao.getAll();} }在 Spring4.X 版本中推荐的注入方式就是这种。 注解注入 ​ 以 Autowired自动注入注解注入为例修饰符有三个属性Constructor、byType、byName。默认按照 byType 注入。 Constructor通过构造方法进行自动注入spring 会匹配与构造方法参数类型一致的 bean 进行注入如果有一个多参数的构造方法一个只有一个参数的构造方法在容器中查找到多个匹配多参数构造方法的 bean那么 spring 会优先将 bean 注入到多参数的构造方法中。byName被注入 bean 的 id 名必须与 set 方法后半截匹配并且 id 名称的第一个单词首字母必须小写这一点与手动 set 注入优点不同。byType查找所有的 set 方法将符合参数类型的 bean 注入。 比如 public class UserServiceImpl {Autowiredprivate UserDao userDao;public ListUser findAll() {return userDao.getAll();} }1.5. IoC 和 DI 使用问题小结 为什么推荐构造器注入方式 ​ 先看一下 Spring 在文档中解释 The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. ​ 简单翻译一下这个构造器注入的方式能够保证注入的组件不可变并且确保需要的依赖不为空。此外构造器注入的依赖总是能够在返回客户端组件代码的时候保证完全初始化的状态。 下面简单解释一下 依赖不可变其实说的就是 final 关键字。依赖不为空省去了我们对其检查当要实例化 UserServiceImpl 的时候由于自己实现了有参数的构造函数所以不会调用默认无参构造函数那么就需要 Spring 容器传入所需要的参数所以就两种情况1、有该类型的参数 - 传入正确实例化。2、无该类型的参数 - 报错。完全初始化的状态这个可以跟上面的依赖不为空结合起来向构造器传参之前要确保注入的内容不为空那么肯定要调用依赖组件的构造方法完成实例化。而在 Java 类加载实例化的过程中构造方法是最后一步之前如果有父类先初始化父类然后自己的成员变量最后才是构造方法所以返回来的都是初始化之后的状态。 所以通常是这样的 Service public class UserServiceImpl {private final UserDao userDao;public UserServiceImpl(final UserDao userDao) {this.userDao userDao;} }如果使用 setter 注入缺点是显而易见对于 IoC 容器以外的环境除了使用反射来提供它需要的依赖之外无法复用该实现类。而且将一直是个潜在的隐患因为你不调用将一直无法发现 NPE 的存在。 UserServiceImpl userService new UserServiceImpl(); userService.findAll(); // - NullPointerException, 潜在的隐患循环依赖的问题使用 field 注入可能会导致循环依赖即 A 里面注入 BB 里面又注入 A public class A {Autowiredprivate B b; }public class B {Autowiredprivate A a; }​ 如果使用构造器注入在 spring 项目启动的时候就会抛出BeanCurrentlyInCreationExceptionRequested bean is currently in creation: Is there an unresolvable circular reference从而提醒你避免循环依赖如果是 field 注入的话启动的时候就不会报错在使用那个 bean 的时候才会报错。 Autowired 和 Resource 以及 Inject 等注解注入有何区别 Autowired Autowired 注解源码 Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) Retention(RetentionPolicy.RUNTIME) Documented public interface Autowired {boolean required() default true; }从 Autowired 注解源码上看可以使用在下面这些地方 Target(ElementType.CONSTRUCTOR) #构造函数 Target(ElementType.METHOD) #方法 Target(ElementType.PARAMETER) #方法参数 Target(ElementType.FIELD) #字段、枚举的常量 Target(ElementType.ANNOTATION_TYPE) #注解还有一个 value 属性默认是 true。 简单总结 Autowired 是 Spring 自带的注解通过 AutowiredAnnotationBeanPostProcessor 类实现的依赖注入Autowired 可以作用在 CONSTRUCTOR、METHOD、FIELD、ANNOTATION_TYPEAutowired 默认是根据类型byType进行自动装配的如果有多个类型一样的 Bean 候选者需要指定按照名称byName进行装配则需要配合 Qualifier ​ 指定名称后如果 Spring IoC 容器中没有对应的组件 bean 抛出 NoSuchBeanDefinitionException也可以将 Autowired 中 required 配置为 false如果配置为 false 之后当没有找到相应 bean 的时候系统不会抛出异常。 Resource Resource 注解源码 Target({TYPE, FIELD, METHOD}) Retention(RUNTIME) public interface Resource {String name() default ;// 其他省略 }从 Resource 注解源码上看可以使用在下面这些地方 Target(ElementType.TYPE) #接口、类、枚举、注解 Target(ElementType.FIELD) #字段、枚举的常量 Target(ElementType.METHOD) #方法name 指定注入指定名称的组件。 简单总结 Resource 是 JSR250 规范的实现在 javax.annotation 包下Resource 可以作用在 TYPE、FIELD、METHOD 上Resource 是默认根据属性名称进行自动装配的如果有多个类型一样的 Bean 候选者则可以通过 name 进行指定注入。 Inject Inject 注解源码 Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) Retention(RetentionPolicy.RUNTIME) Documented public interface Inject { }从 Inject 注解源码上看可以用使用在下面这些地方 Target(ElementType.CONSTRUCTOR) #构造函数 Target(ElementType.METHOD) #方法 Target(ElementType.FIELD) #字段、枚举的常量简单总结 Inject 是 JSR330 中的规范需要导入 javax.inject.Inject jar 包才能实现注入Inject 可以作用 CONSTRUCTOR、METHOD、FIELD 上Inject 是根据类型进行自动装配的如果需要按名称进行装配则需要配置 Named 总结 Autowired 是 Spring 自带的Resource 是 JSR250 规范实现的Inject 是 JSR330 规范实现的Autowired、Inject 用法基本一样不同的是 Inject 没有 required 属性Autowired、Inject 是默认按照类型匹配的Resource 是按照名称匹配的Autowired 如果需要按照名称匹配需要和 Qualifier 一起使用Inject 和 Named 一起使用Resource 则通过 name 进行指定。 二、Spring 基础 - 面向切面编程AOP 2.1. 引入 Spring 框架通过定义切面通过拦截切点实现了不同业务模块的解耦这个就叫面向切面编程 - Aspect Oriented ProgrammingAOP为什么 Aspect 注解使用的是 aspect 的 jar 包呢这就引入了 Aspect4J 和 Spring AOP 的历史历史渊源只有理解了 Aspect4J 和 Spring 的渊源才能理解有些注解上的兼容设计如何支持更多拦截方式来实现解耦以满足更多场景需求呢这就是 Around、Pointcut … 等的设计那么 Spring 框架又是如何实现 AOP 的呢这就引入 代理技术分静态代理和动态代理动态代理又包含 JDK 代理和 CGLIB 代理等 ​ 本章节将在此基础上进一步解读 AOP 的含义以及 AOP 的使用方式。 2.2. 如何理解 AOP AOP 是什么 AOP 为 Aspect Oriented Programming 的缩写意为面向切面编程 ​ AOP 最早是 AOP 联盟组织提出的指定的一套规范Spring 将 AOP 的思想引入框架之中通过预编译方式和运行期间动态代理实现程序的统一维护的一种技术。 先来看一个例子如何给 UserServiceImpl 中所有方法添加日志 public class UserServiceImpl {public ListUser findAll() {System.out.println(execute method findAll);return Collections.singletonList(new User(xinghuo, 18));}public void addUser() {System.out.println(execute method: addUser);} }我们将记录日志功能解耦为日志切面它的目标是解耦进而引出 AOP 的理念就是将分散在各个业务逻辑代码中相同的代码通过**横向切割**的方式抽取到一个独立的模块中​ OOP 面向对象编程针对业务处理的实体及其属性和行为进行抽象封装以获得更加清晰高效的逻辑单元划分。而 AOP 则是针对业务处理过程中的切面进行提取它所面对的是处理过程的某个步骤或阶段以获得逻辑过程中各部分之间低耦合的隔离效果。这两种设计思想在目标上有着本质的差异。 AOP 术语 首先让我们从一些重要的 AOP 概念和术语开始。这些术语不是 Spring 特有的。 连接点Jointpoint表示需要在程序中插入横切关注点的拓展点连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等Spring 只支持方法执行连接点在 AOP 中表示为 在哪里干。切入点Pointcut选择一组相关连接点的模式即可以认为i连接点的集合Spring 支持 perl5 正则表达式和 AspectJ 切入点模式Spring 默认使用 AspectJ 语法在 AOP 中表示在哪里干的集合。通知Advice在连接点上执行的行为通知提供了在 AOP 中需要在切入点所选择的连接点处进行拓展现有行为的手段包括前置通知before advice、后置通知after advice、环绕通知around advice在 Spring 中通过代理模式实现 AOP并通过拦截器模式以环绕连接点的拦截器链织入通知在 AOP 中表示为干什么。方面/切面Aspect横切关注点的模块化比如上边提到的日志组件。可以认为是通知、引入和切入点的组合在 Spring 中可以使用 Schema 和 AspectJ 方式进行组织实现在 AOP 中表示为在哪里干和干什么集合。引入inter-type declaration也称为内部类型声明为已有的类添加额外新的字段或方法Spring 允许引入新的接口必须对应一个实现到所有被代理对象目标对象在 AOP 中表示为干什么引入什么。目标对象Target Object需要被织入横切关注点的对象即该对象是切入点选择的对象需要被通知的对象从而也可称为通知对象由于 Spring AOP 通过代理模式实现从而这个对象永远是被代理对象在 AOP 中表示为对谁干。织入Weaving把切面连接到其它的应用程序类型或者对象上并创建一个被通知的对象。AOP 代理AOP ProxyAOP 框架使用代理模式创建的对象从而实现在连接点处插入通知即应用切面就是通过代理来对目标对象应用切面。在 Spring 中AOP 代理可以用 JDK 动态代理或 CGLIB 代理实现而通过拦截器模型应用切面。在 AOP 中表示为怎么实现的一种典型方式。 通知类型 前置通知Before advice在某连接点之前执行的通知但这个通知不能阻止连接点之前的执行流程除非它抛出一个异常。后置通知After returing advice在某连接点正常完成后执行的通知例如一个方法没有抛出任何异常正常返回。异常通知After throwing advice在方法抛出异常退出时执行的通知。最终通知After finallyadvice当某连接点退出的时候执行的通知无论是正常返回还是异常退出。环绕通知Around advice包围一个连接点的通知如方法调用。这是一个强大的通知类型。环绕通知可以在方法调用的前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。 ​ 环绕通知是最常用的通知类型。和 AspectJ 一样Spring 提供所有类型的通知我们推荐你使用尽可能简单的通知类型来实现需要的功能。例如如果你只需要一个方法的返回值来更新缓存最后使用后置通知而不是环绕通知尽管环绕通知也能完成同样的事情。用最合适的通知类型可以使得编程模型变得简单并且能够避免很多潜在的错误。比如你不需要在 JointPoint 上调用用于环绕通知的 proceed() 方法就不会有调用的问题。 下面是术语串联图 Spring AOP 和 AspectJ 是什么关系 首先 AspectJ 是什么 ​ AspectJ 是一个 java 实现的 AOP 框架它能够对 java 代码进行 AOP 编译一般在编译期进行让 java 代码具有 AspectJ 的 AOP 功能当然需要特殊的编译器。 ​ 可以这样说 AspectJ 是目前实现 AOP 框架中最成熟功能最丰富的语言更幸运的是AspectJ 与 java 程序完全兼容几乎是无缝关联因此对于有 java 编程基础的人来说上手和使用都非常容易。 其次为什么需要清楚 Spring AOP 和 AspectJ 的关系 ​ Aspect 以及增强的几个注解为什么不是 Spring 包而是来源于 aspectJ 呢 Spring AOP 和 AspectJ 是什么关系 AspectJ 是更强的 AOP 框架是实际意义的 AOP 标准Spring AOP 使用纯 Java 实现它不需要专门的编译过程它一个重要的原则就是无侵入性non-invasivenessSpring 开发小组喜欢 AspectJ 注解风格更胜于 Spring XML 配置所以在 Spring 2.0 使用了和 AspectJ 5 一样的注解并使用 AspectJ 来做切入点解析和匹配。但是AOP 在运行时仍旧是纯的 Spring AOP并不依赖于 AspectJ 的编译器或织入器weaverSpring 2.5 对 AspectJ 的支持在一些环境下增加了 AspectJ 的装载时编织支持同时提供了一个新的 bean 切入点。 更多关于 AspectJ ​ 了解 AspectJ 应用到 java 代码的过程这个过程称为织入对于织入这个概念可以简单理解为 aspect切面应用到目标函数类的过程。 ​ 对于这个过程一般分为动态织入和静态织入 动态织入的方式是在运行时动态将要增强的代码织入到目标类中这样往往是通过动态代理技术完成的如 Java JDK 的动态代理Proxy底层通过反射实现或者 CGLIB 的动态代理底层通过继承实现Spring AOP 采用的就是基于运行时增强的代理技术。AspectJ 采用的就是静态织入的方式。AspetJ 主要采用的是编译器织入在这个期间使用 AspectJ 的 acj 编译器类似 javac把 aspect 类编译为 class 字节码后在 java 目标类编译时织入即先编译 aspect 类再编译目标类。 2.3. AOP 的配置方式 Spring AOP 支持对 XML 模式和基于 AspectJ 注解的两种配置方式。 XML Schema 配置方式 ​ Spring 提供了使用 “aop” 命名空间来定义一个切面看下面例子 定义目标类 public class AopServiceImpl {public void doMethod1() {System.out.println(doMethod1);}public String doMethod2() {System.out.println(doMethod2);return Hello World!;}public String doMethod3() throws Exception {System.out.println(doMethod3);throw new Exception(some exception);} }定义切面类 public class LogAspect {public void doBefore() {System.out.println(前置通知);}public Object doAround(ProceedingJoinPoint pjp) throws Throwable {System.out.println(环绕通知进入方法);Object result pjp.proceed();System.out.println(环绕通知退出方法);return result;}public void doAfterReturning(String returnValue) {System.out.println(后置通知返回值是 returnValue);}public void doAfterThrowing(Exception e) {System.out.println(异常通知异常信息是 e.getMessage());}public void doAfter() {System.out.println(最终通知);} }XML 配置 AOP ?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsdbean idaopService classio.xinghuo.spring.aop.AopServiceImpl/bean idlogAspect classio.xinghuo.spring.aop.LogAspect/aop:configaop:aspect reflogAspectaop:pointcut idpointCutMethod expressionexecution(* io.xinghuo.spring.aop.*.*(..))/aop:around methoddoAround pointcut-refpointCutMethod/aop:before methoddoBefore pointcut-refpointCutMethod/aop:after-returning methoddoAfterReturning pointcut-refpointCutMethod returningreturnValue/aop:after-throwing methoddoAfterThrowing pointcut-refpointCutMethod throwinge/aop:after methoddoAfter pointcut-refpointCutMethod//aop:aspect/aop:config /beansAspectJ 注解方式 ​ 基于 XML 的声明式 AspectJ 存在一些不足需要在 Spring 配置文件配置大量的代码信息为了解决这个问题SPring 使用了 AspectJ 框架为 AOP 的实现提供了一套注解。 注解名称解释Aspect用来定义一个切面pointcut用于定义切入点表达式。在使用时还需要定义一个包含名称和任意参数类型的方法签名来表示切入点名称这个方法签名就是一个返回值 void且方法体为空的普通方法。Before用于定义前置通知相当于 BeforeAdvice。在使用时通常需要指定一个 value 属性值该属性值用于指定一个切入点表达式可以是已有切入点也可以直接定义切入点表达式。AfterReturning用于定义后置通知相当于 AfterReturningAdvice。在使用时可以指定 pointcut / value 和 returning 属性其中 pointcut / value 这两个属性的作用一样都用于指定切入点表达式。Around用于定义环绕通知相当于 MethodInterceptor。在使用时需要指定一个 value 属性该属性用于指定该通知被织入的切入点。After-Throwing用于定义异常通知来处理程序中未处理的异常相当于 ThrowingAdvice。在使用时可指定 pointcut / value 和 throwing 属性其中 pointcut / value 用于指定切入点表达式而 throwing 属性值用于指定- 一个形参名来表示 Advice 方法中可定义与此同名的形参该形参可用于访问目标方法抛出的异常。After用于定于最终 final 通知不管是否异常该通知都会执行。使用时需要指定一个 value 属性该属性用于指定该通知被织入的切入点。DeclareParents用于定义引介通知相当于 IntroductionInterceptor。 2.4. AOP 使用问题小结 切入点pointcut的申明规则 ​ Spring AOP 用户可能会经常使用 execution 切入点指示符。执行表达式的格式如下 execution (modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)ret-type-pattern 返回类型模式name-pattern 名字模式和 param-pattern 参数模式是必选的其它部分都是可选的。返回类型模式决定了方法的返回类型必须依次匹配一个连接点。你会使用的最频繁的返回类型模式是 *它代表了匹配任意的返回类型。declaring-type-pattern一个全限定的类型名称将只会匹配返回给定类型的方法。name-pattern 名字模式匹配就是方法名。你可以使用 * 通配符作为所有或者部分命名模式。param-pattern 参数模式稍微有点复杂() 匹配了一个不接受任意参数的方法而 (…) 匹配了一个接受任意数量参数的方法零或更多。模式 () 匹配了一个接受一个任意类型的参数的方法。模式 (,String) 匹配了一个接受两个参数的方法第一个可以是任意类型第二个则必须是 String 类型。 ​ 此外 Spring 支持如下三个逻辑运算符来组合切入点表达式 要求连接点同时匹配两个切入点表达式 ||要求连接点匹配任意个切入点表达式 !:要求连接点不匹配指定的切入点表达式多种增强通知的顺序 ​ 如果有多个通知想要在同一连接点运行会发生什么Spring AOP 遵循跟 AspectJ 一样的优先规则来确定通知执行的顺序。在 “进入” 连接点的情况下最高优先级的通知会先执行所以给定的两个前置通知中优先级高的那个会先执行。在 “退出” 连接点的情况下最高优先级的通知会最后执行。所以给定的两个后置通知中优先级高的那个会第二个执行。 ​ 当定义在不同的切面里的两个通知都需要在一个相同的连接点中运行那么除非你指定否则执行的顺序是未知的。你可以通过指定优先级来控制执行顺序。在标准的 Spring 方法中可以在切面类中实现 org.springframework.core.Ordered 接口或者用 Order 注解做到这一点。在两个切面中Ordered.getValue() 方法返回值或者注解值较低的那个有更高的优先级。 ​ 当定义在相同的切面里的两个通知都需要在一个相同的连接点中执行执行的顺序是未知的。 Spring AOP 和 AspectJ 之间的关键区别 ​ AspectJ 可以做 Spring AOP 干不了的事情它是 AOP 编程的完全解决方案Spring AOP 则致力于解决企业级开发中最普遍的 AOP方法织入。 ​ 下表总结了 Spring AOP 和 AspectJ 之间的关键区别 Spring AOPAspectJ在纯 Java 中实现使用 Java 编程语言的拓展实现不需要单独的编译过程除非设置 LTW否则需要 AspectJ 编译器ajc只能使用运行时织入运行时织入不可用。支持编译时、编译后和加载时织入功能强大-仅支持方法级编织更强大-可以编织字段、方法、构造函数、静态初始值设定项、最终类/方法等 …只能在 由 Spring 容器管理的 bean 上实现可以在所有域对象上实现仅支持方法执行切入点支持所有切入点代理是由目标对象创建的并且切面应用在这些代理上在执行应用程序之前在运行时前各方面之间在代码中进行织入比 AspectJ 慢多了更好的性能易于学习和应用相对于 Spring AOP 来说更复杂 Spring AOP 还是完全用 AspectJ ​ 以下 Spring 官方的回答总结来说就是 Spring AOP 更易用AspectJ 更强大。 Spring AOP 比完全使用 AspectJ 更加简单因为它不需要引入 AspectJ 的编译器 / 织入器到你开发和构建过程中。如果你仅仅需要在 Spring bean 上通知执行操作那么 Spring AOP 是合适的选择。如果你需要通知 domain 对象或其它没有在 Spring 容器中管理的任意对象那么你需要使用 AspectJ。如果你想通知除了简单的方法执行之外的连接点如调用连接点、字段 get 或 set 的连接点等等也需要使用 AspectJ。 ​ 当使用 AspectJ 时你可以选择使用 AspectJ 语言也称为 “代码风格” 或 AspectJ 注解风格。如果切面在你的设计中扮演一个很大的角色并且你能在 Eclipse 等 IDE 中使用 AspectJ Development ToolsAJDT那么首选 AspectJ 语言因为该语言专门被设计用来编写切面所以会更清晰、更简单。如果你没有使用 Eclipse 等 IDE或者在你应用中只有很少的切面并没有作为一个主要的角色你或许应该考虑使用 AspectJ 风格 并在你的 IDE 中附加一个普通的 Java 编译器并且在你构建脚本中增加切面织入链接的段落。 三、Spring 进阶 - IOC 体系结构设计 3.1 站在设计者的角度考虑设计 IOC 容器 如果让你设计一个 IoC 容器你会怎么设计我们初步通过这个问题来帮助我们更好的理解 IoC 的设计。 ​ 在设计时首先需要考虑的是 IOC 容器的功能输入和输出承接前面的文章我们初步画出 IOC 容器的整体功能。 ​ 在此基础上我们初步的去思考如果作为一个 IOC 容器的设计者主体上应该包含哪几个部分 加载 Bean 的配置比如 xml 配置 比如不同类型资源的加载解析生成统一 Bean 的定义 根据 Bean 的定义加载生成 Bean 的实例并放置在 Bean 容器中 比如 Bean 的依赖注入Bean 的嵌套Bean 的存放缓存等 除了基础 Bean 外还有常规针对企业级业务的特殊 Bean 比如国际化 Message事件 Event 等生成特殊的类结构去支撑 对容器中的 Bean 提供统一的管理和调用 比如用工厂模式管理提供方法根据名称/类的类型等从容器中获取 Bean … 3.2 Spring IoC 的体系结构设计 下面看 Spring 设计者是如何设计 IoC 并解决这些问题的。 BeanFactory 和 BeanRegistryIOC 容器功能规范和 Bean 的注册 Spring Bean 的创建是典型的工厂模式这一系列的 Bean 工厂也即 IOC 容器为开发者管理对象间的依赖关系提供了很多便利和基础服务在 Spring 中有许多的 IOC 容器的实现供用户选择和使用这是 IOC 容器的基础在顶层的结构设计主要围绕着 BeanFactory 和 xxxRegistry 进行 BeanFactory工厂模式定义了 IOC 容器的基本功能规范BeanRegistry向 IOC 容器手工注册 BeanDefinition 对象的方法 ​ 关系如下 BeanFactory 定义了 IOC 容器基本功能规范 ​ BeanFactory 作为最顶层的一个接口类它定义了 IOC 容器的基本功能规范BeanFactory 有三个子类ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。我们看下 BeanFactory 接口 public interface BeanFactory {// 用于取消引用实例并将其与 FactoryBean 创建的 bean 区分开来。例如如果命名的 bean 是 FactoryBean则获取将返回 Factory而不是 Factory 返回的实例。String FACTORY_BEAN_PREFIX ;// 根据 bean 的名字和 Class 类型等来得到 bean 实例Object getBean(String name) throws BeansException;T T getBean(String name, ClassT requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;T T getBean(ClassT requiredType) throws BeansException;T T getBean(ClassT requiredType, Object... args) throws BeansException;// 返回指定 bean 的 ProviderT ObjectProviderT getBeanProvider(ClassT requiredType);T ObjectProviderT getBeanProvider(ResolvableType requiredType);// 检查工厂中是否包含给定 name 的 bean或者外部注册的 beanboolean containsBean(String name);// 检查所给 name 的类型与 type 是否为单例/原型boolean isSingleton(String name) throws NoSuchBeanDefinitionException;boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 判断所给 name 的类型与 type 是否匹配boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, Class? typeToMatch) throws NoSuchBeanDefinitionException;// 获取给定 name 的 bean 的类型NullableClass? getType(String name) throws NoSuchBeanDefinitionException;NullableClass? getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;// 返回给定 name 的 bean 的别名String[] getAliases(String var1); }BeanFactory 为何要定义这么多层次的接口定义了哪些接口 ​ 主要是为了区分 Spring 内部在操作过程中对象的传递和转换过程中对对象的数据访问所做的限制。 ​ 有哪些接口呢 ListableBeanFactory该接口定义了访问容量中 Bean 基本信息的若干方法如查看 Bean 的个数、获取某一类型 Bean 的配置名、查看容器中是否包含某一 Bean 等方法 HierarchicalBeanFactory父子级联 IoC 容器的接口子容器可以通过接口方法访问父容器 通过 HierarchicalBeanFactory 接口Spring 的 IoC 容器可以建议父子层级关联的容器体系子容器可以访问父容器中的 Bean但父容器不能访问子容器的 Bean。Spring 使用父子容器实现了很多功能比如在 Spring MVC 中展现层 Bean 位于一个子容器中而业务层和持久层的 Bean 位于父容器中。这样展现层 Bean 就可以引用业务层和持久层的 Bean而业务层和持久层的 Bean 则看不到展现层的 Bean。 ConfigurableBeanFactory是一个重要的接口增强了 IoC 容器的可定制性它定义了设置类装载器、属性编辑器、容量初始化后置处理器等方法 ConfigurableListableBeanFactoryListableBeanFactory 和 ConfigurableBeanFactory 的融合 AutowireCapableBeanFactory定义了将容量中的 Bean 按某种规则如按名字匹配、按类型匹配等进行自动装配的方法 如何将 Bean 注册到 BeanFactory 中BeanRegistry ​ Spring 配置文件中每一个 bean 节点元素在 Spring 容器里都通过一个 BeanDefinition 对象表示它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器中手工注册 BeanDefinition 对象的方法。 BeanDefinition各种 Bean 对象及其相互的关系 Bean 对象存在依赖嵌套等关系所以设计者设计了 BeanDefinition它用来对 Bean 对象及关系定义我们在理解时只需要抓住如下三个要点 BeanDefinition 定义了各种 Bean 对象及其相互的关系BeanDefinitionReader 这是 BeanDefinition 的解析器BeanDefinitionHolder 这是 BeanDefinition 的包装类用来存储 BeanDefinition、name 以及 aliases 等。 BeanDefinition ​ Spring IoC 容器管理了我们定义的各种 Bean 对象及其相互的关系Bean 对象在 Spring 实现中是以 BeanDefinition 来描述的其继承体系如下 BeanDefinitionReader ​ Bean 的解析过程非常复杂功能被分的很细因为这里需要被拓展的地方很多必须保证有足够的灵活性以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成 BeanDefinitionHolder ​ BeanDefinitionHolder 这是 BeanDefinition 的包装类用来存储 BeanDefinitionname 以及 aliases 等。 3.3 ApplicationContextIOC 接口设计和实现 IoC 容器的接口类是 ApplicationContext很显然它必然继承 BeanFactory 对 Bean 规范最基本的 IoC 容器的实现进行定义。而 ApplicationContext 表示的是应用的上下文除了对 Bean 的管理外还至少应该包含 访问资源对不同方式的 Bean 配置即资源进行加载。实现 ResourcePatternResolver 接口国际化支持信息源可以实现国际化。实现 MessageSource 接口应用事件支持应用事件。实现 ApplicationEventPublisher 接口 ApplicationContext 接口的设计 HierarchicalBeanFactory 和 ListableBeanFactoryApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口在此基础上还通过多个其它的接口拓展了 BeanFactory 的功能ApplicationEventPublisher让容器拥有发布应用上下文事件的功能包括容器启动事件、关闭事件等。实现了 ApplicationListener 事件监听接口的 Bean 可以接收的到容器事件并对事件进行响应处理。在 ApplicationContext 抽象实现类 AbstractApplicationContext 中我们可以发现存在一个 ApplicationEventMulticaster它负责保存所有监听器以便在容器产生上下文事件时通知这些事件监听者。MessageSource为应用提供 i18n 国际化消息访问的功能ResourcePatternResolver所有 ApplicationContext 实现类都实现了类似于 PathMatchingResourcePatternResolver 的功能可以通过带前缀的 Ant 风格的资源文件路径装载 Spring 的配置文件。LiftCycle该接口是 Spring2.0 加入的该接口提供了 start() 和 stop() 两个方法主要用于控制异步处理过程。在具体使用时该接口同时被 ApplicationContext 实现及具体 Bean 实现ApplicationContext 会将 start/stop 的消息传递给容器中所有实现了该接口的 Bean以达到管理和控制 JMX、任务调度等目的。 ApplicationContext 接口的实现 ​ 在考虑 ApplicationContext 接口的实现时关键的点在于不同 Bean 的配置方式比如 xmlgroovyannotation 等有着不同的资源加载方式这便衍生了众多 ApplicationContext 的实现类。 第一从类结构设计上看围绕着是否需要 Refresh 容器衍生出两个抽象类 GenericApplicationContext是初始化的时候就创建容器往后的每次 refresh 都不会更改AbstractRefreshableApplicationContextAbstractRefreshableApplicationContext 及子类的每次 refresh 都是先清除已有如果不存在就创建的容器然后再重新创建AbstractRefreshableApplicationContext 及子类无法做到 GeniricApplicationContext 混合搭配从不同源头获取 bean 的定义信息 第二从加载的源来看比如 xmlgroovyannotation 等衍生出许多类型的 ApplicationContext典型比如 FileSystemXmlApplicationContext从文件系统下的一个或多个 xml 配置文件中加载上下文定义也就是说系统盘符中加载 xml 配置文件。ClassPathXmlApplicationContext从类路径下的一个或多个 xml 配置文件中加载上下文定义适用于 xml 配置的方式。AnnotationConfigApplicationContext从一个或多个基于 java 的配置类中加载上下文定义适用于 java 注解的方式。ConfigurableApplicationContext拓展于 ApplicationContext它新增加了两个主要的方法refresh() 和 close()让 ApplicationContext 具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用 refresh() 即可启动应用上下文在已启动的状态下调用 refresh() 则清除缓存并重新装载配置信息而调用 close() 则可关闭应用上下文。这些接口方法为容器的控制管理带来了便利但作为开发者我们并不需要关心这些方法。 第三更进一步理解 设计者在设计时 AnnotationConfigApplicationContext 为什么是继承 GenericApplicationContext因为基于注解的配置是不太会被运行时修改的这意味着不需要进行动态 Bean 配置和刷新容器所以只需要 GenericApplicationContext。 而基于 XML 这种配置文件这种文件是容易修改的需要动态性刷新 Bean 的支持所以 XML 相关的配置必然继承 AbstractRefreshableApplicationContext且存在多种 xml 的加载方式位置不同设计所以必然会设计出 AbstractXmlApplicationContext其中包含对 XML 配置解析成 BeanDefiniation 的过程。 我们将之前的设计要点和设计结构结合起来看 四、Spring 进阶 - IOC 初始化流程 4.1 引入 上一节我们了解了 IOC 的设计要点和设计结构下面我们可以看一下源码实现了Spring 如何实现将资源配置以 xml 配置为例通过加载解析生成 BeanDefinitation 并注册到 IoC 容器中。 4.2 如何将 Bean 从 XML 配置中解析后放到 IoC 容器中的 本节目标就是分析 Spring 如何实现将资源配置以 xml 配置为例通过加载解析生成 BeanDefinitation 并注册到 IoC 容器中。 初始化的入口 ​ 对于 xml 配置的 Spring 应用在 main() 方法中实例化 ClassPathXmlApplicationContext 即可创建一个 IoC 容器。我们从这个构造方法入手开始探究 IoC 容器的初始化过程。 ClassPathXmlApplicationContext context new ClassPathXmlApplicationContext(daos.xml);// ClassPathXmlApplicationContext.class public ClassPathXmlApplicationContext(String configLocation) throws BeansException {this(new String[]{configLocation}, true, (ApplicationContext)null); }public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, Nullable ApplicationContext parent) throws BeansException {// 设置 Bean 资源加载器super(parent);// 设置配置路径this.setConfigLocations(configLocations);// 初始化容器if (refresh) {this.refresh();}}设置资源解析器和环境 ​ 调用父类容器 AbstractApplicationContext 的构造方法super(parent) 方法为容器设置好 Bean 资源加载器。 public AbstractApplicationContext(Nullable ApplicationContext parent) {// 默认构造函数初始化容器idname状态 以及资源解析器this();// 将父容器的 Environment 合并到当前容器this.setParent(parent); }​ 通过 AbstractApplicationContext 默认构造函数初始化容器 idname状态以及资源解析器。 public AbstractApplicationContext() {this.logger LogFactory.getLog(this.getClass());this.id ObjectUtils.identityToString(this);this.displayName ObjectUtils.identityToString(this);this.beanFactoryPostProcessors new ArrayList();this.active new AtomicBoolean();this.closed new AtomicBoolean();this.startupShutdownMonitor new Object();this.applicationStartup ApplicationStartup.DEFAULT;this.applicationListeners new LinkedHashSet();this.resourcePatternResolver this.getResourcePatternResolver(); } // Spring 资源加载器 protected ResourcePatternResolver getResourcePatternResolver() {return new PathMatchingResourcePatternResolver(this); }​ 通过 AbstractApplicationContext 的 setParent(parent) 方法将父容器的 Environment 合并到当前容器。 public void setParent(Nullable ApplicationContext parent) {this.parent parent;if (parent ! null) {Environment parentEnvironment parent.getEnvironment();if (parentEnvironment instanceof ConfigurableEnvironment) {this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);}}}设置配置路径 ​ 在设置容器的资源加载器之后接下来 FileSystemXmlApplicationContext 执行 setConfigLocations 方法通过调用其父类 AbstractRefreshableConfigApplicationContext 的方法进行对 Bean 定义文件的定位。 public void setConfigLocations(Nullable String... locations) {if (locations ! null) {Assert.noNullElements(locations, Config locations must not be null);this.configLocations new String[locations.length];for(int i 0; i locations.length; i) {// 解析配置路径this.configLocations[i] this.resolvePath(locations[i]).trim();}} else {this.configLocations null;}}protected String resolvePath(String path) {// 从上一步 Environment 中解析return this.getEnvironment().resolveRequiredPlaceholders(path); }初始化的主体流程 ​ Spring IoC 容器对 Bean 定义资源的载入是从 refresh() 函数开始的refresh() 是一个模板方法refresh() 方法的作用是在创建 IoC 容器前如果已有容器存在则需要把已有的容器销毁和关闭以保证在 refresh 之后使用的是新建立起来的 IoC 容器。refresh 的作用类似于对 IoC 容器的重启在新建立好的容器中对容器进行初始化对 Bean 定义资源进行载入。 public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh this.applicationStartup.start(spring.context.refresh);// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess this.applicationStartup.start(spring.context.beans.post-process);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);beanPostProcess.end();// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn(Exception encountered during context initialization - cancelling refresh attempt: ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset active flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Springs core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();contextRefresh.end();}} }​ 这里在设计上是一个非常典型的资源类加载处理型的思路 模板方法设计模式模板方法中使用典型的钩子方法将具体的初始化加载方式插入到钩子方法之间将初始化的阶段封装用来记录当前初始化到什么阶段常见的设计是 xxxPhase/xxxStage资源加载初始化有失败等处理必然是 try/catch/finally … 初始化 BeanFactory 之 obtainFreshBeanFactory ​ AbstractApplicationContext 的 obtainFreshBeanFactory() 方法调用子类容器的 refreshBeanFactory() 方法启动容器载入 Bean 定义资源文件的过程代码如下 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {// 这里使用了委派的设计模式父类定义了抽象的 refreshBeanFactory() 方法具体实现调用子类容器的 refreshBeanFactory() 方法this.refreshBeanFactory();return this.getBeanFactory(); }​ AbstractApplicationContext 类中只抽象定义了 refreshBeanFactory() 方法容器真正调用的是其子类 AbstractRefreshableApplicationContext 实现的 refreshBeanFactory() 方法在创建 IoC 容器前如果已有容器存在则需要把已有的容器销毁和关闭以保证在 refresh 之后使用的是新建立起来的 IoC 容器。方法源码如下 protected final void refreshBeanFactory() throws BeansException {// 如果已有容器存在则需要把已有的容器销毁和关闭以保证在 refresh 之后使用的是新建立起来的 IoC 容器if (this.hasBeanFactory()) {this.destroyBeans();this.closeBeanFactory();}try {// 创建 DefaultListableBeanFactory并调用 loadBeanDefinitions(beanFactory) 装载 bean 定义DefaultListableBeanFactory beanFactory this.createBeanFactory();beanFactory.setSerializationId(this.getId());this.customizeBeanFactory(beanFactory); // 对 IoC 容器进行定制化如设置启动参数开启注解的自动装配等this.loadBeanDefinitions(beanFactory); // 调用载入 Bean 定义的方法主要这里又使用了一个委派模式在当前类中只定义了抽象的 loadBeanDefinitions 方法具体的实现调用子类容器。this.beanFactory beanFactory;} catch (IOException ex) {throw new ApplicationContextException(I/O error parsing bean definition source for this.getDisplayName(), ex);} }初始化 BeanFactory 之 loadBeanDefinitions ​ AbstractRefreshableApplicationContext 中只定义了抽象的 loadBeanDefinitions 方法容器真正调用的是其子类 AbstractXmlApplicationContext 对该方法的实现AbstractXmlApplicationContext 的主要源码如下 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// 创建 XmlBeanDefinitionReader即创建 Bean 读取器并通过回调设置到容器中去容器使用该读取器读取 Bean 定义资源XmlBeanDefinitionReader beanDefinitionReader new XmlBeanDefinitionReader(beanFactory);// 配置上下文的环境资源加载器、解析器beanDefinitionReader.setEnvironment(this.getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 为 Bean 读取器设置 SAX xml 解析器// 允许子类自行初始化比如校验机制并提供真正的加载方法this.initBeanDefinitionReader(beanDefinitionReader); // 当 Bean 读取器读取 Bean 定义的 Xml 资源文件时启用 Xml 的校验机制this.loadBeanDefinitions(beanDefinitionReader); }protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {// 加载 XML 配置方式里的 Bean 定义的资源Resource[] configResources this.getConfigResources();if (configResources ! null) {reader.loadBeanDefinitions(configResources);}// 加载构造函数里配置的 Bean 配置文件即 {“daos.xml}String[] configLocations this.getConfigLocations();if (configLocations ! null) {reader.loadBeanDefinitions(configLocations);}}​ Xml Bean 读取器XmlBeanDefinitionReader调用其父类 AbstractBeanDefinitionReader 的 reader.loadBeanDefinitions() 方法读取 Bean 定义资源。 ​ 由于我们使用 ClassPathXmlApplicationContext 作为例子分析因此 getConfigResources 返回值为 null因此程序执行 reader.loadBeanDefinitions(configLocations) 分支。 AbstractBeanDefinitionReader 读取 Bean 定义资源 ​ AbstractBeanDefinitionReader 的 loadBeanDefinitions 方法源码如下 public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {return this.loadBeanDefinitions(location, (Set)null); }public int loadBeanDefinitions(String location, Nullable SetResource actualResources) throws BeanDefinitionStoreException {ResourceLoader resourceLoader this.getResourceLoader();if (resourceLoader null) {throw new BeanDefinitionStoreException(Cannot load bean definitions from location [ location ]: no ResourceLoader available);} else if (resourceLoader instanceof ResourcePatternResolver) { // 模式匹配类型的解析器这种方式是加载多个满足匹配条件的资源try {// 获取到要加载的资源Resource[] resources ((ResourcePatternResolver)resourceLoader).getResources(location);int count this.loadBeanDefinitions(resources); // 委派调用其子类 XmlBeanDefinitionReader 的方法实现加载功能if (actualResources ! null) {Collections.addAll(actualResources, resources);}if (this.logger.isTraceEnabled()) {this.logger.trace(Loaded count bean definitions from location pattern [ location ]);}return count;} catch (IOException ex) {throw new BeanDefinitionStoreException(Could not resolve bean definition resource pattern [ location ], ex);}} else {// 只能通过绝对路径 URL 加载单个资源Resource resource resourceLoader.getResource(location);int count this.loadBeanDefinitions((Resource)resource);if (actualResources ! null) {actualResources.add(resource);}if (this.logger.isTraceEnabled()) {this.logger.trace(Loaded count bean definitions from location [ location ]);}return count;} }​ 从对 AbstractBeanDefinitionReader 的 loadBeanDefinitions 方法源码分析可以看出该方法做了以下两件事 首先调用资源加载器的获取资源方法 resourceLoader.getResource(location)获取到要加载的资源。其次真正执行加载的是其子类 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法。 XmlBeanDefinitionReader 加载 Bean 定义资源 ​ 继续看子类 XmlBeanDefinitionReader 的 loadBeanDefinitions(Resource …) 方法看到代表 bean 文件的资源定义以后的载入过程。 /*** 本质上是加载 Xml 配置的 Bean*/ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {try {Document doc this.doLoadDocument(inputSource, resource); // 将 Bean 定义资源转换为 Document 对象int count this.registerBeanDefinitions(doc, resource);if (this.logger.isDebugEnabled()) {this.logger.debug(Loaded count bean definitions from resource);}return count;} catch (BeanDefinitionStoreException ex) {throw ex;} catch (SAXParseException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(), Line ex.getLineNumber() in XML document from resource is invalid, ex);} catch (SAXException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(), XML document from resource is invalid, ex);} catch (ParserConfigurationException ex) {throw new BeanDefinitionStoreException(resource.getDescription(), Parser configuration exception parsing XML from resource, ex);} catch (IOException ex) {throw new BeanDefinitionStoreException(resource.getDescription(), IOException parsing XML document from resource, ex);} catch (Throwable ex) {throw new BeanDefinitionStoreException(resource.getDescription(), Unexpected exception parsing XML document from resource, ex);} }通过源码分析载入 Bean 定义资源文件的最后一步是将 Bean 定义资源转换为 Document 对象该过程由 documentLoader 实现。DocumentLoader 将 Bean 定义资源转换为 Document 对象 ​ DocumentLoader 将 Bean 定义资源转换成 Document 对象的源码如下 /*** 使用标准的 JAXP 将载入的 Bean 定义资源转换成 Document 对象*/ public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {// 创建文件解析器工厂DocumentBuilderFactory factory this.createDocumentBuilderFactory(validationMode, namespaceAware);if (logger.isTraceEnabled()) {logger.trace(Using JAXP provider [ factory.getClass().getName() ]);}// 创建文档解析器DocumentBuilder builder this.createDocumentBuilder(factory, entityResolver, errorHandler);return builder.parse(inputSource); // 解析 }protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware) throws ParserConfigurationException {DocumentBuilderFactory factory DocumentBuilderFactory.newInstance();factory.setNamespaceAware(namespaceAware);// 设置解析 XML 的校验if (validationMode ! 0) {factory.setValidating(true);if (validationMode 3) {factory.setNamespaceAware(true);try {factory.setAttribute(http://java.sun.com/xml/jaxp/properties/schemaLanguage, http://www.w3.org/2001/XMLSchema);} catch (IllegalArgumentException ex) {ParserConfigurationException pcex new ParserConfigurationException(Unable to validate using XSD: Your JAXP provider [ factory ] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? Upgrade to Apache Xerces (or Java 1.5) for full XSD support.);pcex.initCause(ex);throw pcex;}}}return factory; } ​ 该解析过程调用 JavaEE 标准的 JAXP 标准进行处理。 ​ 至此 Spring IoC 容器根据定位的 Bean 定义资源文件将其加载读入并转换成 Document 对象过程完成。 ​ 接下来我们要继续分析 Spring IoC 容器将载入的 Bean 定义资源文件转换为 Document 对象之后是如何将其解析为 Spring IoC 管理的 Bean 对象并将其注册到容器中的。 XmlBeanDefinitionReader 解析载入的 Bean 定义资源文件 ​ XmlBeanDefinitionReader 类中的 doLoadBeanDefinitions 方法是从特定 XML 文件中实际载入 Bean 定义资源的方法该方法在载入 Bean 定义资源之后将其转换成 Document 对象接下来调用 registerBeanDefinitions 启动 Spring IoC 容器对 Bean 定义的解析过程regisiterBeanDefinitions 方法源码如下 /*** 按照 Spring 的 Bean 语义要求将 Bean 定义资源解析并转换为容器内部数据结构*/ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {BeanDefinitionDocumentReader documentReader this.createBeanDefinitionDocumentReader();int countBefore this.getRegistry().getBeanDefinitionCount();// 解析过程入口这里使用了委派模式具体的解析实现过程由实现类 DefaultBeanDefinitionDocumentReader 完成documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));return this.getRegistry().getBeanDefinitionCount() - countBefore; // 返回此次解析了多少个对象 }/*** 创建 BeanDefinitionDocumentReader 对象解析 Document 对象*/ protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {return (BeanDefinitionDocumentReader)BeanUtils.instantiateClass(this.documentReaderClass); }public XmlReaderContext createReaderContext(Resource resource) {return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, this.getNamespaceHandlerResolver()); }​ Bean 定义资源的载入解析分为以下两个过程 首先通过调用 XML 解析器将 Bean 定义资源文件转换得到 Document 对象但是这些 Document 对象并没有按照 Spring 的 Bean 规则进行解析。这一步是载入的过程。其次在完成通用的 XML 解析之后按照 Spring 的 Bean 规则对 Document 对象进行解析。 ​ 按照 Spring 的 Bean 规则对 Document 对象解析的过程是在接口 BeanDefinitionDocumentReader 的实现类 DefaultBeanDefinitionDocumentReader 中实现的。 DefaultBeanDefinitionDocumentReader 对 Bean 定义的 Document 对象解析 ​ BeanDefinitionDocumentReader 接口通过 regisiterBeanDefinitions 方法调用其实现类 DefaultBeanDefinitionDocumentReader 对 Document 对象进行解析解析的代码如下 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext readerContext;this.doRegisterBeanDefinitions(doc.getDocumentElement()); } /*** 注册 beans / 配置的 Beans*/ protected void doRegisterBeanDefinitions(Element root) {BeanDefinitionParserDelegate parent this.delegate;this.delegate this.createDelegate(this.getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {String profileSpec root.getAttribute(profile);if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles StringUtils.tokenizeToStringArray(profileSpec, ,; );if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (this.logger.isDebugEnabled()) {this.logger.debug(Skipped XML bean definition file due to specified profiles [ profileSpec ] not matching: this.getReaderContext().getResource());}return;}}}this.preProcessXml(root);this.parseBeanDefinitions(root, this.delegate); // 从 Document 的根元素开始进行 Bean 定义的 Document 对象this.postProcessXml(root);this.delegate parent; }BeanDefinitionParserDelegate 解析 Bean 定义资源文件生成 BeanDefinition protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {if (delegate.isDefaultNamespace(root)) {NodeList nl root.getChildNodes();for(int i 0; i nl.getLength(); i) {Node node nl.item(i);if (node instanceof Element) {Element ele (Element)node;if (delegate.isDefaultNamespace(ele)) {this.parseDefaultElement(ele, delegate);} else {delegate.parseCustomElement(ele);}}}} else {delegate.parseCustomElement(root);}}private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {// 如果元素节点是Import 导入元素进行导入解析if (delegate.nodeNameEquals(ele, import)) {this.importBeanDefinitionResource(ele);} else if (delegate.nodeNameEquals(ele, alias)) { // 如果元素节点是 Alias 别名元素进行别名解析this.processAliasRegistration(ele);} else if (delegate.nodeNameEquals(ele, bean)) { // 如果元素节点是 Bean 元素按照 Spring 的 Bean 规则解析元素this.processBeanDefinition(ele, delegate);} else if (delegate.nodeNameEquals(ele, beans)) { // 如果元素节点 Beans 元素即它是嵌套类型的// 递归解析this.doRegisterBeanDefinitions(ele);}}​ 解析 Bean 生成 BeanDefinitionHolder 的方法 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {BeanDefinitionHolder bdHolder delegate.parseBeanDefinitionElement(ele);if (bdHolder ! null) {bdHolder delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// 注册最终的装饰实例BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());} catch (BeanDefinitionStoreException ex) {this.getReaderContext().error(Failed to register bean definition with name bdHolder.getBeanName() , ele, ex);}this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}解析过后的 BeanDefinition 在 IoC 容器中的注册 ​ Document 对象的解析后得到封装 BeanDefinition 的 BeanDefinitionHold 对象然后调用 BeanDefinitionReaderUtils 的 registerBeanDefinition 方法向 IoC 容器注册解析的 BeanBeanDefinitionReaderUtils 的源码如下 /*** 通过 BeanDefinitionRegistry 将 BeanDefinitionHolder 注册到 BeanFactory*/ public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {String beanName definitionHolder.getBeanName();registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());String[] aliases definitionHolder.getAliases();if (aliases ! null) {for(String alias : aliases) {registry.registerAlias(beanName, alias);}}}​ 当调用 BeanDefinitionReaderUtils 向 IoC 容器注册解析的 BeanDefinition 时真正完成注册功能的是 DefaultListableBeanFactory。 DefaultListableBeanFactory 向 IoC 容器注册解析后的 BeanDefinition ​ IOC 容器本质上就是一个 beanDefintionMap注册即将 BeanDefinition put 到 map 中。 /** Map of bean definition objects, keyed by bean name. */ private final MapString, BeanDefinition beanDefinitionMap new ConcurrentHashMap(256);/** Map from bean name to merged BeanDefinitionHolder. */ private final MapString, BeanDefinitionHolder mergedBeanDefinitionHolders new ConcurrentHashMap(256);public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {Assert.hasText(beanName, Bean name must not be empty);Assert.notNull(beanDefinition, BeanDefinition must not be null);if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition)beanDefinition).validate();} catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, Validation of bean definition failed, ex);}}BeanDefinition existingDefinition (BeanDefinition)this.beanDefinitionMap.get(beanName);// 如果已经注册if (existingDefinition ! null) {// 检查是否可以覆盖if (!this.isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}if (existingDefinition.getRole() beanDefinition.getRole()) {if (this.logger.isInfoEnabled()) {this.logger.info(Overriding user-defined bean definition for bean beanName with a framework-generated bean definition: replacing [ existingDefinition ] with [ beanDefinition ]);}} else if (!beanDefinition.equals(existingDefinition)) {if (this.logger.isDebugEnabled()) {this.logger.debug(Overriding bean definition for bean beanName with a different definition: replacing [ existingDefinition ] with [ beanDefinition ]);}} else if (this.logger.isTraceEnabled()) {this.logger.trace(Overriding bean definition for bean beanName with an equivalent definition: replacing [ existingDefinition ] with [ beanDefinition ]);}// 覆盖this.beanDefinitionMap.put(beanName, beanDefinition);} else {if (this.hasBeanCreationStarted()) {synchronized(this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);ListString updatedDefinitions new ArrayList(this.beanDefinitionNames.size() 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames updatedDefinitions;this.removeManualSingletonName(beanName);}} else {this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);this.removeManualSingletonName(beanName);}// 重置所有已经注册过的 BeanDefinition 的缓存this.frozenBeanDefinitionNames null;}if (existingDefinition null !this.containsSingleton(beanName)) {if (this.isConfigurationFrozen()) {this.clearByTypeCache();}} else {this.resetBeanDefinition(beanName);}}​ 至此Bean 定义资源文件中配置的 Bean 被解析过后已经注册到 IoC 容器中被容器管理起来真正完成了 IoC 容器初始化所作的全部工作。现在 IoC 容器中已经建立了整个 Bean 的配置信息这些 BeanDefinition 信息已经可以使用并且可以被检索IoC 容器的作用就是对这些注册的 Bean 定义信息进行处理和维护。这些注册的 Bean 定义信息是 IoC 容器控制反转的基础正是有了这些注册的数据容器才可以进行依赖注入。 4.3 总结 ​ 现在通过上面的代码对 IOC 容器初始化的基本步骤进行总结。 初始化的入口在容器实现中的 refresh() 调用来实现对 bean 定义载入 IoC 容器使用的方法是 loadBeanDefinition其中大致的过程如下 通过 ResourceLoader 来完成资源文件位置的定位DefaultResourceLoader 是默认的实现同时上下文本身就给出了 ResourceLoader 的实现可以从类路径、文件系统、URL 等方式来定位资源位置。如果是 XmlBeanFactory 作为 IoC 容器那么需要为它指定 bean 定义的资源也就是说 bean 定义文件时通过抽象成 Resource 来被 IoC 容器处理的。通过 BeanDefinitionReader 来完成定义信息的解析和 Bean 信息的注册往往使用的是 XmlBeanDefinitionReader 来解析 bean 的 xml 定义文件 - 实际的处理过程是委托给 BeanDefinitionParserDelegate 来完成的从而得到 bean 的定义信息这些信息在 Spring 中使用 BeanDefinition 对象来表示 - 这个名字可以让我们想到 loadBeanDefinitionRegisterBeanDefinition 这些相关方法 - 它们都是为处理 BeanDefinition 服务的。容器解析得到 BeanDefinition 后需要把它在 IoC 容器中注册这由 IoC 实现 BeanDefinitionRegister 接口来实现。注册过程就是在 IoC 容器内部维护的一个 HashMap 来保存得到的 BeanDefinition 的过程这个 HashMap 是 IoC 容器持有 bean 信息的场所以后对 bean 的操作都是围绕这个 HashMap 来实现的。 然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 Spring IOC 的服务了在使用 IOC 容器的时候我们注意到除了少量粘合代码绝大多数以正确 IoC 风格编写的应用程序代码完全不用关心如何到达工厂因为容器将把这些对象与容器管理的其它对象钩链在一起。基本的策略是把工厂放到已知的地方最好是放在对预期使用的上下文有意义的地方以及代码将实际需要访问工厂的地方。Spring 本身提供了对声明式载入 web 应用程序用法的应用程序上下文并将其存储在 ServletContext 中的框架实现。 五、Spring 进阶 - Bean 实例化声明周期、循环依赖等 5.1 引入 我们了解过了 IOC 设计要点和设计结构以及 Spring 如何实现将资源配置以 XML 配置为例通过加载解析生成 BeanDefinition 并注册到 IoC 容器中的容器中存放的 Bean 的定义即 BeanDefinition 放到 beanDefinitionMap 中本质上是一个 ConcurrentHashMapString, object并且 BeanDefinition 接口中包含了这个类的 Class 信息以及是否是单例等。那么如何从 BeanDefinition 中实例化 Bean 对象呢 ​ 本文主要研究如何从 IOC 容器已有的 BeanDefinition 信息实例化出 Bean 对象这里还会包含三块重点内容 BeanFactory 中 getBean 的主体思路Spring 如何解决循环依赖问题Spring 中 Bean 的生命周期 5.2 BeanFactory 中 getBean 的主体思路 第四章第二节中我们知道 BeanFactory 定义了 Bean 容器的规范其中包含根据 bean 的名字Class 类型和参数等来得到 bean 实例。 // 根据 bean 的名字和 Class 类型等来得到 bean 实例 Object getBean(String name) throws BeansException; T T getBean(String name, ClassT requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; T T getBean(ClassT requiredType) throws BeansException; T T getBean(ClassT requiredType, Object... args) throws BeansException;初步的思考 ​ 上文中我们已经分析了 IoC 初始化的流程最终将 Bean 的定义即 BeanDefinition 放到 beanDefinitionMap 中本质上是一个 ConcurrentHashMapString, Object并且 BeanDefinition 接口中包含了这个类的 Class 信息以及是否是单例等 ​ 这样我们初步有了实现 Object getBean(String name) 这个方法是思路 从 beanDefinitionMap 通过 beanName 获得 BeanDefinition从 BeanDefinition 中获得 beanClassName通过反射初始化 beanClassName 的实例 instance 构造函数从 BeanDefinition 的 getConstructorArgumentValues() 方法获取属性值从 BeanDefinition 的 getPropertyValues() 方法获取 返回 beanName 的实例 instance ​ 由于 BeanDefinition 还有单例的信息如果是无参构造函数的实例还可以放在一个缓存中这样下次获取这个单例的实例时只需要从缓存中获取如果获取不到再通过上述步骤获取。 Spring 中 getBean 的主体思路 ​ BeanFactory 实现 getBean 方法在 AbstractBeanFactory 中这个方法重载都是调用 doGetBean 方法进行实现的 public Object getBean(String name) throws BeansException {return this.doGetBean(name, (Class)null, (Object[])null, false); }public T T getBean(String name, ClassT requiredType) throws BeansException {return (T)this.doGetBean(name, requiredType, (Object[])null, false); }public Object getBean(String name, Object... args) throws BeansException {return this.doGetBean(name, (Class)null, args, false); }public T T getBean(String name, Nullable ClassT requiredType, Nullable Object... args) throws BeansException {return (T)this.doGetBean(name, requiredType, args, false); }​ 下面来看一下 doGetBean 方法这个方法较长我们主要看它的整体思路和设计要点 /*** 参数 typeCheckOnlybean 实例是否包含一个类型检查*/ protected T T doGetBean(String name, Nullable ClassT requiredType, Nullable Object[] args, boolean typeCheckOnly) throws BeansException {// 解析 bean 的真正 name如果 bean 是工厂类name 前缀会加 %需要去掉String beanName this.transformedBeanName(name);Object sharedInstance this.getSingleton(beanName);Object beanInstance;if (sharedInstance ! null args null) {if (this.logger.isTraceEnabled()) {if (this.isSingletonCurrentlyInCreation(beanName)) {this.logger.trace(Returning eagerly cached instance of singleton bean beanName that is not fully initialized yet - a consequence of a circular reference);} else {this.logger.trace(Returning cached instance of singleton bean beanName );}}// 无参单例从缓存中获取beanInstance this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);} else {// 如果 bean 实例还在创建中则直接抛出异常if (this.isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// 如果 bean definition 存在于父的 bean 工厂中委派给父 Bean 工厂获取BeanFactory parentBeanFactory this.getParentBeanFactory();if (parentBeanFactory ! null !this.containsBeanDefinition(beanName)) {String nameToLookup this.originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory) {return (T)((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}if (args ! null) {return (T)parentBeanFactory.getBean(nameToLookup, args);}if (requiredType ! null) {return (T)parentBeanFactory.getBean(nameToLookup, requiredType);}return (T)parentBeanFactory.getBean(nameToLookup);}if (!typeCheckOnly) {// 将当前 bean 实例放入 alreadyCreated 集合中标识这个 bean 准备创建this.markBeanAsCreated(beanName);}StartupStep beanCreation this.applicationStartup.start(spring.beans.instantiate).tag(beanName, name);try {if (requiredType ! null) {beanCreation.tag(beanType, requiredType::toString);}RootBeanDefinition mbd this.getMergedLocalBeanDefinition(beanName);this.checkMergedBeanDefinition(mbd, beanName, args);// 确保它的依赖已被初始化String[] dependsOn mbd.getDependsOn();if (dependsOn ! null) {for(String dep : dependsOn) {if (this.isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, Circular depends-on relationship between beanName and dep );}this.registerDependentBean(dep, beanName);try {// 初始化它依赖的 Beanthis.getBean(dep);} catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, beanName depends on missing bean dep , ex);}}}// 创建 Bean 实例单例if (mbd.isSingleton()) {sharedInstance this.getSingleton(beanName, () - {try {// 真正创建 bean 的方法return this.createBean(beanName, mbd, args);} catch (BeansException ex) {this.destroySingleton(beanName);throw ex;}});beanInstance this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);} else if (mbd.isPrototype()) { // 创建 Bean 实例原型Object prototypeInstance null;try {this.beforePrototypeCreation(beanName);prototypeInstance this.createBean(beanName, mbd, args);} finally {this.afterPrototypeCreation(beanName);}beanInstance this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);} else { // 创建 Bean 实例根据 bean 的 scope 创建String scopeName mbd.getScope();if (!StringUtils.hasLength(scopeName)) {throw new IllegalStateException(No scope name defined for bean beanName );}Scope scope (Scope)this.scopes.get(scopeName);if (scope null) {throw new IllegalStateException(No Scope registered for scope name scopeName );}try {Object scopedInstance scope.get(beanName, () - {this.beforePrototypeCreation(beanName);Object var4;try {var4 this.createBean(beanName, mbd, args);} finally {this.afterPrototypeCreation(beanName);}return var4;});beanInstance this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);} catch (IllegalStateException ex) {throw new ScopeNotActiveException(beanName, scopeName, ex);}}} catch (BeansException ex) {beanCreation.tag(exception, ex.getClass().toString());beanCreation.tag(message, String.valueOf(ex.getMessage()));this.cleanupAfterBeanCreationFailure(beanName);throw ex;} finally {beanCreation.end();}}return (T)this.adaptBeanInstance(name, beanInstance, requiredType); }​ 这段代码很长主要看部分方法即可。 解析 bean 的真正 name如果 bean 是工厂类name 前缀会加 %需要去掉无参单例先从缓存中尝试获取如果 bean 实例还在创建则直接抛出异常如果 bena definition 存在于父 bean 工厂中委派给父 Bean 工厂获取标记这个 beanName 的实例正在创建确保它的依赖也被初始化真正创建 单例时原型时根据 bean 的 scope 创建 5.3 重点Spring 如何解决循环依赖问题 首先我们需要说明Spring 只是解决了单例模式下属性依赖的循环问题Spring 为了解决单例的循环依赖问题使用了三级缓存。 Spring 单例模式下的属性依赖 ​ 先来看一下这三级缓存 private final MapString, Object singletonObjects new ConcurrentHashMap(256); private final MapString, ObjectFactory? singletonFactories new HashMap(16); private final MapString, Object earlySingletonObjects new ConcurrentHashMap(16);第一层缓存singletonObjects单例对象缓存池已经实例化并且属性赋值这里的对象是成熟对象第二层缓存earlySingletonObjects单例对象缓存池已经实例化但尚未属性赋值这里的对象是半成品对象第三次对象singletonFactories单例工厂的缓存 ​ 如下是获取单例中 protected Object getSingleton(String beanName, boolean allowEarlyReference) {// Spring 首先从 singletonObjects一级缓存中尝试获取Object singletonObject this.singletonObjects.get(beanName);// 若是获取不到而且对象在建立中则尝试从 earlySingletonObjects二级缓存中获取if (singletonObject null this.isSingletonCurrentlyInCreation(beanName)) {singletonObject this.earlySingletonObjects.get(beanName);if (singletonObject null allowEarlyReference) {synchronized(this.singletonObjects) {singletonObject this.singletonObjects.get(beanName);if (singletonObject null) {singletonObject this.earlySingletonObjects.get(beanName);if (singletonObject null) {ObjectFactory? singletonFactory (ObjectFactory)this.singletonFactories.get(beanName);if (singletonFactory ! null) {// 若是仍是获取不到而且允许从 singletonFactories 经过 getObject 获取则经过 singletonFactories.getObejct()三级缓存获取singletonObject singletonFactory.getObject();// 若是获取到了则从 singletionObject 放入到 earlySingletonObjects也就是将三级缓存提高到二级缓存中this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject; }​ 补充一些方法和参数 isSingletonCurrentlyInCreation()判断当前单例 bean 是否正在建立中也就是未初始化完成allowEarlyReference是否容许从 singletonFactories 中经过 getObject 拿到对象 ​ 分析 getSingleton() 的整个过程Spring 首先从一级缓存 singletonObjects 中获取。若获取不到而且对象正在建立中就再从二级缓存 earlySingletonObjects 中获取。若仍获取不到且容许 singletonFactories 经过 getObject() 获取就从三级缓存 singletonFactory.getObject()三级缓存获取若是获取到了则从三级缓存移到了二级缓存。 ​ 从上面三级缓存的分析咱们能够知道Spring 解决循环依赖的诀窍就在于 singletonFactories 这个三级 cache。这个 cache 的类型是 ObjectFactory定义如下 public interface ObjectFactoryT {T getObject() throws BeansException; }​ 在 bean 建立过程中有两处比较重要的匿名内部类实现了该接口。一处是 Spring 利用其建立 bean 的时候另外一处就是 addSingletonFactory(beanName, new ObjectFactoryObject() {Override public Object getObject() throws BeansException {return getEarlyBeanReference(beanName, mbd, bean);}});​ 此处就是解决循环依赖的关键这段代码发生在 createBeanInstance 以后也就是说单例对象此时已经被建立出来了。这个对象已经被生产出来了。虽然还不完全尚未进行初始化的第二步和第三步可是已经能被人认出来了根据对象引用能够定位到堆中的对象因此 Spring 此时将这个对象提早曝光出来供我们使用。 ​ 比如A 对象 setter 依赖 B 对象B 对象 setter 依赖 A 对象A 首先完成了初始化的第一步而且将本身提早曝光到 singletonFactories 中此时进行初始化的第二步发现本身依赖对象 B此时就尝试去 get(B)发现 B 尚未被 create因此走 create 流程B 在初始化第一步的时候发现本身依赖了对象 A因而尝试 get(A)尝试一级缓存 singletonObjects(确定没有由于 A 还未初始化彻底)尝试二级缓存 earlySingletonObjects也没有尝试三级缓存 singletonFactories因为 A 经过 ObjectFactory 将本身提早曝光了因此 B 可以经过 ObjectFactory.getObject 拿到 A 对象半成品B 拿到 A 对象后顺利完成了初始化阶段 一、二、三彻底初始化以后将本身放入到一级缓存 singletonObjects 中。此时返回 A 中A 此时能拿到 B 的对象顺利完成本身的初始化阶段 二、三最终 A 也完成了初始化进入了一级缓存 singletonObjects 中并且更加幸运的是因为 B 拿到了 A 的对象引用因此 B 如今 hold 住的 A 对象完成了初始化。 Spring 为何不能解决非单例属性之外的依赖循环 通过以下几个问题辅助我们进一步理解。 Spring 为什么不能解决构造器的循环依赖 ​ 构造器注入形参的循环依赖也就是 bean B 需要在 beanA 的构造函数中完成初始化bean A 也需要在 bean B 的构造函数中完成初始化这种情况的结果就是两个 bean 都不能完成初始化循环依赖难以解决。 ​ Spring 解决循环依赖主要是依赖三级缓存但是在调用构造方法之前还未将其放入三级缓存之中因此后续的依赖调用构造方法的时候并不能从三级缓存种获取到依赖的 Bean因此不能解决。 Spring 为什么不能解决 prototype 作用域循环依赖 ​ 这种循环依赖同样无法解决因为 Spring 不会缓存 ‘prototype’ 作用域的 bean而 spring 中循环依赖的解决正是通过缓存来实现的。 Spring 为什么不能解决多例的循环依赖 ​ 多实例 Bean 是每次调用一次 getBean 都会执行一次构造方法并且给属性赋值根本没有三级缓存因此不能解决循环依赖。 那么其它循环依赖如何解决 那么实际开发中类似的依赖是如何解决 生产代理对象产生的循环依赖 ​ 这类循环依赖问题解决方法很多主要有 使用 Lazy 注解延迟加载使用 DependsOn 注解指定加载先后关系修改文件名称改变循环依赖类的加载顺序 使用 DependsOn 产生的循环依赖 ​ 这类循环依赖问题要找到 DependsOn 注解循环依赖的地方迫使它不循环依赖就可以解决问题。 多例循环依赖 ​ 这里循环依赖问题可以通过把 bean 改为单例的解决。 构造器循环依赖 ​ 这类循环依赖问题可以通过使用 Lazy 注解解决。 5.4 重点Spring 中 Bean 的生命周期 Spring 只帮我们管理单例模式 Bean 的完整生命周期对于 prototype 的 beanSpring 在创建好交给使用者之后则不会再管理后续的生命周期。 ​ Spring 容器可以管理 singleton 作用域 Bean 的生命周期在此作用域下Spring 能够精确地知道该 Bean 何时被创建何时初始化完成以及何时被销毁。 ​ 而对于 prototype 作用域的 BeanSpring 只负责创建当容器创建了 Bean 的实例后Bean 的实例就交给客户端代码管理Spring 容器将不再跟踪其生命周期。每次客户端请求 prototype 作用域的 Bean 时Spring 容器都会创建一个新的实例并且不会管那些被配置成 prototype 作用域的 Bean 的生命周期。 ​ 了解 Spring 生命周期的意义就在于可以利用 Bean 在其存活期间的指定时刻完成一些相关操作。这种时刻可能有很多但一般情况下会在 Bean 被初始化后和被销毁前执行一些相关操作。 Spring Bean 的生命周期流程 在 Spring 中Bean 的生命周期是一个很复杂的执行过程我们可以利用 Spring 提供的方法定制 Bean 的创建过程。 ​ Spring 容器中 Bean 的生命周期流程 如果 BeanFactoryPostProcessor 和 Bean 关联则调用 postProcessBeanFactory 方法。即首先尝试从 Bean 工厂中获取 Bean如果 InstantiationAwareBeanPostProcessor 和 Bean 关联则调用 postProcessBeforeInstantiation 方法。根据配置情况调用 Bean 构造方法实例化 Bean。利用依赖注入完成 Bean 中所有属性值的配置注入。如果 InstantiationAwareBeanPostProcessor 和 Bean 关联则调用 postProcessAfterInstantiation 方法和 postProcessProperties。调用 xxxAware 接口 第一类 Aware 接口 如果 Bean 实现了 BeanNameAware 接口则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。如果 Bean 实现了 BeanClassLoaderAware 接口则 Spring 调用 setBeanClassLoader() 方法传入 classLoader 的引用。如果 Bean 实现了 BeanFactoryAware 接口则 Spring 调用 setBeanFactory() 方法传入当前 ApplicationContext 实例的引用。… 第二类 Aware 接口 如果 Bean 实现了 EnvironmentAware 接口则 Spring 调用 setEnvironment() 方法传入当前 Environment 实例的引用。如果 Bean 实现了 EmbeddedValueResolverAware 接口则 SPring 调用 setEmbeddedValueResolver() 方法传入当前 StringValueResolver 实例的引用。如果 Bean 实现了 ApplicationContextAware 接口则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。… 如果 BeanPostProcessor 和 Bean 关联则 Spring 将调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作此处非常重要Spring 的 AOP 就是利用它实现的。如果 Bean 实现了 InitializingBean 接口则 Spring 调用 afterPropertiesSet() 方法。或者执行有 PostConstruct 注解的方法如果在配置文件中通过 init-method 属性指定了初始化方法则调用该初始化方法。如果 BeanPostProcessor 和 Bean 关联则 Spring 将调用该接口的初始化方法 postProcessBeforeInitialzation() 。此时Bean 已经可以被应用系统使用了。如果在 bean 中指定了该 Bean 的作用范围为 scope“singleton”则将该 Bean 放入 Spring IoC 的缓存池中将触发 Spring 对该 Bean 的生命周期管理如果在 bean 中指定了该 Bean 的作用范围为 scope“prototype”则将该 Bean 交给调用者调用者管理该 Bean 的生命周期Spring 不再管理该 Bean。如果 Bean 实现了 DisposableBean 接口则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁或者执行有 PreDestory 注解的方法如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法则 Spring 将调用该方法对 Bean 进行销毁。 Bean 的完整生命周期经历了各种方法调用这些方法可以划分为以下几类 Bean 自身的方法这个包括了 Bean 本身调用的方法和通过配置文件中 bean 的 init-method 和 destory-method 指定的方法Bean 级生命周期接口方法这个包括了 BeanNameAware、BeanFactoryAware、ApplicationContextAware当然也包括 InitializingBean 和 DiposableBean 这些接口的方法可以被 PostConstruct 和 PreDestroy 注解替代容器级生命周期接口方法这个包括了 InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现一般称它们的实现类为 “后处理器”。工厂后处理器接口方法这个包括了 AspectJWeavingEnablerConfigurationClassPostProcessorCustomAutowireConfigurer 等等非常有用的工厂后置处理器接口的方法。工厂后处理器也是容量级的。在应用上下文装配配置文件之后立即调用。 Spring Bean 生命周期案例 通过一个例子来验证上面的整个流程。 ​ 定义 Bean并让它实现相关接口 Slf4j ToString public class User implements BeanFactoryAware, BeanNameAware, ApplicationContextAware,InitializingBean, Destroyable {private String name;private int age;private BeanFactory beanFactory;private ApplicationContext applicationContext;private String beanName;public User() {log.info(execute User#new User());}public void setName(String name) {log.info(execute User#setUserName({}), name);this.name name;}public void setAge(int age) {log.info(execute User#setAge({}), age);this.age age;}Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {log.info(execute BeanFactoryAware#setBeanFactory);this.beanFactory beanFactory;}Overridepublic void setBeanName(String s) {log.info(execute BeanNameAware#setBeanName);this.beanName s;}Overridepublic void afterPropertiesSet() throws Exception {log.info(execute InitializingBean#afterPropertiesSet);}Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {log.info(execute ApplicationContextAware#setApplicationContext);this.applicationContext applicationContext;}Overridepublic void destroy() {log.info(execute Destroyable#destroy);}public void doInit() {log.info(execute User#doInit);}public void doDestroy() {log.info(execute User#doDestroy);} }​ 定义 BeanFactoryPostProcessor 的实现类 Slf4j Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {log.info(execute BeanFactoryPostProcessor#postProcessBeanFactory);} }​ 定义 InstantiationAwareBeanPostProcessor 的实现类 Slf4j Component public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {public Object postProcessBeforeInstantiation(Class? beanClass, String beanName) throws BeansException {log.info(execute InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation for {}, beanName);return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);}public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {log.info(execute InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation for {}, beanName);return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);}public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {log.info(execute InstantiationAwareBeanPostProcessor#postProcessProperties for {}, beanName);return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);} }​ 定义 BeanPostProcessor 的实现类 Slf4j Component public class MyBeanPostProcessor implements BeanPostProcessor {public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.info(execute BeanPostProcessor#postProcessBeforeInitialization for ({}), beanName);return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {log.info(execute BeanPostProcessor#postProcessAfterInitialization for ({}), beanName);return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);} }​ 通过 Java 配置方式初始化 Bean Configuration public class BeansConfig {Bean(name user, initMethod doInit, destroyMethod doDestroy)public User user() {User user new User();user.setName(Zhanbo);user.setAge(18);return user;} }​ 测试方法 Slf4j public class Test {public static void main(String[] args) {log.info(Init application context);AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(io.xinghuo.spring.instance);User user (User) context.getBean(user);log.info(user.toString());log.info(Shutdown application context);context.registerShutdownHook();} } ​ 输出去除无关输出 21:07:38 [main] INFO io.xinghuo.spring.instance.Test - Init application context ... 21:07:39 [main] INFO io.xinghuo.spring.instance.MyBeanFactoryPostProcessor - execute BeanFactoryPostProcessor#postProcessBeanFactory ... 21:07:39 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean user 21:07:39 [main] INFO io.xinghuo.spring.instance.MyInstantiationAwareBeanPostProcessor - execute InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation for user 21:07:39 [main] INFO io.xinghuo.spring.instance.User - execute User#new User() 21:07:39 [main] INFO io.xinghuo.spring.instance.User - execute User#setUserName(Zhanbo) 21:07:39 [main] INFO io.xinghuo.spring.instance.User - execute User#setAge(18) 21:07:39 [main] INFO io.xinghuo.spring.instance.MyInstantiationAwareBeanPostProcessor - execute InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation for user 21:07:39 [main] INFO io.xinghuo.spring.instance.MyInstantiationAwareBeanPostProcessor - execute InstantiationAwareBeanPostProcessor#postProcessProperties for user 21:07:39 [main] INFO io.xinghuo.spring.instance.User - execute BeanNameAware#setBeanName 21:07:39 [main] INFO io.xinghuo.spring.instance.User - execute BeanFactoryAware#setBeanFactory 21:07:39 [main] INFO io.xinghuo.spring.instance.User - execute ApplicationContextAware#setApplicationContext 21:07:39 [main] INFO io.xinghuo.spring.instance.MyBeanPostProcessor - execute BeanPostProcessor#postProcessBeforeInitialization for (user) 21:07:39 [main] INFO io.xinghuo.spring.instance.User - execute InitializingBean#afterPropertiesSet 21:07:39 [main] INFO io.xinghuo.spring.instance.User - execute User#doInit 21:07:39 [main] INFO io.xinghuo.spring.instance.MyBeanPostProcessor - execute BeanPostProcessor#postProcessAfterInitialization for (user) 21:07:39 [main] INFO io.xinghuo.spring.instance.Test - User(nameZhanbo, age18) 21:07:39 [main] INFO io.xinghuo.spring.instance.Test - Shutdown application context 21:07:39 [SpringContextShutdownHook] INFO io.xinghuo.spring.instance.User - execute Destroyable#destroy 21:07:39 [SpringContextShutdownHook] INFO io.xinghuo.spring.instance.User - execute User#doDestroy六、Spring 进阶 - AOP 切面的实现 6.1 引入 我们应该从哪里开始着手 Spring AOP 的源码呢与我们前几节分析的 IOC 源码实现有什么关系呢 ​ 我们通过 XML 来配置 AOP我们不难发现 AOP 是基于 IOC 的 Bean 加载来实现的而 aop:aspectj-autoproxy/ 便是主要的入口。 ​ 然后我们就可以如下初始化的流程和 aop 对应的 handler 类 即 parseCustomElement 方法找到 parse aop:aspectj-autoproxy 的 handler(AopNamespaceHandler) 6.2 AOP 配置标签的解析 上节中我们找到了 AopNamespaceHandler其实就是注册 BeanDefinition 的解析器 BeanDefinitionParser将 aop:xxx 配置标签交给指定的 parser 来处理。 public class AopNamespaceHandler extends NamespaceHandlerSupport {public void init() {// 注册解析 aop:config 配置this.registerBeanDefinitionParser(config, new ConfigBeanDefinitionParser());// 注册解析 aop:aspectj-autoproxy 配置this.registerBeanDefinitionParser(aspectj-autoproxy, new AspectJAutoProxyBeanDefinitionParser());this.registerBeanDefinitionDecorator(scoped-proxy, new ScopedProxyBeanDefinitionDecorator());this.registerBeanDefinitionParser(spring-configured, new SpringConfiguredBeanDefinitionParser());} }config 配置标签的解析 aop:config/ 由 ConfigBeanDefinitionParser 这个类处理作为 parser 类最重要的就是 parse 方法。 Nullable public BeanDefinition parse(Element element, ParserContext parserContext) {CompositeComponentDefinition compositeDef new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));parserContext.pushContainingComponent(compositeDef);this.configureAutoProxyCreator(parserContext, element);for(Element elt : DomUtils.getChildElements(element)) {String localName parserContext.getDelegate().getLocalName(elt);if (pointcut.equals(localName)) {this.parsePointcut(elt, parserContext);} else if (advisor.equals(localName)) {this.parseAdvisor(elt, parserContext);} else if (aspect.equals(localName)) {this.parseAspect(elt, parserContext);}}parserContext.popAndRegisterContainingComponent();return null; }private void parseAspect(Element aspectElement, ParserContext parserContext) {String aspectId aspectElement.getAttribute(id);String aspectName aspectElement.getAttribute(ref);try {this.parseState.push(new AspectEntry(aspectId, aspectName));ListBeanDefinition beanDefinitions new ArrayList();ListBeanReference beanReferences new ArrayList();ListElement declareParents DomUtils.getChildElementsByTagName(aspectElement, declare-parents);for(int i 0; i declareParents.size(); i) {Element declareParentsElement (Element)declareParents.get(i);beanDefinitions.add(this.parseDeclareParents(declareParentsElement, parserContext));}NodeList nodeList aspectElement.getChildNodes();boolean adviceFoundAlready false;for(int i 0; i nodeList.getLength(); i) {Node node nodeList.item(i);if (this.isAdviceNode(node, parserContext)) {if (!adviceFoundAlready) {adviceFoundAlready true;if (!StringUtils.hasText(aspectName)) {parserContext.getReaderContext().error(aspect tag needs aspect bean reference via ref attribute when declaring advices., aspectElement, this.parseState.snapshot());return;}beanReferences.add(new RuntimeBeanReference(aspectName));}AbstractBeanDefinition advisorDefinition this.parseAdvice(aspectName, i, aspectElement, (Element)node, parserContext, beanDefinitions, beanReferences);beanDefinitions.add(advisorDefinition);}}AspectComponentDefinition aspectComponentDefinition this.createAspectComponentDefinition(aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);parserContext.pushContainingComponent(aspectComponentDefinition);for(Element pointcutElement : DomUtils.getChildElementsByTagName(aspectElement, pointcut)) {this.parsePointcut(pointcutElement, parserContext);}parserContext.popAndRegisterContainingComponent();} finally {this.parseState.pop();} }aspectj-autoproxy 配置标签的解析 aop:aspectj-autoproxy/ 则由 AspectJAutoProxyBeanDefinitionParser 这个类处理我们看下 parse 方法 Nullable public BeanDefinition parse(Element element, ParserContext parserContext) {// 注册 AspectJAnnotationAutoProxyCreatorAopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);// 拓展 BeanDefinitionthis.extendBeanDefinition(element, parserContext);return null; }​ AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法对应如下 public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {BeanDefinition beanDefinition AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);registerComponentIfNecessary(beanDefinition, parserContext); }AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法对应如下 Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Nullable Object source) {return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); }​ 到此我们可以发现 AOP 的创建工作交给了 AnnotationAwareAspectJAutoProxyCreator 来完成。 6.3 注解切面代理创建类AnnotationAwareAspectJAutoProxyCreator AnnotationAwareAspectJAutoProxyCreator 是如何工作的呢这时候我们就要看 AnnotationAwareAspectJAutoProxyCreator 类结构关系了。 ​ 如下是类结构关系 ​ 它实现了两类接口 BeanFactoryAware 属于 Bean 级生命周期接口方法InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现一般称它们的实现类为 “后处理器”是容器级生命周期接口方法 结合 6.4 节中 Spring Bean 生命周期的流程 ​ 我们就可以定位到核心的初始化方法肯定在 postProcessBeforeInstantiation 和 postProcessorAfterInitialization 中。 postProcessorBeforeInstantiation ​ 如下是上述结构中 postProcessBeforeInstantiation 的方法 public Object postProcessBeforeInstantiation(Class? beanClass, String beanName) {Object cacheKey this.getCacheKey(beanClass, beanName);if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {// 如果已经在缓存中则忽略if (this.advisedBeans.containsKey(cacheKey)) {return null;}// 是否是 aop 基础类是否跳过if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}TargetSource targetSource this.getCustomTargetSource(beanClass, beanName);if (targetSource ! null) {if (StringUtils.hasLength(beanName)) {this.targetSourcedBeans.add(beanName);}Object[] specificInterceptors this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy this.createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;} else {return null;} }判断是否是 aop 基础类 ​ 是否是 aop 基础类的判断方法 isInfrastructureClass 如下 protected boolean isInfrastructureClass(Class? beanClass) {// 该类是否实现了 AdvicePointcutAdvisor 或者 AopInfrastructureBean 接口boolean retVal Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass) || AopInfrastructureBean.class.isAssignableFrom(beanClass);if (retVal this.logger.isTraceEnabled()) {this.logger.trace(Did not attempt to auto-proxy infrastructure class [ beanClass.getName() ]);}return retVal; }是否应该跳过 shouldSkip protected boolean shouldSkip(Class? beanClass, String beanName) {for(Advisor advisor : this.findCandidateAdvisors()) {if (advisor instanceof AspectJPointcutAdvisor ((AspectJPointcutAdvisor)advisor).getAspectName().equals(beanName)) {return true;}}return super.shouldSkip(beanClass, beanName); }切换方法转成 Advisor ​ findCandiadateAdvisors 方法如下 protected ListAdvisor findCandidateAdvisors() {// 在父类中找到所有的 advisor基于 xml 配置的 aop:before/ 生产的ListAdvisor advisors super.findCandidateAdvisors();// 为 bean Factory 中 AspectJ 切面构建 advistor通过 AspectJ 注解的方式生成 Advisor 类if (this.aspectJAdvisorsBuilder ! null) {advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());}return advisors; }​ 在当前的 bean Factory 中通过 AspectJ 注解的方式生成 Advisor 类buildAspectJAdvisors 方法如下 public ListAdvisor buildAspectJAdvisors() {ListString aspectNames this.aspectBeanNames;if (aspectNames null) {synchronized(this) {aspectNames this.aspectBeanNames;if (aspectNames null) {ListAdvisor advisors new ArrayList();aspectNames new ArrayList();String[] beanNames BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);for(String beanName : beanNames) {if (this.isEligibleBean(beanName)) {Class? beanType this.beanFactory.getType(beanName, false);if (beanType ! null this.advisorFactory.isAspect(beanType)) {aspectNames.add(beanName);AspectMetadata amd new AspectMetadata(beanType, beanName);if (amd.getAjType().getPerClause().getKind() PerClauseKind.SINGLETON) {MetadataAwareAspectInstanceFactory factory new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);ListAdvisor classAdvisors this.advisorFactory.getAdvisors(factory);if (this.beanFactory.isSingleton(beanName)) {this.advisorsCache.put(beanName, classAdvisors);} else {this.aspectFactoryCache.put(beanName, factory);}advisors.addAll(classAdvisors);} else {if (this.beanFactory.isSingleton(beanName)) {throw new IllegalArgumentException(Bean with name beanName is a singleton, but aspect instantiation model is not singleton);}MetadataAwareAspectInstanceFactory factory new PrototypeAspectInstanceFactory(this.beanFactory, beanName);this.aspectFactoryCache.put(beanName, factory);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}}}this.aspectBeanNames aspectNames;return advisors;}}}if (aspectNames.isEmpty()) {return Collections.emptyList();} else {ListAdvisor advisors new ArrayList();for(String aspectName : aspectNames) {ListAdvisor cachedAdvisors (List)this.advisorsCache.get(aspectName);if (cachedAdvisors ! null) {advisors.addAll(cachedAdvisors);} else {MetadataAwareAspectInstanceFactory factory (MetadataAwareAspectInstanceFactory)this.aspectFactoryCache.get(aspectName);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}return advisors;} }​ 上述方法本质上的思路是用 DCL 双重锁的单例实现方式拿到切面类里的切面方法将其转换称 advisor并放入缓存中。 ​ 转换称 advisor 的方法是this.advisorFactory.getAdvisors public ListAdvisor getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {Class? aspectClass aspectInstanceFactory.getAspectMetadata().getAspectClass();String aspectName aspectInstanceFactory.getAspectMetadata().getAspectName();this.validate(aspectClass);MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);ListAdvisor advisors new ArrayList();for(Method method : this.getAdvisorMethods(aspectClass)) {Advisor advisor this.getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);if (advisor ! null) {advisors.add(advisor);}}if (!advisors.isEmpty() lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {Advisor instantiationAdvisor new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);advisors.add(0, instantiationAdvisor);}for(Field field : aspectClass.getDeclaredFields()) {Advisor advisor this.getDeclareParentsAdvisor(field);if (advisor ! null) {advisors.add(advisor);}}return advisors; }​ getAdvisor 方法如下 Nullable public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) {this.validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());AspectJExpressionPointcut expressionPointcut this.getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());// 封装称 advisorreturn expressionPointcut null ? null : new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }获取表达式的切点 ​ 获取表达式的切点的方法 getPointcut 如下 Nullable private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class? candidateAspectClass) {AbstractAspectJAdvisorFactory.AspectJAnnotation? aspectJAnnotation AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation null) {return null;} else {AspectJExpressionPointcut ajexp new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);ajexp.setExpression(aspectJAnnotation.getPointcutExpression());if (this.beanFactory ! null) {ajexp.setBeanFactory(this.beanFactory);}return ajexp;} }​ AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod 的方法如下 private static final Class?[] ASPECTJ_ANNOTATION_CLASSES new Class[]{Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};Nullable protected static AspectJAnnotation? findAspectJAnnotationOnMethod(Method method) {for(Class? clazz : ASPECTJ_ANNOTATION_CLASSES) {AspectJAnnotation? foundAnnotation findAnnotation(method, clazz);if (foundAnnotation ! null) {return foundAnnotation;}}return null; }​ findAnnotation 方法如下 private static A extends Annotation AspectJAnnotationA findAnnotation(Method method, ClassA toLookFor) {A result AnnotationUtils.findAnnotation(method, toLookFor);return result ! null ? new AspectJAnnotation(result) : null;}​ AnnotationUtils.findAnnotation 获取注解方法如下 Nullablepublic static A extends Annotation A findAnnotation(Method method, Nullable ClassA annotationType) {if (annotationType null) {return null;} else {return (A)(!AnnotationFilter.PLAIN.matches(annotationType) !AnnotationsScanner.hasPlainJavaAnnotationsOnly(method) ? (Annotation)MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none()).get(annotationType).withNonMergedAttributes().synthesize(MergedAnnotation::isPresent).orElse((Object)null) : method.getDeclaredAnnotation(annotationType));}}封装称 Advisor 注Advisor 是 advice 的包装器包含了 advice 及其它信息。 ​ 由 InstantiationModelAwarePointcutAdvisorImpl 构造完成 public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {this.declaredPointcut declaredPointcut;this.declaringClass aspectJAdviceMethod.getDeclaringClass();this.methodName aspectJAdviceMethod.getName();this.parameterTypes aspectJAdviceMethod.getParameterTypes();this.aspectJAdviceMethod aspectJAdviceMethod;this.aspectJAdvisorFactory aspectJAdvisorFactory;this.aspectInstanceFactory aspectInstanceFactory;this.declarationOrder declarationOrder;this.aspectName aspectName;if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {Pointcut preInstantiationPointcut Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);this.pointcut new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);this.lazy true;} else {this.pointcut this.declaredPointcut;this.lazy false;this.instantiatedAdvice this.instantiateAdvice(this.declaredPointcut);}}​ 通过 pointcut 获取 advice private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {Advice advice this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);return advice ! null ? advice : EMPTY_ADVICE; }​ 交给 aspectJAdvisorFactory 获取 Nullable public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {// 获取切面类Class? candidateAspectClass aspectInstanceFactory.getAspectMetadata().getAspectClass();this.validate(candidateAspectClass);// 获取切面注解AbstractAspectJAdvisorFactory.AspectJAnnotation? aspectJAnnotation AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation null) {return null;} else if (!this.isAspect(candidateAspectClass)) {throw new AopConfigException(Advice must be declared inside an aspect type: Offending method candidateAdviceMethod in class [ candidateAspectClass.getName() ]);} else {if (this.logger.isDebugEnabled()) {this.logger.debug(Found AspectJ method: candidateAdviceMethod);}// 切面注解转换称 AdviceAbstractAspectJAdvice springAdvice;switch (aspectJAnnotation.getAnnotationType()) {case AtPointcut: // AtPointcut 忽略if (this.logger.isDebugEnabled()) {this.logger.debug(Processing pointcut candidateAdviceMethod.getName() );}return null;case AtAround:springAdvice new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtBefore:springAdvice new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfter:springAdvice new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfterReturning:springAdvice new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterReturning afterReturningAnnotation (AfterReturning)aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterReturningAnnotation.returning())) {springAdvice.setReturningName(afterReturningAnnotation.returning());}break;case AtAfterThrowing:springAdvice new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterThrowing afterThrowingAnnotation (AfterThrowing)aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {springAdvice.setThrowingName(afterThrowingAnnotation.throwing());}break;default:throw new UnsupportedOperationException(Unsupported advice type on method: candidateAdviceMethod);}// 最后将其它切面信息配置到 advicespringAdvice.setAspectName(aspectName);springAdvice.setDeclarationOrder(declarationOrder);String[] argNames this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);if (argNames ! null) {springAdvice.setArgumentNamesFromStringArray(argNames);}springAdvice.calculateArgumentBindings();return springAdvice;} }小结 ​ 回头看主要是处理使用了 Aspect 注解的切面类然后将切面类的所有切面方法根据使用的注解生成对应 Advice并将 Advice 连同切入点匹配器和切面类等信息一并封装到 Advisor 的过程。 postProcessAfterInitialization ​ 有了 Advisor注入到合适的位置并交给代理cglib 和 jdk实现了。 public Object postProcessAfterInitialization(Nullable Object bean, String beanName) {if (bean ! null) {Object cacheKey this.getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) ! bean) {return this.wrapIfNecessary(bean, beanName, cacheKey);}}return bean; }6.4 总结 ​ 通过上文的分析我们进行小结 由 IOC Bean 加载方法栈中找到 parseCustomElement 方法找到 parse aop:aspectj-autoproxy 的 handler(org.springframework.aop.config.AopNamespaceHandler)AopNamespaceHandler 注册了 aop:aspectj-autoproxy/ 的解析类是 AspectJAutoProxyBeanDefinitionParserAspectJAutoProxyBeanDefinitionParser 的 parse 方法通过 AspectJAwareAdvisorAutoProxyCreator 类去创建AspectJAwareAdvisorAutoProxyCreator 实现了两类接口BeanFactoryAware 和 BeanPostProcessor根据 Bean 生命周期方法找到了两个核心方法postProcessBeforeInstantiation 和 postProcessAfterInitialization postProcessBeforeInstantiation主要是处理使用了 Aspect 注解的切面类然后将切面类的所有切面方法根据使用的注解生成对应 Advice并将 Advice 连同切入点匹配器和切面类等信息一并封装到 AdvisorpostProcessAfterInitialization主要负责将 Advisor 注入到合适的位置创建cglib 或 jdk为后面给代理进行增强实现做准备 七、Spring 进阶 - AOP 代理的创建 7.1 引入 前文主要介绍了 Spring AOP 原理解析的切面实现过程加载配置将切面类的所有切面方法根据使用的注解生成对应 Advice并将 Advice 连同切入点匹配器和切面类等信息一并封装到 Advisor。 ​ 同时我们也总结了 Spring AOP 初始化的过程具体如下 由 IOC Bean 加载方法栈中找到 parseCustomElement 方法找到 parse aop:aspectj-autoproxy 的 handler(org.springframework.aop.config.AopNamespaceHandler)AopNamespaceHandler 注册了 aop:aspectj-autoproxy 的解析类是 AspectJAutoProxyBeanDefinitionParserAspectJAutoProxyBeanDefinitionParser 的 parse 方法通过 AspectJAwareAdvisorAutoProxyCreator 类去创建AspectJAwareAdvisorAutoProxyCreator 实现了两类接口BeanFactoryAware 和 BeanPostProcessor根据 Bean 生命周期方法找到两个核心方法postPorcessBeforeInistantiation 和 postProcessAfterInitialization postProcessBeforeInstantiation主要是处理了使用 Aspect 注解的切面类然后将切面类的所有切面方法根据使用的注解生成对应 Advice并将 Advice 连同切入点匹配器和切面类等信息一并封装到 AdvisorpostProcessAfterInitialization主要负责将 Advisor 注入到合适的位置创建代理cglib 或 jdk为后面给代理进行增强实现做准备 本节接着介绍 postProcessAfterInitialization 的方法即 Spring AOP 的代理cglib 或 jdk的创建过程。 7.2 代理的创建 ​ 创建代理的方法是 postProcessAfterInitialization如果 bean 被子类表示为代理则使用配置的拦截器创建一个代理 public Object postProcessAfterInitialization(Nullable Object bean, String beanName) {if (bean ! null) {Object cacheKey this.getCacheKey(bean.getClass(), beanName);// 如果不是提前暴露的代理if (this.earlyProxyReferences.remove(cacheKey) ! bean) {return this.wrapIfNecessary(bean, beanName, cacheKey);}}return bean; }wrapIfNecessary 方法主要用于判断是否需要创建带如果 Bean 能够获取到 advisor 才需要创建代理。 protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 如果 bean 是通过 TargetSource if (StringUtils.hasLength(beanName) this.targetSourcedBeans.contains(beanName)) {return bean;} else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { // 如果 bean 是切面类return bean;} else if (!this.isInfrastructureClass(bean.getClass()) !this.shouldSkip(bean.getClass(), beanName)) {// 获取所有 advisor如果没有获取到那说明不要进行增强也就不需要代理了。Object[] specificInterceptors this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);if (specificInterceptors ! DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);// 创建代理Object proxy this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;} else {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}} else {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;} }获取所有的 Advisor ​ 我们看下获取所有 advisor 的方法 getAdvicesAndAdvisorsForBean protected Object[] getAdvicesAndAdvisorsForBean(Class? beanClass, String beanName, Nullable TargetSource targetSource) {ListAdvisor advisors this.findEligibleAdvisors(beanClass, beanName);return advisors.isEmpty() ? DO_NOT_PROXY : advisors.toArray(); }​ 通过 findEligibleAdvisors 方法获取 advisor如果获取不到则返回 DO_NOT_PROXY不需要创建代理findEliglibleAdvisors 方法如下 protected ListAdvisor findEligibleAdvisors(Class? beanClass, String beanName) {// 如上文一样获取所有切面类的切面方法生成 AdvisorListAdvisor candidateAdvisors this.findCandidateAdvisors();// 找到这些 Advisor 中能够应用于 beanClass 的 AdvisorListAdvisor eligibleAdvisors this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);// 如果需要交给子类拓展this.extendAdvisors(eligibleAdvisors);// 对 Advisor 排序if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors this.sortAdvisors(eligibleAdvisors);}return eligibleAdvisors; }​ 获取所有切面类的切面方法生成 Advisor protected ListAdvisor findCandidateAdvisors() {Assert.state(this.advisorRetrievalHelper ! null, No BeanFactoryAdvisorRetrievalHelper available);return this.advisorRetrievalHelper.findAdvisorBeans(); }​ 找到这些 Advisor 中能够应用于 beanClass 的 Advisor public static ListAdvisor findAdvisorsThatCanApply(ListAdvisor candidateAdvisors, Class? clazz) {if (candidateAdvisors.isEmpty()) {return candidateAdvisors;} else {ListAdvisor eligibleAdvisors new ArrayList();for(Advisor candidate : candidateAdvisors) {// 通过 Introduction 实现的 adviceif (candidate instanceof IntroductionAdvisor canApply(candidate, clazz)) {eligibleAdvisors.add(candidate);}}boolean hasIntroductions !eligibleAdvisors.isEmpty();for(Advisor candidate : candidateAdvisors) {if (!(candidate instanceof IntroductionAdvisor) canApply(candidate, clazz, hasIntroductions)) {// 能够应用于 clazz 的 AdviceeligibleAdvisors.add(candidate);}}return eligibleAdvisors;} }创建代理的入口方法 ​ 获取所有 advisor 后如果有 advisor则说明需要增强即需要创建代理创建代理的方法如下 protected Object createProxy(Class? beanClass, Nullable String beanName, Nullable Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);}ProxyFactory proxyFactory new ProxyFactory();proxyFactory.copyFrom(this);if (proxyFactory.isProxyTargetClass()) {if (Proxy.isProxyClass(beanClass)) {for(Class? ifc : beanClass.getInterfaces()) {proxyFactory.addInterface(ifc);}}} else if (this.shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);} else {this.evaluateProxyInterfaces(beanClass, proxyFactory);}Advisor[] advisors this.buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);this.customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (this.advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}ClassLoader classLoader this.getProxyClassLoader();if (classLoader instanceof SmartClassLoader classLoader ! beanClass.getClassLoader()) {classLoader ((SmartClassLoader)classLoader).getOriginalClassLoader();}return proxyFactory.getProxy(classLoader); }​ proxyFactory.getProxy(classLoader) public Object getProxy(Nullable ClassLoader classLoader) {return this.createAopProxy().getProxy(classLoader); }根据条件创建代理jdk 或 cglib ​ DefaultAopProxyFactory.createAopProxy public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (!NativeDetector.inNativeImage() (config.isOptimize() || config.isProxyTargetClass() || this.hasNoUserSuppliedProxyInterfaces(config))) {Class? targetClass config.getTargetClass();if (targetClass null) {throw new AopConfigException(TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.);} else {return (AopProxy)(!targetClass.isInterface() !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));}} else {return new JdkDynamicAopProxy(config);} }​ 几个要点 config.isOptimize() 是通过 optimize 设置表示配置是自定义的默认是 falseconfig.isProxyTargetClass() 是通过 aop:config proxy-target-classtrue/ 来配置的表示优先使用 cglib 代理默认是 falsehasNoUserSuppliedProxyInterfaces(config) 表示是否目标类实现了接口 ​ 由此我们可以知道 ​ Spring 默认在目标类实现接口时是通过 JDK 代理实现的只有非接口的是通过 Cglib 代理实现的。当设置 proxy-target-class 为 true 时在目标类不是接口或者代理类优先使用 cglib 代理实现。 八、Spring 进阶 - Cglib 代理实现 8.1 引入 在前边已经介绍了 SpringAOP 的切面实现和创建动态代理的过程那么动态代理是如何工作的呢本节主要介绍 Cglib 动态代理的案例和 Spring AOP 实现的原理。 ​ 要了解动态代理是如何工作的首先需要了解 什么是代理模式什么是动态代理什么是 CglibSpring AOP 和 Cglib 是什么关系 动态代理要解决什么问题 什么是代理 ​ 代理模式Proxy pattern为另一个对象提供一个替身或占位符以控制对这个对象的访问 ​ 举个简单的例子 ​ 我client如果要买doOperation房可以找中介proxy买房中介直接和卖方target买房。中介和卖方都实现买卖doOperation的操作。中介就是代理proxy。 什么是动态代理 动态代理就是在程序运行期间创建目标对象的代理对象并对目标对象中的方法进行功能性增强的一种技术。 ​ 在生成代理对象的过程中目标对象不变代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间对象中方法的动态拦截在拦截方法的前后执行功能操作。 什么是 CglibSpring AOP 和 Cglib 是什么关系 Cglib 是一个强大的、高性能的代码生成包它广泛被许多 AOP 框架使用为他们提供方法的拦截。 最底层是字节码字节码相关的知识可以参考Java 字节码 ASM 是操作字节码的工具 cglib 基于 ASM 字节码工具操作字节码即动态生成代理对方法进行增强 Spring AOP 基于 cglib 进行封装实现 cglib 方式的动态代理 8.2 Cglib 代理的例子 这里举一个使用 cglib 的简单例子。 pom 包依赖 ​ 引入 cglib 的依赖包 dependencygroupIdcglib/groupIdartifactIdcglib/artifactIdversion3.3.0/version /dependency定义实体类 ​ User public class user {private String name;private int age;public user(String name, int age) {this.name name;this.age age;}public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}public String toString() {return User{name name , age age };} }被代理的类 ​ 即目标类对被代理的类中的方法进行增强 public class UserServiceImpl {public ListUser findUserList() {return Collections.singletonList(new User(zhangsan, 18));}public void addUser() {// TODO Add User} }Cglib 代理 ​ Cglib 代理类需要实现 MethodInterceptor 接口并指定代理目标类 target public class UserLogProxy implements MethodInterceptor {/*** 业务类对象供代理方法中进行真正方法调用*/private Object target;public Object getUserLogProxy(Object target) {// 给业务对象赋值this.target target;// 创建加强类用来创建动态代理类Enhancer enhancer new Enhancer();// 为加强器指定要代理的业务类即为下面生成的代理类指定父类enhancer.setSuperclass(target.getClass());// 设置回调对于代理类上所有方法的调用都会调用 Callback而 Callback 则需要实现 intercept() 方法进行拦截enhancer.setCallback(this);// 创建动态代理类并返回return enhancer.create();}Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println([before] execute method: method.getName());Object result methodProxy.invokeSuper(o, objects);System.out.println([after] execute method: method.getName() , result value: result );return result;} }使用代理 ​ 启动类中指定代理目标并执行 public class Test {public static void main(String[] args) {UserServiceImpl userLogProxy (UserServiceImpl) new UserLogProxy().getUserLogProxy(new UserServiceImpl());userLogProxy.findUserList();userLogProxy.addUser();} }简单测试 ​ 启动上述类 main 函数执行的结果如下 [before] execute method: findUserList [after] execute method: findUserList, result value: [User{namezhangsan, age18}] [before] execute method: addUser [after] execute method: addUser, result value: null8.3 Cglib 代理的流程 ​ 将上述测试的主要流程画出来便于进行理解 ​ 更多细节 在上图中我们可以通过在 Enhancer 中配置更多的参数来控制代理的行为比如如果只希望增强这个类中的一个方法而不是所有的方法那就增强 callbackFilter 来对目标类中方法进行过滤在 Enhancer 可以有更多的参数类配置其行为不过我们只学习上述主要的流程就够了。final 方法为什么不能被代理很显然 final 方法没法被子类覆盖当然能不能被代理了。Mockito 为什么不能 mock 静态方法因为 mockito 也是基于 cglib 动态代理来实现的static 方法也不能被子类覆盖所以显然不能 mock。但 PowerMock 可以 mock 静态方法因为它直接在 bytecode 上工作。 8.4 Spring AOP 中 Cglib 代理的实现 Spring AOP 封装了 cglib通过其进行动态代理的创建。 我们看一下 CglibAopProxy 的 getProxy 方法 public Object getProxy() {return this.getProxy((ClassLoader)null); }public Object getProxy(Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace(Creating CGLIB proxy: this.advised.getTargetSource());}try {Class? rootClass this.advised.getTargetClass();Assert.state(rootClass ! null, Target class must be available for creating a CGLIB proxy);// 上面流程图中的目标类Class? proxySuperClass rootClass;if (rootClass.getName().contains($$)) {proxySuperClass rootClass.getSuperclass();Class?[] additionalInterfaces rootClass.getInterfaces();for(Class? additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface);}}this.validateClassIfNecessary(proxySuperClass, classLoader);// 重点看这里设置各种参数来构建 EnhancerEnhancer enhancer this.createEnhancer();if (classLoader ! null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader ((SmartClassLoader)classLoader).isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}enhancer.setSuperclass(proxySuperClass);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));// 设置 callback 回调接口即方法的增强点Callback[] callbacks this.getCallbacks(rootClass);Class?[] types new Class[callbacks.length];for(int x 0; x types.length; x) {types[x] callbacks[x].getClass();}// 上节提到的 filterenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);// 创建 proxy 和其实例return this.createProxyClassAndInstance(enhancer, callbacks);} catch (IllegalArgumentException | CodeGenerationException ex) {throw new AopConfigException(Could not generate CGLIB subclass of this.advised.getTargetClass() : Common causes of this problem include using a final class or a non-visible class, ex);} catch (Throwable ex) {throw new AopConfigException(Unexpected AOP exception, ex);} }获取 callback 的方法如下提几个理解的要点rootClass即目标代理类advised包含上下文中我们获取到的 advisor 增强器的集合exposeProxy在 xml 配置文件中配置的背景就是如果在事务 A 中使用了代理事务 A 调用了目标类的方法 a在方法 a 中又调用目标类的方法 b方法 ab 同时都是要被增强的方法如果不配置 exposeProxy 属性方法 b 的增强将会失效如果配置 exposeProxy方法 b 在方法 a 的执行中也会被增强了DynamicAdvisedInterceptor拦截器将 advised包含上文中我们获取到的 advisor 增强器构建配置的 AOP 的 callback第一个 callbacktargetInterceptorxml 配置的 optimize 属性使用的第二个 callback最后连同其它 5 个默认的 Interceptor 返回作为 cglib 的拦截器链之后通过 CallbackFilter 的 accpet 方法返回的索引从这个集合中返回对应的拦截增强器执行增强操作 private Callback[] getCallbacks(Class? rootClass) throws Exception {boolean exposeProxy this.advised.isExposeProxy();boolean isFrozen this.advised.isFrozen();boolean isStatic this.advised.getTargetSource().isStatic();Callback aopInterceptor new DynamicAdvisedInterceptor(this.advised);Callback targetInterceptor;if (exposeProxy) {targetInterceptor (Callback)(isStatic ? new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));} else {targetInterceptor (Callback)(isStatic ? new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));}Callback targetDispatcher (Callback)(isStatic ? new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());Callback[] mainCallbacks new Callback[]{aopInterceptor, targetInterceptor, new SerializableNoOp(), targetDispatcher, this.advisedDispatcher, new EqualsInterceptor(this.advised), new HashCodeInterceptor(this.advised)};Callback[] callbacks;if (isStatic isFrozen) {Method[] methods rootClass.getMethods();Callback[] fixedCallbacks new Callback[methods.length];this.fixedInterceptorMap CollectionUtils.newHashMap(methods.length);for(int x 0; x methods.length; x) {Method method methods[x];ListObject chain this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);fixedCallbacks[x] new FixedChainStaticTargetInterceptor(chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());this.fixedInterceptorMap.put(method, x);}callbacks new Callback[mainCallbacks.length fixedCallbacks.length];System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);this.fixedInterceptorOffset mainCallbacks.length;} else {callbacks mainCallbacks;}return callbacks; }九、Spring 进阶 - JDK 代理实现 9.1 引入 上文我们学习了 Spring AOP Cglib 动态代理的实现本文主要围绕 Spring AOP JDK 动态代理进行展开。 什么是 JDK 代理 ​ JDK 动态代理是由 JDK 提供的工具类 Proxy 实现的动态代理类是在运行时生成指定接口的代理类每个代理实例实现需要代理的接口都有一个关联的调用处理程序对象此对象实现了 InvocationHandler最终的业务逻辑是在 InvocationHandler 实现类的 invoke 方法上。 9.2 JDK 代理的例子 这里举一个使用 JDK 代理的简单例子。 定义实体 ​ User public class User {private String name;private int age;public User(String name, int age) {this.name name;this.age age;}public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}public String toString() {return User{name name , age age };} }被代理的类和接口 ​ 接口如下 public interface UserService {ListUser findUserList();void addUser(); }​ 实现类如下 public class UserServiceImpl implements UserService {public ListUser findUserList() {return Collections.singletonList(new User(zhangsan, 18));}public void addUser() {// TODO Add User} }JDK 代理类 ​ 代理类如下 public class UserLogProxy {private UserService target;public UserLogProxy(UserServiceImpl target) {super();this.target target;}public UserService getLogProxy() {ClassLoader loader target.getClass().getClassLoader();Class[] interfaces new Class[]{UserService.class};return (UserService) Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {/*** param proxy 代理对象。一般不使用该对象。* param method 正在被调用的方法* param args 调用方法传入的参数*/Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName method.getName();System.out.println([before] execute method: methodName);Object result null;try {result method.invoke(target, args);} catch (NullPointerException e) {e.printStackTrace();}System.out.println([after] execute method: methodName , return value: result);return result;}});} }使用代理 ​ 启动类指定代理目标并执行 public class Test {public static void main(String[] args) {UserService userService new UserLogProxy(new UserServiceImpl()).getLogProxy();userService.findUserList();userService.addUser();} }简单测试 ​ 启动上述类 main 函数执行结果如下 [before] execute method: findUserList [after] execute method: findUserList, return value: [User{namezhangsan, age18}] [before] execute method: addUser [after] execute method: addUser, return value: null9.3 JDK 代理的流程 JDK 代理自动生成的 class 是由 sun.misc.PorxyGenerator 来生成的。 ProxyGenerator 生成代码 ​ 我们看下 sun.misc.ProxyGenerator 生成代码的逻辑 public static byte[] generateProxyClass(final String name, Class?[] interfaces, int accessFlags) {ProxyGenerator gen new ProxyGenerator(name, interfaces, accessFlags);final byte[] classFile gen.generateClassFile();... }​ generateClassFile 方法如下 private byte[] generateClassFile() {// 第一步将所有方法包装成 ProxyMethod 对象// 将 Object 类中 hashCode、equals、toString 方法包装成 ProxyMethod 对象this.addProxyMethod(hashCodeMethod, Object.class);this.addProxyMethod(equalsMethod, Object.class);this.addProxyMethod(toStringMethod, Object.class);// 将代理类接口方法包装成 ProxyMethod 对象for(Class intf : this.interfaces) {for(Method m : intf.getMethods()) {this.addProxyMethod(m, intf);}}// 校验返回类型for(List sigmethods : this.proxyMethods.values()) {checkReturnTypes(sigmethods);}// 第二步为代理类组装字段构建函数方法static 初始化块等try {// 添加构造函数参数是 InvocationHandlerthis.methods.add(this.generateConstructor());// 代理方法for(List sigmethods : this.proxyMethods.values()) {for(ProxyMethod pm : sigmethods) {// 字段this.fields.add(new FieldInfo(pm.methodFieldName, Ljava/lang/reflect/Method;, 10));// 上述 ProxyMethod 中的方法this.methods.add(pm.generateMethod());}}// static 初始化块this.methods.add(this.generateStaticInitializer());} catch (IOException e) {throw new InternalError(unexpected I/O Exception, e);}if (this.methods.size() 65535) {throw new IllegalArgumentException(method limit exceeded);} else if (this.fields.size() 65535) {throw new IllegalArgumentException(field limit exceeded);} else {// 第三步写入 class 文件this.cp.getClass(dotToSlash(this.className));this.cp.getClass(java/lang/reflect/Proxy);for(Class intf : this.interfaces) {this.cp.getClass(dotToSlash(intf.getName()));}this.cp.setReadOnly();ByteArrayOutputStream bout new ByteArrayOutputStream();DataOutputStream dout new DataOutputStream(bout);try {dout.writeInt(0xCAFEBABE);dout.writeShort(CLASSFILE_MINOR_VERSION);dout.writeShort(CLASSFILE_MAJOR_VERSION);cp.write(dout); dout.writeShort(accessFlags);dout.writeShort(cp.getClass(dotToSlash(className)));dout.writeShort(cp.getClass(superclassName));dout.writeShort(interfaces.length);for (Class? intf : interfaces) {dout.writeShort(cp.getClass(dotToSlash(intf.getName())));}dout.writeShort(fields.size());for (FieldInfo f : fields) {f.write(dout);}dout.writeShort(methods.size());for (MethodInfo m : methods) {m.write(dout);}dout.writeShort(0);} catch (IOException e) {throw new InternalError(unexpected I/O Exception, e);}return bout.toByteArray();} }​ 一共三个步骤把大象装进冰箱分几步 第一步把冰箱门打开准备工作将所有方法包装成 ProxyMethod 对象包括 Object 类中 hashCode、equals、toString 方法以及被代理的接口中的方法第二步把大象装进去为代理类组装字段、构造函数、方法、static 初始化块等第三步把冰箱门关上写入 class 文件 从生成的 Proxy 代码看执行流程 ​ 从上述 sun.mics.ProxyGenerator 类中可以看到这个类里面有一个配置参数 sun.mics.ProxyGenerator.saveGeneratedFiles可以通过这个参数将生成的 Proxy 类保存在本地比如设置为 true 执行后System.setProperty(sun.misc.ProxyGenerator.saveGeneratedFiles, true)生成的文件如下 ​ 我们看一下生成后的代码 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) //package com.sun.proxy;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import java.util.List; import proxy.cglib.UserService;// 所有类和方法都是 final 类型的 public final class $Proxy0 extends Proxy implements UserService {private static Method m1;private static Method m4;private static Method m2;private static Method m0;private static Method m3;// 构造函数注入 InvocationHandlerpublic $Proxy0(InvocationHandler var1) throws {super(var1);}public final boolean equals(Object var1) throws {try {return (Boolean)super.h.invoke(this, m1, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final List findUserList() throws {try {return (List)super.h.invoke(this, m4, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final String toString() throws {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final int hashCode() throws {try {return (Integer)super.h.invoke(this, m0, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final void addUser() throws {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}static {try {// 初始化 methods2 个 UserSerice 接口中的方法 3个 Object 中的接口m1 Class.forName(java.lang.Object).getMethod(equals, Class.forName(java.lang.Object));m4 Class.forName(proxy.cglib.UserService).getMethod(findUserList);m2 Class.forName(java.lang.Object).getMethod(toString);m0 Class.forName(java.lang.Object).getMethod(hashCode);m3 Class.forName(proxy.cglib.UserService).getMethod(addUser);} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(((Throwable)var2).getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(((Throwable)var3).getMessage());}} }​ 上述代码的主要流程是 ProxyGenerator 创建 Proxy 的具体类 $Proxy0由 static 初始化块初始化接口方法2 个 UserService 接口中的方法3 个 Object 中的接口方法由 构造函数注入 InvocationHandler执行的时候通过 ProxyGenerator 创建的 Proxy调用 InvocationHandler 的 invoke 方法执行我们自定义的 invoke 方法 9.4 Spring AOP 中 JDK 代理的实现 ​ Spring AOP 扮演的是 JDK 代理的创建和调用两个角色我们通过这两个方向来看下 Spring AOP 的代码JdkDynamicAopProxy 类 Spring AOP Jdk 代理的创建 ​ 代理的创建比较简单调用 getProxy 方法然后直接调用 JDK 中 Proxy.newProxyInstance() 方法将 classloader 和被代理的接口方法传入即可。 public Object getProxy() {return this.getProxy(ClassUtils.getDefaultClassLoader()); }public Object getProxy(Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace(Creating JDK dynamic proxy: this.advised.getTargetSource());}return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this); }Spring AOP Jdk 代理的执行 ​ 执行的方法如下 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object oldProxy null;boolean setProxyContext false;TargetSource targetSource this.advised.targetSource;Object target null;Boolean retVal;try {// 执行的是 equals 方法if (this.equalsDefined || !AopUtils.isEqualsMethod(method)) {// 执行的是 hashCode 方法if (!this.hashCodeDefined AopUtils.isHashCodeMethod(method)) {Integer var19 this.hashCode();return var19;}// 如果是包装类则 dispatch to proxy configif (method.getDeclaringClass() DecoratingProxy.class) {Class var18 AopProxyUtils.ultimateTargetClass(this.advised);return var18;}// 用反射方式来执行切点if (!this.advised.opaque method.getDeclaringClass().isInterface() method.getDeclaringClass().isAssignableFrom(Advised.class)) {Object var17 AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);return var17;}if (this.advised.exposeProxy) {oldProxy AopContext.setCurrentProxy(proxy);setProxyContext true;}target targetSource.getTarget();Class? targetClass target ! null ? target.getClass() : null;// 获取拦截链ListObject chain this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;if (chain.isEmpty()) {Object[] argsToUse AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);} else {MethodInvocation invocation new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);retVal invocation.proceed();}Class? returnType method.getReturnType();if (retVal ! null retVal target returnType ! Object.class returnType.isInstance(proxy) !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {retVal proxy;} else if (retVal null returnType ! Void.TYPE returnType.isPrimitive()) {throw new AopInvocationException(Null return value from advice does not match primitive return type for: method);}Object var12 retVal;return var12;}retVal this.equals(args[0]);} finally {if (target ! null !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {AopContext.setCurrentProxy(oldProxy);}}return retVal; }
http://www.w-s-a.com/news/269483/

相关文章:

  • 如何做好网站首页企术建站
  • 杭州网站建设咨询蓝韵网络聊城有制作网站的吗
  • 网站开发注意的事项深圳企业网站
  • 哈尔滨网站制作哪里专业网站建设维护有哪些内容
  • 花的网站建设规划书网络营销培训
  • 又拍云wordpress全站cdn无锡做网站品牌公司
  • 计算机网络工程网站建设黄石建设信息网站
  • 旅游网站开发毕业设计开题报告青岛网站建设服务公司
  • 人员调动在网站上怎么做网站开发课程意见和建议
  • 卓训网是个什么网站wordpress命令执行时间
  • 网站建设需要做哪些工作网片焊接
  • 网站优化方案dedecms win8风格网站模板
  • 企业如何制作网站管理系统慈溪住房和城乡建设部网站
  • 青岛网站建设有哪些公司区块链网站开发价格
  • 怎么设置网站的logo微信公众号的h5网站开发6
  • 粉色的网站绍兴市建设局网站
  • 个人网站的基本风格是wordpress 模板选择
  • 南昌专业做网站公司有哪些广州市住房城乡建设部门户网站
  • 福州网站建设团队淘宝联盟网站怎么建设
  • 福州企业网站建站模板国内黑色风格的网站
  • 好看的网站首页设计android移动开发
  • 域名注册完成后如何做网站域名 删除 wordpress
  • wordpress xml导入大小东莞seo优化方案
  • 网站建设效益网站销售怎么做的
  • 利用网站空间做代理设计方案的格式范文
  • 无锡建设工程质量监督网站遵义做手机网站建设
  • 衡阳商城网站制作ps做网站首页规范尺寸
  • 微信网站应用开发营销推广的方案
  • 广州做网站商城的公司制作一个app的完整流程
  • 湖南城乡建设厅网站163注册企业邮箱