diff --git a/README.md b/README.md index af8032b..47c1d13 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,86 @@ # mini-spring [![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/DerekYRC/mini-spring) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) +[![Stars](https://img.shields.io/github/stars/DerekYRC/mini-spring)](https://img.shields.io/github/stars/DerekYRC/mini-spring) +[![Forks](https://img.shields.io/github/forks/DerekYRC/mini-spring)](https://img.shields.io/github/forks/DerekYRC/mini-spring) -## About -* [中文版](./README_CN.md) - -The **mini-spring** is a simplified version of the Spring framework that will help you quickly get familiar with the Spring source code and grasp the core principles of Spring. The core logic of Spring is extracted, the code is extremely simplified, and the core functions of Spring, such as IoC and AOP, resource loaders, event listeners, type conversion, container extension points, bean life cycle and scope, and application context, are retained. - -If this project can help you, please give a **STAR, thank you!!!** - -## Contents -#### Basics -* [IoC](#Ioc) - * [Implement a simple container](#实现一个简单的容器) - * [BeanDefinition and BeanDefinitionRegistry](#BeanDefinition和BeanDefinitionRegistry) - * [Bean Instantiation Strategy](#Bean实例化策略InstantiationStrategy) - * [Populate bean with property values](#为bean填充属性) - * [Populate bean with bean](#为bean注入bean) - * [Resource and ResourceLoader](#资源和资源加载器) - * [Define the bean in the XML file](#在xml文件中定义bean) - * [Container extension mechanism:BeanFactoryPostProcess and BeanPostProcessor](#容器扩展机制BeanFactoryPostProcess和BeanPostProcessor) - * [ApplicationContext](#应用上下文ApplicationContext) - * [Init method and destroy method of bean](#bean的初始化和销毁方法) - * [Aware interface](#Aware接口) - * [Bean scope, added prototype support](#bean作用域,增加prototype的支持) - * [FactoryBean](#FactoryBean) - * [Event and event listener](#容器事件和事件监听器) -* [AOP](#AOP) - * [Pointcut expression](#切点表达式) - * [JDK-based dynamic proxy](#基于JDK的动态代理) - * [CGLIB-based dynamic proxy](#基于CGLIB的动态代理) - * [AOP ProxyFactory](#AOP代理工厂ProxyFactory) - * [Common Advice: BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice](#几种常用的Advice) - * [PointcutAdvisor:A combination of Pointcut and Advice](#PointcutAdvisor:Pointcut和Advice的组合) - * [Dynamic proxies are integrated into the bean lifecycle](#动态代理融入bean生命周期) +**[English](./README_en.md) | 简体中文** + +**姊妹版:**[**mini-spring-cloud**](https://github.com/DerekYRC/mini-spring-cloud) **(简化版的spring cloud框架)** + +## 关于 + +**mini-spring**是简化版的spring框架,能帮助你快速熟悉spring源码和掌握spring的核心原理。抽取了spring的核心逻辑,**代码极度简化,保留spring的核心功能**,如IoC和AOP、资源加载器、事件监听器、类型转换、容器扩展点、bean生命周期和作用域、应用上下文等核心功能。 + +如果本项目能帮助到你,请给个**STAR,谢谢!!!** + +## 功能 +#### 基础篇 +* [IoC](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#基础篇IoC) + * [实现一个简单的容器](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#最简单的bean容器) + * [BeanDefinition和BeanDefinitionRegistry](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#BeanDefinition和BeanDefinitionRegistry) + * [Bean实例化策略InstantiationStrategy](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#Bean实例化策略InstantiationStrategy) + * [为bean填充属性](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#为bean填充属性) + * [为bean注入bean](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#为bean注入bean) + * [资源和资源加载器](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#资源和资源加载器) + * [在xml文件中定义bean](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#在xml文件中定义bean) + * [容器扩展机制BeanFactoryPostProcess和BeanPostProcessor](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#BeanFactoryPostProcess和BeanPostProcessor) + * [应用上下文ApplicationContext](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#应用上下文ApplicationContext) + * [bean的初始化和销毁方法](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#bean的初始化和销毁方法) + * [Aware接口](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#Aware接口) + * [bean作用域,增加prototype的支持](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#bean作用域增加prototype的支持) + * [FactoryBean](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#FactoryBean) + * [容器事件和事件监听器](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#容器事件和事件监听器) +* [AOP](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#基础篇AOP) + * [切点表达式](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#切点表达式) + * [基于JDK的动态代理](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#基于JDK的动态代理) + * [基于CGLIB的动态代理](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#基于CGLIB的动态代理) + * [AOP代理工厂ProxyFactory](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#AOP代理工厂) + * [几种常用的Advice: BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#几种常用的AdviceBeforeAdviceAfterAdviceAfterReturningAdviceThrowsAdvice) + * [PointcutAdvisor:Pointcut和Advice的组合](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#pointcutadvisorpointcut和advice的组合) + * [动态代理融入bean生命周期](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#动态代理融入bean生命周期) +#### 扩展篇 +* [PropertyPlaceholderConfigurer](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#propertyplaceholderconfigurer) +* [包扫描](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#包扫描) +* [@Value注解](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#value注解) +* [基于注解@Autowired的依赖注入](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#autowired注解) +* [类型转换(一)](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#类型转换一) +* [类型转换(二)](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#类型转换二) + +#### 高级篇 +* [解决循环依赖问题(一):没有代理对象](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#解决循环依赖问题一没有代理对象) +* [解决循环依赖问题(二):有代理对象](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#解决循环依赖问题二有代理对象) + +#### 其他 +* [没有为代理bean设置属性(discovered and fixed by kerwin89)](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#bug-fix没有为代理bean设置属性discovered-and-fixed-by-kerwin89) +* [支持懒加载和多切面增强(by zqczgl)](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md#支持懒加载和多切面增强by-zqczgl) + +## 使用方法 +阅读[changelog.md](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md) + +[视频教程(完整版)](https://www.bilibili.com/video/BV1nb4y1A7YJ) + +## 提问 +[**点此提问**](https://github.com/DerekYRC/mini-spring/issues/4) + +## 贡献 +欢迎Pull Request + +## 关于我 +[**点此了解**](https://github.com/DerekYRC) + +手机/微信:**15521077528** 邮箱:**15521077528@163.com** -#### Expanding -* [PropertyPlaceholderConfigurer](#PropertyPlaceholderConfigurer) -* [Type conversion](#类型转换) -* [Package scan](#包扫描) -* [Autowired annotation](#基于注解的依赖注入Autowired) +## Star History -#### Advanced -* [Solve the problem of circular dependencies](#解决循环依赖问题) +[![Star History Chart](https://api.star-history.com/svg?repos=DerekYRC/mini-spring&type=Date)](https://star-history.com/#DerekYRC/mini-spring&Date) -## Usage -Each function point corresponds to a branch. Switch to the branch corresponding to the function point to see the new function. The incremental change point is described in the [changelog.md](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md) file. +## 版权说明 +未取得本人书面许可,不得将该项目用于商业用途 -## Reference +## 参考 - [《Spring源码深度解析》](https://book.douban.com/subject/25866350/) +- [《Spring 源码解析》](http://svip.iocoder.cn/categories/Spring) - [《精通Spring 4.x》](https://book.douban.com/subject/26952826/) -- [tiny-spring](https://github.com/code4craft/tiny-spring) +- [《tiny-spring》](https://github.com/code4craft/tiny-spring) diff --git a/README_CN.md b/README_CN.md deleted file mode 100644 index 42c344b..0000000 --- a/README_CN.md +++ /dev/null @@ -1,54 +0,0 @@ -# mini-spring -[![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/DerekYRC/mini-spring) -[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) - -## 关于 -* [English version](./README.md) - -**mini-spring**是简化版的spring框架,能帮助你快速熟悉spring源码和掌握spring的核心原理。抽取了spring的核心逻辑,代码极度简化,保留spring的核心功能,如IoC和AOP、资源加载器、事件监听器、类型转换、容器扩展点、bean生命周期和作用域、应用上下文等核心功能。 - -如果本项目能帮助到你,请给个**STAR,谢谢!!!** - -## 功能 -#### 基础篇 -* [IoC](#Ioc) - * [实现一个简单的容器](#实现一个简单的容器) - * [BeanDefinition和BeanDefinitionRegistry](#BeanDefinition和BeanDefinitionRegistry) - * [Bean实例化策略InstantiationStrategy](#Bean实例化策略InstantiationStrategy) - * [为bean填充属性](#为bean填充属性) - * [为bean注入bean](#为bean注入bean) - * [资源和资源加载器](#资源和资源加载器) - * [在xml文件中定义bean](#在xml文件中定义bean) - * [容器扩展机制BeanFactoryPostProcess和BeanPostProcessor](#容器扩展机制BeanFactoryPostProcess和BeanPostProcessor) - * [应用上下文ApplicationContext](#应用上下文ApplicationContext) - * [bean的初始化和销毁方法](#bean的初始化和销毁方法) - * [Aware接口](#Aware接口) - * [bean作用域,增加prototype的支持](#bean作用域,增加prototype的支持) - * [FactoryBean](#FactoryBean) - * [容器事件和事件监听器](#容器事件和事件监听器) -* [AOP](#AOP) - * [切点表达式](#切点表达式) - * [基于JDK的动态代理](#基于JDK的动态代理) - * [基于CGLIB的动态代理](#基于CGLIB的动态代理) - * [AOP代理工厂ProxyFactory](#AOP代理工厂ProxyFactory) - * [几种常用的Advice: BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice](#几种常用的Advice) - * [PointcutAdvisor:Pointcut和Advice的组合](#PointcutAdvisor:Pointcut和Advice的组合) - * [动态代理融入bean生命周期](#动态代理融入bean生命周期) - - -#### 扩展篇 -* [PropertyPlaceholderConfigurer](#PropertyPlaceholderConfigurer) -* [类型转换](#类型转换) -* [包扫描](#包扫描) -* [基于注解的依赖注入Autowired](#基于注解的依赖注入Autowired) - -#### 高级篇 -* [解决循环依赖问题](#解决循环依赖问题) - -## 使用方法 -每个功能点对应一个分支,切换到功能点对应的分支了解新增的功能,增量改动点在[changelog.md](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md)文件中描述。 - -## 参考 -- [《Spring源码深度解析》](https://book.douban.com/subject/25866350/) -- [《精通Spring 4.x》](https://book.douban.com/subject/26952826/) -- [tiny-spring](https://github.com/code4craft/tiny-spring) diff --git a/README_en.md b/README_en.md new file mode 100644 index 0000000..79175b2 --- /dev/null +++ b/README_en.md @@ -0,0 +1,71 @@ +# mini-spring +[![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/DerekYRC/mini-spring) +[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) +[![Stars](https://img.shields.io/github/stars/DerekYRC/mini-spring)](https://img.shields.io/github/stars/DerekYRC/mini-spring) +[![Forks](https://img.shields.io/github/forks/DerekYRC/mini-spring)](https://img.shields.io/github/forks/DerekYRC/mini-spring) + +**English | [简体中文](./README.md)** + +[**mini-spring-cloud**](https://github.com/DerekYRC/mini-spring-cloud) **(simplified version of the Spring Cloud framework)** + +## About + +The **mini-spring** is a simplified version of the Spring framework that will help you quickly get familiar with the Spring source code and grasp the core principles of Spring. The core logic of Spring is extracted, the code is extremely simplified, and the core functions of Spring, such as IoC and AOP, resource loaders, event listeners, type conversion, container extension points, bean life cycle and scope, and application context, are retained. + +If this project can help you, please **STAR the project, thank you!!!** + +## Contents +#### Basics +* [IoC](#Ioc) + * [Implement a simple container](#实现一个简单的容器) + * [BeanDefinition and BeanDefinitionRegistry](#BeanDefinition和BeanDefinitionRegistry) + * [Bean Instantiation Strategy](#Bean实例化策略InstantiationStrategy) + * [Populate bean with property values](#为bean填充属性) + * [Populate bean with bean](#为bean注入bean) + * [Resource and ResourceLoader](#资源和资源加载器) + * [Define the bean in the XML file](#在xml文件中定义bean) + * [Container extension mechanism:BeanFactoryPostProcess and BeanPostProcessor](#容器扩展机制BeanFactoryPostProcess和BeanPostProcessor) + * [ApplicationContext](#应用上下文ApplicationContext) + * [Init method and destroy method of bean](#bean的初始化和销毁方法) + * [Aware interface](#Aware接口) + * [Bean scope, added prototype support](#bean作用域,增加prototype的支持) + * [FactoryBean](#FactoryBean) + * [Event and event listener](#容器事件和事件监听器) +* [AOP](#AOP) + * [Pointcut expression](#切点表达式) + * [JDK-based dynamic proxy](#基于JDK的动态代理) + * [CGLIB-based dynamic proxy](#基于CGLIB的动态代理) + * [AOP ProxyFactory](#AOP代理工厂ProxyFactory) + * [Common Advice: BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice](#几种常用的Advice) + * [PointcutAdvisor:A combination of Pointcut and Advice](#PointcutAdvisor:Pointcut和Advice的组合) + * [Dynamic proxies are integrated into the bean lifecycle](#动态代理融入bean生命周期) + + +#### Expanding +* [PropertyPlaceholderConfigurer](#PropertyPlaceholderConfigurer) +* [Package scan](#包扫描) +* [Value annotation](#Value) +* [Autowired annotation](#Autowired) +* [Type conversion(first part)](#类型转换一) +* [Type conversion(second part)](#类型转换二) + +#### Advanced +* [Solve the problem of circular dependencies(first part): without proxy bean](#解决循环依赖问题一) +* [Solve the problem of circular dependencies(second part): with proxy bean](#解决循环依赖问题二) + +#### Bug fix +* [populate proxy bean with property values(discovered and fixed by kerwin89)](#没有为代理bean设置属性) + +## Usage +Each function point corresponds to a branch. Switch to the branch corresponding to the function point to see the new function. The incremental change point is described in the [changelog.md](https://github.com/DerekYRC/mini-spring/blob/main/changelog.md) file. + +## Contributing +Any contributions you make are greatly appreciated. + +## Contact +Please feel free to ask me any questions related to mini-spring and other technologies. My email is **15521077528@163.com**. + +## Reference +- [《Spring源码深度解析》](https://book.douban.com/subject/25866350/) +- [《精通Spring 4.x》](https://book.douban.com/subject/26952826/) +- [tiny-spring](https://github.com/code4craft/tiny-spring) diff --git a/assets/application-context-life-cycle.drawio b/assets/application-context-life-cycle.drawio new file mode 100644 index 0000000..b6f4432 --- /dev/null +++ b/assets/application-context-life-cycle.drawio @@ -0,0 +1 @@ +7ZnbcpswEIafRpfpmJMQl2CTtDPJTDqetkluOopRgBYjV8ixnaevJCQDPiRup3Zd11dGv1Yg7e4nwRo4/fH8iuFJdkMTUgC7l8yBMwC2bfegLX6ksqgVy0JaSVmeaK0RhvkL0WJPq9M8IVXHkFNa8HzSFUe0LMmIdzTMGJ11zZ5o0X3qBKdkTRiOcLGufskTntUqsv1Gf0/yNDNPtmBQ94yxMdYrqTKc0FlLcmLg9BmlvL4az/ukkN4zfqnHXW7pXU6MkZLvMuDl68tt+uFj+HmYk/wxCL2H5NMF0nPjC7Ngkoj16yZlPKMpLXERN2rE6LRMiLxrT7Qam2tKJ0K0hPiNcL7QwcRTToWU8XGhe8k853dy+DtPt+5bPYO5vrNqLEyj5GzRGiSb9+2+ZphqmXHrXtKOq+iUjcgrrjHZhllK+Ct2Op+l31oP0DG4InRMxHyEASMF5vlzN6+wTs90addEUFzoIP5CQPV9n3Ex1U8CNizE/KMnKjzQDjX8MaWm46JSwQqFgYUm86ZTXKX6V92lmuDSaHc31yCGIIAA+SB2QRSBCBpDMfu2bUuuJ2LklfRrkkvmwyzLORlOsArUTGwx3USqOKPfSZ8WlKnRDrIfHQjlmvKiaOmJR1Di6rWa7N5AkPEeYZzMX42m2eGQptvsb4b2WbNZWFBrWWujcHt7SoDgTPQbpL5JtHNURNt7IjoiuByQp7zMeU7LU+XT7fJp99b5XNochE/zYnMGdCt4bwIKjwpQZ4+AXuIRp2xxSyt+y+iIVJVARh61lyCM1eErroP/AuXl0WpQRusou+igKNtnlLf5xt0RZf+oUHb3iPIKwx5AAUADEPsgGiiYPRC6ALlSCXoAwRPleHkEa45dd51jxz4ox96Z422+8Xbk2HKPCmTvkCBLWuMzyHDDu/WBQbbWfHqkIP8+kHBHII+LR7gnHh8Fjwq4GASxejGOAYrU6dqXhakTJW+l6uTAv111spx/hbzDH6H+jsR6R0Wsvydi5UerePMNfRAjEFqC05phcVaG6tAU6CIQDBTVfgfmutYcBfIickDgnSreK0Urb0NR+cBFq319GKkteyDLGTIBXBCiE42ps1K9gBuqF3+oeCGazb+Kqq/156wT/wQ= \ No newline at end of file diff --git a/assets/auto-proxy.drawio b/assets/auto-proxy.drawio new file mode 100644 index 0000000..33bac5a --- /dev/null +++ b/assets/auto-proxy.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/assets/aware-interface.drawio b/assets/aware-interface.drawio new file mode 100644 index 0000000..2d088c9 --- /dev/null +++ b/assets/aware-interface.drawio @@ -0,0 +1 @@ +7Ztfk6I4EMA/TR69kv/wiMrsbtXOlnvW3e3ey1WUDOYWCQdx1Pn0l2AQEERnSpASnySdAKE7v3SnE4EyXm0/RTBcPhMX+UAeulugTIAsy6ZpsR8u2e0lkmTKe4kXYVfIMsEMvyEhHArpGrsoLjSkhPgUh0XhggQBWtCCDEYR2RSbvRC/+NYQeqgkmC2gX5b+hV263EtN2cjknxH2lumbJV188QqmjcWXxEvokk1OpDhAGUeE0P3VajtGPtdeqpf9fU8nag8di1BAL7nh7Z+3qfflu/3nDCM8t2ztb/ePgSn6RnfpByOXfb8okoguiUcC6DuZdBSRdeAi/tQhK2VtvhISMqHEhP8iSnfCmHBNCRMt6coXtWiL6Q9++2+aKP3M1Uy24slJYZcWAhrtcjfx4s98XXZbUkrvK2tJKC4m62iBalSTjjYYeYjWtBPjmest9wJhg0+IrBDrD2sQIR9S/FocV1AMT+/QLrMguxBGfIdBxXNfob8WbwKy7rP+j14I00De1Pp/a5JWDOLEWDZrIJnhNqtkV574TZ4ShzBIZT+evwJHB5YOTAM4KhiNwEhPG7Le59vmxPuOpOKj4ZcNLj4eNktM0SyEiaE2bIopDqSYRuQXGhOfRMndiinPFV3n34R9Pyd3NWS6qvjWdHRXEJRqD0UUbWuteZjhBN1iflNFcZPNFZIuZMvcPJG2u7r9rQfQZ0A9C7TSKaDlhoAeIRhM0AsOMMUkuFc81SKeklzm89CmFT7TuOYB6EnwzgKqdwpQpUFAn+CCkmg3JTGdRmSB4pghwz3tE7CdxPeya6sXKB/QFSjLwzLKqtkqyvID5VO6US9E2egUymqDKB8xrAG2NDYnwDHAaJLArAFbBabKJdYQmPqdcnzgNo2YK0JmRW6VY+3B8SndaBdyLKmdAllrE2ROq/MAWa9wyI2B7FMyCH69ehB6u+3Udp/jcJYule6d4yPGPga2Xga7Uqc3xVhvCOM5wzjh1AGWk8TTDjBHiVMe83TWnQJ7lKs6lG+WrJKUfhD7EUCNCz2vdivHq8fuHBuf4+/Ot/FU+ab//jbwBmWDXs9cOWNlpjtjLqlgrMx2bZirUkFSp+Iko6EJlqcm2PrGNoBjAlti0+p+ymURkZ2ERmymNYE1SSZhozD37jcURha/GCnA0u51Nj5KTWptpiYrh2b9XmBAgh7NvlJFAqNSad1KYEhNZTCSIGnC846cYRXY5p1iqRylGfWKIKmpLGPlAFP7SWWd8zwLpdypmKipLfkvPN8PffyGA48nJhgW8IWiaBqRkI13jOIZoneK6aCURqzgtLHsQ6WZ67OI1wZVAh+Ihofg+tFwHYDdTf/X9boB52nyEzK2neYc7MSdWjxEfk8kzDf4BuxTl8TtC9aafmusJamk1EYdcFdWuXXR7lkHbHWK65utci0eKjPHxNrZT8CUeru4NSowbndxe2azvl9xtHUhxulhpY5wbDXE8QTHIYnh3EcijHYRR2h3p3CWnKxRkXpq2cnWbwT0K3hOqetu7qm22+2Gz+ddrIC5Z4GzefPd+J7s7b13N/70LvsF62W1JcDrOtnkQVd7w5THOIkRzUm7y2wJ0ArzX3yARmlzrVtp4dsehGtt5XsVZC894XpTZJvaDrLD0McLyI+bj1l7tKU5csuVdwqwfsFRVvVKq1xWzP4qmtTl/nGrOP8D \ No newline at end of file diff --git a/assets/chainProceed.png b/assets/chainProceed.png new file mode 100644 index 0000000..bda72e3 Binary files /dev/null and b/assets/chainProceed.png differ diff --git a/assets/init-and-destroy-method.drawio b/assets/init-and-destroy-method.drawio new file mode 100644 index 0000000..dde0db2 --- /dev/null +++ b/assets/init-and-destroy-method.drawio @@ -0,0 +1 @@ +7Ztbs9o2EIB/jR7p4Lv8aINPkpkkQ8q0TfrSMVjHVmssaotw+fWVjIRtbC5JseEAT1gryUhafburFQBtMFu9S/159IkEKAZqP1gBbQhUVYXQZh9cst5KFAWqW0mY4kDICsEYb5AQ9oV0gQOUVRpSQmKK51XhlCQJmtKKzE9Tsqw2eyVx9VvnfohqgvHUj+vSP3BAo60UqlYhf49wGMlvVkwx45kvG4uZZJEfkGVJpHlAG6SE0O3TbDVAMV89uS7bfi8HancDS1FCz+mw+WszCj98cX4fY4QntmP8GfzWg2JsdC0njAI2f1EkKY1ISBI/9gqpm5JFEiD+1j4rFW0+EjJnQoUJ/0aUroUy/QUlTBTRWSxq0QrTr7z7L4YofSvVDFfizXlhLQsJTdelTrz4rVxXdMtLsl99lcTCZWSRTtGRpZG7zU9DRI+0E/uZr1vpC4QO3iEyQ2w8rEGKYp/i79V95YvtGe7aFRpkD0KJP6BQ8d7vfrwQ3wRUM2bjd18JW4Gyqs1/F0RW9LJcWQ5roMD5qqhkT6H4zN+Szf1Eyr5++gg8E9gmgBbwdOC6wDVlQzb6ctuSeDsQKd7bfsXm4vthGWGKxnM/V9SSmZjqRspoSv5BAxKTNO+tQXWimSafE47jkjwwEAx0MVe5uxsIkquHUopWR7W5s3CCbmnfJO3LwlgoppBFJUOh91vaAPaT6BOkniRauymi1ZaIdpGfDNErTjDFJLlXPvUqn2q/zueuTSd8ysDmCehB8E4Cat4UoFqLgL74U0rS9YhkdJSSKcoyhgx3tS/A8XLny57th0B551olyrCOsg47RVl9onxobfQzUbZuCmW9RZT3GDYAOxvDIfAs4A5zmA3g6ADqXGL3ATTvlOOdCxYc63qdY03tlGPjyfGhtTHO5FjRbwpko0uQOa3eE2SzIbbuGGSltqY3CvLPA2meCeRt8Wi2xOOE8ZgD5wHbywNjD0A3964Dnpi6U/L2sk6aee2sk6K9FfK6d6HWmcQa1yLWzIIJtt5nX7zPg5H22fx10wt7dYVeTl0lZRWqO6EupaKsQndH1bW3lhfTX+OKXdXAWi0ZWJ5jYAcVxwIeBI7CzOrW5LLQxsljHGZpIbCHuRG2KrZ3ezXg2vzB1YBt3Ks13ssxGg13AK3lGBt34vFbvYQkb9T6XgRnpSE10biK1jV5VtpKReRB0pAnEDnDOnDgnWKp7eULzYZ8YVvpwsb9pD+pPOE8z4BSvWZM1Nbl+geeuPdjvMFJyDMMDAv/laJ0lJI52+8YZWNEbxfTGpMNW+Agpr1aPrCB09bSCI1qPp4OvDSoCviJaLgPOoiGDwN4bmL/WqCqrTlPyH/r4jgy5+Dk7tTmIfKPRML8pq7HphqR4FGwNhpyFN1ifSI7eHEHfLOn3MPR7hkO2L4m11c75do8VGaOibVzXgBU3tLh9n9xvH+4tRow7vZwe+LW/cHjaPtMjJV+s9q74dhuieMhzuYk8ycxEmF0gDhC6zuFs+ZkrYbUU8dO9vhFwIMHz/LHfzeTezo6ym7D59MuVsD8YIEzbPFanRWLP6DkdaX/8Wjefw== \ No newline at end of file diff --git a/assets/populate-proxy-bean-with-property-values.drawio b/assets/populate-proxy-bean-with-property-values.drawio new file mode 100644 index 0000000..7f3e4e9 --- /dev/null +++ b/assets/populate-proxy-bean-with-property-values.drawio @@ -0,0 +1 @@ +7Vxbd5s4EP41evQeczU8go3b7ml63M3Z3l72YKNgUoIoKLGdX7+SEDEX+ZIUMLX9ZBgkLGbmmxmNRgLK+GH9LnHj5Q3yYAjkobcGygTIsmwYJvmhlA2nKJqUUfwk8DJagXAbPENOHHLqY+DBtNQQIxTiIC4TFyiK4AKXaG6SoFW52R0Ky/8auz6sEW4Xblinfg08vMyohjza0t/DwF/m/yzp/Isf3Lwx/5J06XpoVSApDlDGCUI4u3pYj2FIuZfzJes33fH0ZWAJjPAxHZ7/e575Hz5bX24DGMxNS/vh/Tsw+NjwJv9g6JHv57cowUvko8gNnS3VTtBj5EH61iG527b5iFBMiBIh3kOMN1yY7iNGhLTEDyF/CtcB/ka7/6Xxu++FJ5M1fzO72eQ3EU42hU709nvx2bYbu8v71bnEGZeix2QB97Am1zY38SHe007O2lG+Ff6Ay+AdRA+QjIc0SGDo4uCprFcuV0//pd1WguSCC/EVAuXvfXLDR/5PQNZDMn77DhEOFEWt/3pE+YNByoRlkQaSEa+3D8mVz3/ZW9LYjXLat5uPwNGBqQNjBBwV2DawdUoxDEJchG6aCp7zF5GvK76rQM4GmpMr6rlVPqovq2WA4W3sMkGuiAkqK1qKE/QTjlGIEtZbMeS5ouv0m4MwLNA9DRqeynmRa78AYTl3YYLheq+0cwuocvRz+zfI71dbY/LSZlkwJDmtcQUxr4jvDMm86wwFDHpcJxS9rBOSXJF1ZnJ4r4q4X4bxdg2QWzIRNnSjCbwLogAHKLoQQHeJZ5jcG8t/br9NUfzjyfmyNm1jnkvziue6oit1Dy5kodQrD660CM+pu8Ao2cxQimcJWsA0JYChnnkKLIf5anJtXgSQX6xu1QoXkKwaXXpm6QrlnbxRjwzGR72CstoilCsY1gCZahsT4IyAPWFg1oClAkOlFHMIDP1McSwPyzjWjTqOFblTHGtXHO/ijXYkjiW1V0DWugQyRatzBbIhCK1bA/JqJt3jX+poejOe/60s081z+HmgXwaOKxh7G7D1OrCFPFVOCWO9JRjPCYwZTh1gOiyedoBhM6c8pumvMwWsUQasLPC8kt6p51UuA7FvAejoSM+rncrx6qk3D0bv08/Op/FM+aT/8zzwB3WBNieugrC2ojsgLqkkrK3suhCXkEH9yl2MWjKwbFHBBNYIOAawJGJWM5NLIiKLhUYaW3OYMCM8KtnebAHCNumFrQBTO1drXElMjvQOM5PmwJHhwHknaf4iSZZffX0lDfIl3Ks13j2/KeJbzMRe4VtqK6PBgqYJzUNSTKvAMs4Upkol7WgK0o5tZR2FDkTdC9IIReeJyn3O9KDTlXsVI7W15P+B5v/dMHgOIp8mKggs3DsMk1mCYqLvAUxvIT5TmA6q2YiRAKetZSOEYt6fVWwaqBJ4Q3Q8BM1Hx/sA2N/lgH2jbsF5GrTCxrLyHITF3KlJQ+bXRMZ0wW9APnWJvEuBtTE8NawlqcbUVh1wX2a9QmYcO+s1T4VrYYx+LaJ8jUCFLOyXoT5ZGsOkcx8S/pN21hQY0sVmLyRROUZr6QuxYZavM6OqwT2cjhz2CshmS0CeBGmMUnceQj4x8iDF0OZM0VkLm6Rhl1UWYk3bv9ZzWfOhHHYH8dkvP5sPu9sZ0WEny9F8YXMhSRIsGbQG6hCjQfTzyXddf7OeWd5NGt8KlvsuMZIWsubY7UgnK5wSlnv8OWtAQrb9hqha3zbyUujY/LaRfQrYZl26tSLcJjYvhbhAPVP7Wytc7TIVJZTwaetW+5KYErLm2Przk1nffaNuHLJWHIfBwqW7Q8akPVzjAnLrD88UwPoRledqQykLNL+nu9vlYejOYZjLUj6UmZQLH8AVpv+euNXYal+RRW9mT+SWC/wVWeiTbux90/S4Det9rHy7yUKzrlaSuJtCg5hGaOnuQE/TqskWtXKyw6EOcrkDucjG8NZ4cB9TG3cu6QLFEDhTYI+BOT3adyzRw/wxPew3dnmH3f6kAVdRrfqRhoJgT1T2U43xG1uzamtHSxpEfgjx8dtGWYBQklACyZ/Q3CrHPbU62d+yx24Y+BG5XhBxQCIrm4qBBByhxR88BJ7HrBsHGvkbzQbapCJ8niEsy12YNmxCAap7/odmXQFMgQJUzwZoTAHa2gsRJwgjvCEQvipAQQHUmgUQLHB1qgCiRc5qABGGQZzCw0bVTePs8KW7YE251hMrK3doZYUnC1zzmTtZc2w+U+8o5j5OoO3VWqbYjXDAps1sMl3fGior8fbehncogaVuZzrNrlQGiDCtt1UYIMxpC+zkOabJXpsKF+f/6yjfjauTLVK0BGq2+KgDQ2YrjA4wx2zNcUL3c293dRt014PJKKZNmmUlQWz6Y4zZsuWYrV9qdLWSXGdLm3a2H3xKX0dfMaJPS72M/DgImxUcZResDpTuTh3mbQRFSf01JDWrIdDKnYbEGEoHLUlT+brjFG90GeFBI5ZEcJiTsJ16SkvS1tlNrw4PLLpR4w+JDn4L1JXoQBGUMHQbHVxIMXAjmBasqgnbaafEdFuLaK/GNN93tflCR5JeBp5VwSEw3eL5Qs5LbQTPgtOdWimQ+S2BtpX5ZuGzQ4Pr4tFNtsSOhNFpxE1C7PMEbfUkGE2Q22zqJBhyuz0zPVvL2h49rzj/Aw== \ No newline at end of file diff --git a/assets/populate-proxy-bean-with-property-values.png b/assets/populate-proxy-bean-with-property-values.png new file mode 100644 index 0000000..e27eac5 Binary files /dev/null and b/assets/populate-proxy-bean-with-property-values.png differ diff --git a/assets/prototype-bean.drawio b/assets/prototype-bean.drawio new file mode 100644 index 0000000..d93e34a --- /dev/null +++ b/assets/prototype-bean.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/changelog.md b/changelog.md index 3b42239..801c78d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,9 +1,9 @@ - # IOC - ## 最简单的bean容器 - > 分支:simple-bean-container + # [基础篇:IoC](#基础篇IoC) + ## [最简单的bean容器](#最简单的bean容器) + > 代码分支:simple-bean-container 定义一个简单的bean容器BeanFactory,内部包含一个map用以保存bean,只有注册bean和获取bean两个方法 -``` +```java public class BeanFactory { private Map beanMap = new HashMap<>(); @@ -18,7 +18,7 @@ public class BeanFactory { ``` 测试: -``` +```java public class SimpleBeanContainerTest { @Test @@ -39,20 +39,20 @@ public class SimpleBeanContainerTest { } ``` -## BeanDefinition和BeanDefinitionRegistry -> 分支:bean-definition-and-bean-definition-registry +## [BeanDefinition和BeanDefinitionRegistry](#BeanDefinition和BeanDefinitionRegistry) +> 代码分支:bean-definition-and-bean-definition-registry 主要增加如下类: -- BeanDefinition,顾名思义,用于定义bean信息的类,包含bean的class类型、构造参数、属性值等信息,每个bean对应一个BeanDefinition的实例。简化BeanDefition仅包含bean的class类型。 -- BeanDefinitionRegistry,BeanDefinition注册表接口,定义注册BeanDefintion的方法。 +- BeanDefinition,顾名思义,用于定义bean信息的类,包含bean的class类型、构造参数、属性值等信息,每个bean对应一个BeanDefinition的实例。简化BeanDefinition仅包含bean的class类型。 +- BeanDefinitionRegistry,BeanDefinition注册表接口,定义注册BeanDefinition的方法。 - SingletonBeanRegistry及其实现类DefaultSingletonBeanRegistry,定义添加和获取单例bean的方法。 -bean容器作为BeanDefinitionRegistry和SingletonBeanRegistry的实现类,具备两者的能力。向bean容器中注册BeanDefintion后,使用bean时才会实例化。 +bean容器作为BeanDefinitionRegistry和SingletonBeanRegistry的实现类,具备两者的能力。向bean容器中注册BeanDefinition后,使用bean时才会实例化。 ![](./assets/bean-definition-and-bean-definition-registry.png) 测试: -``` +```java public class BeanDefinitionAndBeanDefinitionRegistryTest { @Test @@ -74,8 +74,8 @@ class HelloService { } ``` -## Bean实例化策略InstantiationStrategy -> 分支:instantiation-strategy +## [Bean实例化策略InstantiationStrategy](#Bean实例化策略InstantiationStrategy) +> 代码分支:instantiation-strategy 现在bean是在AbstractAutowireCapableBeanFactory.doCreateBean方法中用beanClass.newInstance()来实例化,仅适用于bean有无参构造函数的情况。 @@ -85,13 +85,13 @@ class HelloService { - SimpleInstantiationStrategy,使用bean的构造函数来实例化 - CglibSubclassingInstantiationStrategy,使用CGLIB动态生成子类 -## 为bean填充属性 -> 分支:populate-bean-with-property-values +## [为bean填充属性](#为bean填充属性) +> 代码分支:populate-bean-with-property-values 在BeanDefinition中增加和bean属性对应的PropertyValues,实例化bean之后,为bean填充属性(AbstractAutowireCapableBeanFactory#applyPropertyValues)。 测试: -``` +```java public class PopulateBeanWithPropertyValuesTest { @Test @@ -111,12 +111,12 @@ public class PopulateBeanWithPropertyValuesTest { } ``` -## 为bean注入bean -> 分支:populate-bean-with-bean +## [为bean注入bean](#为bean注入bean) +> 代码分支:populate-bean-with-bean 增加BeanReference类,包装一个bean对另一个bean的引用。实例化beanA后填充属性时,若PropertyValue#value为BeanReference,引用beanB,则先去实例化beanB。 由于不想增加代码的复杂度提高理解难度,暂时不支持循环依赖,后面会在高级篇中解决该问题。 -``` +```java protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) { try { for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) { @@ -138,7 +138,7 @@ protected void applyPropertyValues(String beanName, Object bean, BeanDefinition ``` 测试: -``` +```java public class PopulateBeanWithPropertyValuesTest { /** @@ -176,8 +176,8 @@ public class PopulateBeanWithPropertyValuesTest { } ``` -## 资源和资源加载器 -> 分支:resource-and-resource-loader +## [资源和资源加载器](#资源和资源加载器) +> 代码分支:resource-and-resource-loader Resource是资源的抽象和访问接口,简单写了三个实现类 @@ -190,7 +190,7 @@ Resource是资源的抽象和访问接口,简单写了三个实现类 ResourceLoader接口则是资源查找定位策略的抽象,DefaultResourceLoader是其默认实现类 测试: -``` +```java public class ResourceAndResourceLoaderTest { @Test @@ -222,14 +222,14 @@ public class ResourceAndResourceLoaderTest { } ``` -## 在xml文件中定义bean -> 分支:xml-file-define-bean +## [在xml文件中定义bean](#在xml文件中定义bean) +> 代码分支:xml-file-define-bean 有了资源加载器,就可以在xml格式配置文件中声明式地定义bean的信息,资源加载器读取xml文件,解析出bean的信息,然后往容器中注册BeanDefinition。 BeanDefinitionReader是读取bean定义信息的抽象接口,XmlBeanDefinitionReader是从xml文件中读取的实现类。BeanDefinitionReader需要有获取资源的能力,且读取bean定义信息后需要往容器中注册BeanDefinition,因此BeanDefinitionReader的抽象实现类AbstractBeanDefinitionReader拥有ResourceLoader和BeanDefinitionRegistry两个属性。 -由于从xml文件中读取的内容是String类型,所以属性仅支持String类型和引用其他Bean。后面会讲到属性编辑器PropertyEditor,实现类型转换。 +由于从xml文件中读取的内容是String类型,所以属性仅支持String类型和引用其他Bean。后面会讲到类型转换器,实现类型转换。 为了方便后面的讲解和功能实现,并且尽量保持和spring中BeanFactory的继承层次一致,对BeanFactory的继承层次稍微做了调整。 @@ -237,7 +237,7 @@ BeanDefinitionReader是读取bean定义信息的抽象接口,XmlBeanDefinition 测试: bean定义文件spring.xml -``` +```xml ``` -``` +```java public class XmlFileDefineBeanTest { @Test @@ -280,10 +280,10 @@ public class XmlFileDefineBeanTest { } ``` -## BeanFactoryPostProcess和BeanPostProcessor -> 分支:bean-factory-post-processor-and-bean-post-processor +## [BeanFactoryPostProcessor和BeanPostProcessor](#BeanFactoryPostProcessor和BeanPostProcessor) +> 代码分支:bean-factory-post-processor-and-bean-post-processor -BeanFactoryPostProcess和BeanPostProcessor是spring框架中具有重量级地位的两个接口,理解了这两个接口的作用,基本就理解spring的核心原理了。为了降低理解难度分两个小节实现。 +BeanFactoryPostProcessor和BeanPostProcessor是spring框架中具有重量级地位的两个接口,理解了这两个接口的作用,基本就理解spring的核心原理了。为了降低理解难度分两个小节实现。 BeanFactoryPostProcessor是spring提供的容器扩展机制,允许我们在bean实例化之前修改bean的定义信息即BeanDefinition的信息。其重要的实现类有PropertyPlaceholderConfigurer和CustomEditorConfigurer,PropertyPlaceholderConfigurer的作用是用properties文件的配置值替换xml文件中的占位符,CustomEditorConfigurer的作用是实现类型转换。BeanFactoryPostProcessor的实现比较简单,看单元测试BeanFactoryPostProcessorAndBeanPostProcessorTest#testBeanFactoryPostProcessor追下代码。 @@ -291,7 +291,7 @@ BeanPostProcessor也是spring提供的容器扩展机制,不同于BeanFactoryP BeanPostProcessor的两个方法分别在bean执行初始化方法(后面实现)之前和之后执行,理解其实现重点看单元测试BeanFactoryPostProcessorAndBeanPostProcessorTest#testBeanPostProcessor和AbstractAutowireCapableBeanFactory#initializeBean方法,有些地方做了微调,可不必关注。 -``` +```java public interface BeanPostProcessor { /** * 在bean执行初始化方法之前执行此方法 @@ -308,7 +308,7 @@ public interface BeanPostProcessor { 下一节将引入ApplicationContext,能自动识别BeanFactoryPostProcessor和BeanPostProcessor,就可以在xml文件中配置而不需要手动添加到BeanFactory了。 测试: -``` +```java public class BeanFactoryProcessorAndBeanPostProcessorTest { @Test @@ -345,14 +345,14 @@ public class BeanFactoryProcessorAndBeanPostProcessorTest { } ``` -## 应用上下文ApplicationContext -> 分支:application-context +## [应用上下文ApplicationContext](#应用上下文ApplicationContext) +> 代码分支:application-context 应用上下文ApplicationContext是spring中较之于BeanFactory更为先进的IOC容器,ApplicationContext除了拥有BeanFactory的所有功能外,还支持特殊类型bean如上一节中的BeanFactoryPostProcessor和BeanPostProcessor的自动识别、资源加载、容器事件和监听器、国际化支持、单例bean自动初始化等。 BeanFactory是spring的基础设施,面向spring本身;而ApplicationContext面向spring的使用者,应用场合使用ApplicationContext。 -具体实现查看AbstractApplicationContext#refresh方法即可。注意BeanFactoryPostProcessor和BeanPostProcessor的自定识别,这样就可以在xml文件中配置二者而不需要像上一节一样手动添加到容器中了。 +具体实现查看AbstractApplicationContext#refresh方法即可。注意BeanFactoryPostProcessor和BeanPostProcessor的自动识别,这样就可以在xml文件中配置二者而不需要像上一节一样手动添加到容器中了。 从bean的角度看,目前生命周期如下: @@ -360,8 +360,8 @@ BeanFactory是spring的基础设施,面向spring本身;而ApplicationContext 测试:见ApplicationContextTest -## bean的初始化和销毁方法 -> 分支:init-and-destroy-method +## [bean的初始化和销毁方法](#bean的初始化和销毁方法) +> 代码分支:init-and-destroy-method 在spring中,定义bean的初始化和销毁方法有三种方法: - 在xml文件中制定init-method和destroy-method @@ -382,7 +382,7 @@ BeanFactory是spring的基础设施,面向spring本身;而ApplicationContext 测试: init-and-destroy-method.xml -``` +```xml ``` -``` + +```java public class Person implements InitializingBean, DisposableBean { private String name; @@ -433,7 +434,8 @@ public class Person implements InitializingBean, DisposableBean { //setter and getter } ``` -``` + +```java public class InitAndDestoryMethodTest { @Test @@ -444,8 +446,8 @@ public class InitAndDestoryMethodTest { } ``` -## Aware接口 -> 分支:aware-interface +## [Aware接口](#Aware接口) +> 代码分支:aware-interface Aware是感知、意识的意思,Aware接口是标记性接口,其实现子类能感知容器相关的对象。常用的Aware接口有BeanFactoryAware和ApplicationContextAware,分别能让其实现者感知所属的BeanFactory和ApplicationContext。 @@ -461,7 +463,7 @@ Aware是感知、意识的意思,Aware接口是标记性接口,其实现子 测试: spring.xml -``` +```xml ``` -``` + +```java public class HelloService implements ApplicationContextAware, BeanFactoryAware { private ApplicationContext applicationContext; @@ -501,7 +504,8 @@ public class HelloService implements ApplicationContextAware, BeanFactoryAware { } } ``` -``` + +```java public class AwareInterfaceTest { @Test @@ -514,10 +518,10 @@ public class AwareInterfaceTest { } ``` -## bean作用域,增加prototype的支持 -> 分支:prototype-bean +## [bean作用域,增加prototype的支持](#bean作用域增加prototype的支持) +> 代码分支:prototype-bean -每次向容器获取prototype作用域bean时,容器都会创建一个新的实例。在BeanDefinition中增加描述bean的作用域的字段scope/singleton/prototype,创建prototype作用域bean时(AbstractAutowireCapableBeanFactory#doCreateBean),不往singletonObjects中增加该bean。prototype作用域bean不执行销毁方法,查看AbstractAutowireCapableBeanFactory#registerDisposableBeanIfNecessary方法。 +每次向容器获取prototype作用域bean时,容器都会创建一个新的实例。在BeanDefinition中增加描述bean的作用域的字段scope,创建prototype作用域bean时(AbstractAutowireCapableBeanFactory#doCreateBean),不往singletonObjects中增加该bean。prototype作用域bean不执行销毁方法,查看AbstractAutowireCapableBeanFactory#registerDisposableBeanIfNecessary方法。 至止,bean的生命周期如下: @@ -525,7 +529,7 @@ public class AwareInterfaceTest { 测试: prototype-bean.xml -``` +```xml ``` -``` + +```java public class PrototypeBeanTest { @Test @@ -555,8 +560,8 @@ public class PrototypeBeanTest { } ``` -## FactoryBean -> 分支:factory-bean +## [FactoryBean](#FactoryBean) +> 代码分支:factory-bean FactoryBean是一种特殊的bean,当向容器获取该bean时,容器不是返回其本身,而是返回其FactoryBean#getObject方法的返回值,可通过编码方式定义复杂的bean。 @@ -564,7 +569,7 @@ FactoryBean是一种特殊的bean,当向容器获取该bean时,容器不是 测试: factory-bean.xml -``` +```xml ``` -``` + +```java public class CarFactoryBean implements FactoryBean { private String brand; @@ -602,7 +608,8 @@ public class CarFactoryBean implements FactoryBean { } } ``` -``` + +```java public class FactoryBeanTest { @Test @@ -616,16 +623,17 @@ public class FactoryBeanTest { } ``` -## 容器事件和事件监听器 -> 分支:event-and-event-listener +## [容器事件和事件监听器](#容器事件和事件监听器) + +> 代码分支:event-and-event-listener -ApplicationContext容器提供了完善的时间发布和时间监听功能。 +ApplicationContext容器提供了完善的事件发布和事件监听功能。 ApplicationEventMulticaster接口是注册监听器和发布事件的抽象接口,AbstractApplicationContext包含其实现类实例作为其属性,使得ApplicationContext容器具有注册监听器和发布事件的能力。在AbstractApplicationContext#refresh方法中,会实例化ApplicationEventMulticaster、注册监听器并发布容器刷新事件ContextRefreshedEvent;在AbstractApplicationContext#doClose方法中,发布容器关闭事件ContextClosedEvent。 测试: event-and-event-listener.xml -``` +```xml ``` -``` + +```java public class EventAndEventListenerTest { @Test @@ -654,6 +663,7 @@ public class EventAndEventListenerTest { } } ``` + 观察输出: ``` org.springframework.test.common.event.ContextRefreshedEventListener @@ -661,15 +671,17 @@ org.springframework.test.common.event.CustomEventListener org.springframework.test.common.event.ContextClosedEventListener ``` -## 切点表达式 -> 分支:pointcut-expression +# [基础篇:AOP](#基础篇AOP) + +## [切点表达式](#切点表达式) +> 代码分支:pointcut-expression Joinpoint,织入点,指需要执行代理操作的某个类的某个方法(仅支持方法级别的JoinPoint);Pointcut是JoinPoint的表述方式,能捕获JoinPoint。 最常用的切点表达式是AspectJ的切点表达式。需要匹配类,定义ClassFilter接口;匹配方法,定义MethodMatcher接口。PointCut需要同时匹配类和方法,包含ClassFilter和MethodMatcher,AspectJExpressionPointcut是支持AspectJ切点表达式的PointCut实现,简单实现仅支持execution函数。 测试: -``` +```java public class HelloService { public String sayHello() { System.out.println("hello"); @@ -677,7 +689,8 @@ public class HelloService { } } ``` -``` + +```java public class PointcutExpressionTest { @Test @@ -692,13 +705,13 @@ public class PointcutExpressionTest { } ``` -## 基于JDK的动态代理 -> 分支:jdk-dynamic-proxy +## [基于JDK的动态代理](#基于JDK的动态代理) +> 代码分支:jdk-dynamic-proxy AopProxy是获取代理对象的抽象接口,JdkDynamicAopProxy的基于JDK动态代理的具体实现。TargetSource,被代理对象的封装。MethodInterceptor,方法拦截器,是AOP Alliance的"公民",顾名思义,可以拦截方法,可在被代理执行的方法前后增加代理行为。 测试; -``` +```java public class DynamicProxyTest { @Test @@ -719,13 +732,13 @@ public class DynamicProxyTest { } ``` -## 基于CGLIB的动态代理 -> 分支:cglib-dynamic-proxy +## [基于CGLIB的动态代理](#基于CGLIB的动态代理) +> 代码分支:cglib-dynamic-proxy 基于CGLIB的动态代理实现逻辑也比较简单,查看CglibAopProxy。与基于JDK的动态代理在运行期间为接口生成对象的代理对象不同,基于CGLIB的动态代理能在运行期间动态构建字节码的class文件,为类生成子类,因此被代理类不需要继承自任何接口。 测试: -``` +```java public class DynamicProxyTest { private AdvisedSupport advisedSupport; @@ -751,13 +764,13 @@ public class DynamicProxyTest { } ``` -## AOP代理工厂 -> 分支:proxy-factory +## [AOP代理工厂](#AOP代理工厂) +> 代码分支:proxy-factory 增加AOP代理工厂ProxyFactory,由AdvisedSupport#proxyTargetClass属性决定使用JDK动态代理还是CGLIB动态代理。 测试: -``` +```java public class DynamicProxyTest { private AdvisedSupport advisedSupport; @@ -790,8 +803,8 @@ public class DynamicProxyTest { } ``` -## 几种常用的Advice:BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice... -> 分支: common-advice +## [几种常用的Advice:BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice...](#几种常用的AdviceBeforeAdviceAfterAdviceAfterReturningAdviceThrowsAdvice) +> 代码分支: common-advice Spring将AOP联盟中的Advice细化出各种类型的Advice,常用的有BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice,我们可以通过扩展MethodInterceptor来实现。 @@ -802,7 +815,7 @@ Spring将AOP联盟中的Advice细化出各种类型的Advice,常用的有Befor - [ ] ThrowsAdvice 测试: -``` +```java public class WorldServiceBeforeAdvice implements MethodBeforeAdvice { @Override @@ -811,7 +824,8 @@ public class WorldServiceBeforeAdvice implements MethodBeforeAdvice { } } ``` -``` + +```java public class DynamicProxyTest { private AdvisedSupport advisedSupport; @@ -840,13 +854,13 @@ public class DynamicProxyTest { } ``` -## PointcutAdvisor:Pointcut和Advice的组合 -> 分支:pointcut-advisor +## [PointcutAdvisor:Pointcut和Advice的组合](#PointcutAdvisorPointcut和Advice的组合) +> 代码分支:pointcut-advisor Advisor是包含一个Pointcut和一个Advice的组合,Pointcut用于捕获JoinPoint,Advice决定在JoinPoint执行某种操作。实现了一个支持aspectj表达式的AspectJExpressionPointcutAdvisor。 测试: -``` +```java public class DynamicProxyTest { @Test @@ -876,8 +890,9 @@ public class DynamicProxyTest { } ``` -## 动态代理融入bean生命周期 -> 分支:auto-proxy +## [动态代理融入bean生命周期](#动态代理融入bean生命周期) + +> 代码分支:auto-proxy 结合前面讲解的bean的生命周期,BeanPostProcessor处理阶段可以修改和替换bean,正好可以在此阶段返回代理对象替换原对象。不过我们引入一种特殊的BeanPostProcessor——InstantiationAwareBeanPostProcessor,如果InstantiationAwareBeanPostProcessor处理阶段返回代理对象,会导致短路,不会继续走原来的创建bean的流程,具体实现查看AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation。 @@ -889,7 +904,7 @@ DefaultAdvisorAutoProxyCreator是处理横切逻辑的织入返回代理对象 测试: auto-proxy.xml -``` +```xml ``` -``` +```java public class AutoProxyTest { @Test @@ -931,8 +946,10 @@ public class AutoProxyTest { } ``` -## PropertyPlaceholderConfigurer -> 分支:property-placeholder-configurer +# [扩展篇](#扩展篇) + +## [PropertyPlaceholderConfigurer](#PropertyPlaceholderConfigurer) +> 代码分支:property-placeholder-configurer 经常需要将配置信息配置在properties文件中,然后在XML文件中以占位符的方式引用。 @@ -940,10 +957,11 @@ public class AutoProxyTest { 测试: car.properties -``` +```properties brand=lamborghini ``` -``` + +```xml ``` -``` + +```java public class PropertyPlaceholderConfigurerTest { @Test @@ -976,24 +995,934 @@ public class PropertyPlaceholderConfigurerTest { } ``` +## [包扫描](#包扫描) +> 代码分支:package-scan + +结合bean的生命周期,包扫描只不过是扫描特定注解的类,提取类的相关信息组装成BeanDefinition注册到容器中。 + +在XmlBeanDefinitionReader中解析``````标签,扫描类组装BeanDefinition然后注册到容器中的操作在ClassPathBeanDefinitionScanner#doScan中实现。 + +测试: +```java +@Component +public class Car { + +} +``` + +package-scan.xml +```xml + + + + + + +``` + +```java +public class PackageScanTest { + + @Test + public void testScanPackage() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:package-scan.xml"); + + Car car = applicationContext.getBean("car", Car.class); + assertThat(car).isNotNull(); + } +} +``` + +## [@Value注解](#Value注解) +> 代码分支:value-annotation + +注解@Value和@Autowired通过BeanPostProcessor处理。InstantiationAwareBeanPostProcessor增加postProcessPropertyValues方法,在bean实例化之后设置属性之前执行,查看AbstractAutowireCapableBeanFactory#doCreateBean方法。 + +增加AutowiredAnnotationBeanPostProcessor用于处理注解@Value,@Autowired的处理在下一节实现,在ClassPathBeanDefinitionScanner#doScan将其添加到容器中。查看AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues,其中字符解析器StringValueResolver在PropertyPlaceholderConfigurer中添加到BeanFactory中。 + +测试: +```java +@Component +public class Car { + + @Value("${brand}") + private String brand; +} +``` + +value-annotation.xml +```xml + + + + + + + + + + +``` + +car.properties +```properties +brand=lamborghini +``` + +```java +public class ValueAnnotationTest { + + @Test + public void testValueAnnotation() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:value-annotation.xml"); + + Car car = applicationContext.getBean("car", Car.class); + assertThat(car.getBrand()).isEqualTo("lamborghini"); + } +} +``` + +## [@Autowired注解](#Autowired注解) +> 代码分支:autowired-annotation + +@Autowired注解的处理见AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues + +测试: +```java +@Component +public class Car { + +} + +@Component +public class Person implements InitializingBean, DisposableBean { + + @Autowired + private Car car; +} +``` +autowired-annotation.xml +```xml + + + + + + +``` + +```java +public class AutowiredAnnotationTest { + + @Test + public void testAutowiredAnnotation() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:autowired-annotation.xml"); + + Person person = applicationContext.getBean(Person.class); + assertThat(person.getCar()).isNotNull(); + } +} +``` +## [bug fix:没有为代理bean设置属性(discovered and fixed by @kerwin89)](#bug-fix没有为代理bean设置属性discovered-and-fixed-by-kerwin89) +> 代码分支: populate-proxy-bean-with-property-values + +问题现象:没有为代理bean设置属性 + +问题原因:织入逻辑在InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation中执行,而该方法如果返回非null,会导致"短路",不会执行后面的设置属性逻辑。因此如果该方法中返回代理bean后,不会为代理bean设置属性。 + +修复方案:跟spring保持一致,将织入逻辑迁移到BeanPostProcessor#postProcessAfterInitialization,即将DefaultAdvisorAutoProxyCreator#postProcessBeforeInstantiation的内容迁移到DefaultAdvisorAutoProxyCreator#postProcessAfterInitialization中。 + +顺便完善spring的扩展机制,为InstantiationAwareBeanPostProcessor增加postProcessAfterInstantiation方法,该方法在bean实例化之后设置属性之前执行。 + +至此,bean的生命周期比较完整了,如下: + +![](./assets/populate-proxy-bean-with-property-values.png) + +测试: +populate-proxy-bean-with-property-values.xml +```xml + + + + + + + + + + + + + + + + + + + + + + +``` + +```java +public class WorldServiceImpl implements WorldService { + + private String name; + + @Override + public void explode() { + System.out.println("The " + name + " is going to explode"); + } + + //setter and getter +} +``` + +```java +public class AutoProxyTest { + + @Test + public void testPopulateProxyBeanWithPropertyValues() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:populate-proxy-bean-with-property-values.xml"); + + //获取代理对象 + WorldService worldService = applicationContext.getBean("worldService", WorldService.class); + worldService.explode(); + assertThat(worldService.getName()).isEqualTo("earth"); + } +} +``` + +## [类型转换(一)](#类型转换一) +> 代码分支:type-conversion-first-part + +spring在org.springframework.core.convert.converter包中定义了三种类型转换器接口:Converter、ConverterFactory、GenericConverter。 + +### 一、Converter +```java +public interface Converter { + + /** + * 类型转换 + */ + T convert(S source); +} +``` +Converter能将S类型的对象转换为T类型的对象,比如将String类型的对象转换为Integer类型的对象的实现类: +```java +public class StringToIntegerConverter implements Converter { + @Override + public Integer convert(String source) { + return Integer.valueOf(source); + } +} +``` +使用: +```java +Integer num = new StringToIntegerConverter().convert("8888"); +``` + +### 二、ConverterFactory +```java +public interface ConverterFactory { + + Converter getConverter(Class targetType); +} +``` +Converter接口适合一对一的类型转换,如果要将String类型转换为Ineger/Long/Float/Double/Decimal等类型,就要实现一系列的StringToInteger/StringToLongConverter/StringToFloatConverter转换器,非常不优雅。 + +ConverterFactory接口则适合一对多的类型转换,可以将一种类型转换为另一种类型及其子类。比如将String类型转换为Ineger/Long/Float/Double/Decimal等Number类型时,只需定义一个ConverterFactory转换器: +```java +public class StringToNumberConverterFactory implements ConverterFactory { + + @Override + public Converter getConverter(Class targetType) { + return new StringToNumber(targetType); + } + + private static final class StringToNumber implements Converter { + + private final Class targetType; + + public StringToNumber(Class targetType) { + this.targetType = targetType; + } + + @Override + public T convert(String source) { + if (source.length() == 0) { + return null; + } + + if (targetType.equals(Integer.class)) { + return (T) Integer.valueOf(source); + } else if (targetType.equals(Long.class)) { + return (T) Long.valueOf(source); + } + //TODO 其他数字类型 + + else { + throw new IllegalArgumentException( + "Cannot convert String [" + source + "] to target class [" + targetType.getName() + "]"); + } + } + } + +} +``` +使用: +```java +StringToNumberConverterFactory converterFactory = new StringToNumberConverterFactory(); +Converter stringToIntegerConverter = converterFactory.getConverter(Integer.class); +Integer num = stringToIntegerConverter.convert("8888"); +``` +### 三、GenericConverter +```java +public interface GenericConverter { + Set getConvertibleTypes(); + Object convert(Object source, Class sourceType, Class targetType); +} +``` +String类型转换为Boolean类型的实现类: +```java +public class StringToBooleanConverter implements GenericConverter { + @Override + public Set getConvertibleTypes() { + return Collections.singleton(new ConvertiblePair(String.class, Boolean.class)); + } + @Override + public Object convert(Object source, Class sourceType, Class targetType) { + return Boolean.valueOf((String) source); + } +} +``` +使用: +```java +Boolean flag = new StringToBooleanConverter().convert("true", String.class, Boolean.class); +``` +ConversionService是类型转换体系的核心接口,将以上三种类型转换器整合到一起,GenericConversionService是其实现类,DefaultConversionService在GenericConversionService的基础上添加内置转换器。 +测试见TypeConversionFirstPartTest。 +## [类型转换(二)](#类型转换二) +> 代码分支:type-conversion-second-part +上一节实现了spring中的类型转换体系,本节将类型转换的能力整合到容器中。 +为了方便使用,提供了创建ConversionService的FactoryBean——ConversionServiceFactoryBean。 +如果有定义ConversionService,在AbstractApplicationContext#finishBeanFactoryInitialization方法中设置到容器中。 +类型转换的时机有两个: +- 为bean填充属性时,见AbstractAutowireCapableBeanFactory#applyPropertyValues +- 处理@Value注解时,见AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues +你可能会有疑问,如果没有定义ConversionService,是怎么进行基本类型的转换的?其实spring为了向下兼容保留了一套比较旧的类型转换机制,没有定义ConversionService时会使用其进行基本类型的转换工作,不必关注旧的类型转换机制。 +测试: +```java +public class Car { + private int price; + private LocalDate produceDate; +} +``` +```java +public class StringToLocalDateConverter implements Converter { + private final DateTimeFormatter DATE_TIME_FORMATTER; + public StringToLocalDateConverter(String pattern) { + DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(pattern); + } + @Override + public LocalDate convert(String source) { + return LocalDate.parse(source, DATE_TIME_FORMATTER); + } +} +``` + +type-conversion-second-part.xml +```xml + + + + + + + + + + + + + + + +``` + +```java +public class TypeConversionSecondPartTest { + + @Test + public void testConversionService() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:type-conversion-second-part.xml"); + + Car car = applicationContext.getBean("car", Car.class); + assertThat(car.getPrice()).isEqualTo(1000000); + assertThat(car.getProduceDate()).isEqualTo(LocalDate.of(2021, 1, 1)); + } +} +``` + +# [高级篇](#高级篇) + +## [解决循环依赖问题(一):没有代理对象](#解决循环依赖问题一没有代理对象) +> 代码分支:circular-reference-without-proxy-bean + +虽然放在高级篇,其实解决循环依赖问题的方法非常简单。 + +先理解spring中为什么会有循环依赖的问题。比如如下的代码 + +```java +public class A { + + private B b; + + //getter and setter +} +``` + +```java +public class B { + + private A a; + + //getter and setter +} +``` + +```xml + + + + + + + + +``` + +A依赖B,B又依赖A,循环依赖。容器加载时会执行依赖流程: + +- 实例化A,发现依赖B,然后实例化B +- 实例化B,发现依赖A,然后实例化A +- 实例化A,发现依赖B,然后实例化B +- ... + +死循环直至栈溢出。 + +解决该问题的关键在于何时将实例化后的bean放进容器中,设置属性前还是设置属性后。现有的执行流程,bean实例化后并且设置属性后会被放进singletonObjects单例缓存中。如果我们调整一下顺序,当bean实例化后就放进singletonObjects单例缓存中,提前暴露引用,然后再设置属性,就能解决上面的循环依赖问题,执行流程变为: + +- 步骤一:getBean(a),检查singletonObjects是否包含a,singletonObjects不包含a,实例化A放进singletonObjects,设置属性b,发现依赖B,尝试getBean(b) +- 步骤二:getBean(b),检查singletonObjects是否包含b,singletonObjects不包含b,实例化B放进singletonObjects,设置属性a,发现依赖A,尝试getBean(a) +- 步骤三:getBean(a),检查singletonObjects是否包含a,singletonObjects包含a,返回a +- 步骤四:步骤二中的b拿到a,设置属性a,然后返回b +- 步骤五:步骤一中的a拿到b,设置属性b,然后返回a + +可见调整bean放进singletonObjects(人称一级缓存)的时机到bean实例化后即可解决循环依赖问题。但为了和spring保持一致,我们增加一个二级缓存earlySingletonObjects,在bean实例化后将bean放进earlySingletonObjects中(见AbstractAutowireCapableBeanFactory#doCreateBean方法第6行),getBean()时检查一级缓存singletonObjects和二级缓存earlySingletonObjects中是否包含该bean,包含则直接返回(见AbstractBeanFactory#getBean第1行)。 + +单测见CircularReferenceWithoutProxyBeanTest#testCircularReference。 + +增加二级缓存,不能解决有代理对象时的循环依赖。原因是放进二级缓存earlySingletonObjects中的bean是实例化后的bean,而放进一级缓存singletonObjects中的bean是代理对象(代理对象在BeanPostProcessor#postProcessAfterInitialization中返回),两个缓存中的bean不一致。比如上面的例子,如果A被代理,那么B拿到的a是实例化后的A,而a是被代理后的对象,即b.getA() != a,见单测CircularReferenceWithProxyBeanTest。 + +下一节填坑。 + +## [解决循环依赖问题(二):有代理对象](#解决循环依赖问题二有代理对象) +> 代码分支:circular-reference-with-proxy-bean + +解决有代理对象时的循环依赖问题,需要提前暴露代理对象的引用,而不是暴露实例化后的bean的引用(这是上节的遗留问题的原因,应该提前暴露A的代理对象的引用)。 + +spring中用singletonFactories(一般称第三级缓存)解决有代理对象时的循环依赖问题。在实例化后提前暴露代理对象的引用(见AbstractAutowireCapableBeanFactory#doCreateBean方法第6行)。 + +getBean()时依次检查一级缓存singletonObjects、二级缓存earlySingletonObjects和三级缓存singletonFactories中是否包含该bean。如果三级缓存中包含该bean,则挪至二级缓存中,然后直接返回该bean。见AbstractBeanFactory#getBean方法第1行。 + +最后将代理bean放进一级缓存singletonObjects,见AbstractAutowireCapableBeanFactory第104行。 + +单测见CircularReferenceWithProxyBeanTest。 + +## [支持懒加载和多切面增强(By @zqczgl)](#支持懒加载和多切面增强by-zqczgl) + +### [懒加载](#懒加载) + +> 代码分支: lazy-init-and-multi-advice + +事实上,并不是所有的bean在初始化容器的时候都会创建。随着项目规模的不断扩大,bean的数目也越来越多。如果每次启动容器都需要加载大量的bean,这无疑会带来大量的资源浪费。所有spring提供了懒加载机制,我们可以将我们认为暂时用不到的bean设为懒加载,这样只有在我们需要这个bean的时候这个bean才会被创建。 + +测试 + +lazy-test.xml + +```java +//只有当bean是单例且不为懒加载才会被创建 +public void preInstantiateSingletons() throws BeansException { + beanDefinitionMap.forEach((beanName, beanDefinition) -> { + if(beanDefinition.isSingleton()&&!beanDefinition.isLazyInit()){ + getBean(beanName); + } + }); + } +``` + +```java +public class LazyInitTest { + @Test + public void testLazyInit() throws InterruptedException { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:lazy-test.xml"); + System.out.println(System.currentTimeMillis()+":applicationContext-over"); + TimeUnit.SECONDS.sleep(1); + Car c= (Car) applicationContext.getBean("car"); + c.showTime();//显示bean的创建时间 + } +} +``` + +```xml + + + + + + + +``` + +关闭懒加载的输出: + +``` +1671698959957:applicationContext-over +1671698959951:bean create +``` + +开启懒加载: + +``` +1671699030293:applicationContext-over +1671699031328:bean create +``` + +可以清楚的看到开启和不开启懒加载bean的创建时机的差异 + +### [多个切面匹配同一方法](#多个切面匹配同一方法) + +> 代码分支: lazy-init-and-multi-advice + +虽然在前面我们完成了对方法的增强,但并不完美。我们的目前的代码只能支持对方法的单个增强。作为spring的核心功能如果不支持多切面的话有点太别扭了。spring利用了拦截器链来完成了对多个切面的支持。 + +#### [ProxyFactory](#ProxyFactory) + +让我们从ProxyFactory开始,来看一下代理对象的整个创建流程。至于为什么从ProxyFactory开,这是因为代理对象最终是用ProxyFactory的getProxy()函数来获得的。 + +```java +public class ProxyFactory extends AdvisedSupport{ + + + public ProxyFactory() { + } + + public Object getProxy() { + return createAopProxy().getProxy(); + } + + private AopProxy createAopProxy() { + if (this.isProxyTargetClass()||this.getTargetSource().getTargetClass().length==0) { + return new CglibAopProxy(this); + } + return new JdkDynamicAopProxy(this); + } +} +``` + +为了更贴合spring的实现,这里更改了ProxyFactory使其继承了AdvisedSupport,正如spring源码中做的那样。 + +#### [基于JDK动态代理](#基于JDK动态代理) + +ProxyFactory只是简单的做了下选择,当我们设置proxyTargetClass属性或者被代理对象没有接口时会调用cjlib动态代理,否则调用jdk动态代理。二者实现并没有太大区别,这里只贴出jdk动态代理的实现。 + +```java + public Object getProxy() { + return Proxy.newProxyInstance(getClass().getClassLoader(), advised.getTargetSource().getTargetClass(), this); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + // 获取目标对象 + Object target=advised.getTargetSource().getTarget(); + Class targetClass = target.getClass(); + Object retVal = null; + // 获取拦截器链 + List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); + if(chain==null||chain.isEmpty()){ + return method.invoke(target, args); + }else{ + // 将拦截器统一封装成ReflectiveMethodInvocation + MethodInvocation invocation = + new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); + // Proceed to the joinpoint through the interceptor chain. + // 执行拦截器链 + retVal = invocation.proceed(); + } + return retVal; + } +``` + +jdk动态代理可以分为获取拦截器链,将拦截器统一封装成ReflectiveMethodInvocation,执行拦截器链三部分。我们来逐一看一下这三部分。 + +##### [1.获取拦截器链](#1.获取拦截器链) + +首先将获取到所有与当前method匹配的advice(增强),跟踪getInterceptorsAndDynamicInterceptionAdvice代码,我们发现Spring AOP也使用缓存进行提高性能,如果该方法已经获取过拦截器,则直接取缓存,否则通过advisorChainFactory获取拦截器链。AdvisorChainFactory是用来获得拦截器链接口。它的一个实现类为DefaultAdvisorChainFactory + +AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice: + +```java + public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) { + Integer cacheKey=method.hashCode(); + List cached = this.methodCache.get(cacheKey); + if (cached == null) { + cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( + this, method, targetClass); + this.methodCache.put(cacheKey, cached); + } + return cached; + } +``` + +整体代码并不复杂,首先获取所有Advisor(切面),通过pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)校验当前代理对象是否匹配该Advisor,再通过pointcutAdvisor.getPointcut().getMethodMatcher()校验是否匹配当前调用method。如果通过校验,则提取advisor中的interceptors增强,添加到interceptorList中。这里可能有读者会疑惑,我们明明是要获取MethodInterceptor,可AdvisedSupport的getAdvice()返回的是Advice(增强),其实如果我们点开MethodInterceptor的源码,我们会发现MethodInterceptor继承了Interceptor接口,而Interceptor又继承了Advice接口。因为这里的Advice和MethodInterceptor我们都是用的AOP联盟的接口,所以特此说明一下。 + +DefultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice + +```java +public List getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport config, Method method, Class targetClass) { + Advisor[] advisors = config.getAdvisors().toArray(new Advisor[0]); + List interceptorList = new ArrayList<>(advisors.length); + Class actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); + for (Advisor advisor : advisors) { + if (advisor instanceof PointcutAdvisor) { + // Add it conditionally. + PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; + // 校验当前Advisor是否适用于当前对象 + if (pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { + MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); + boolean match; + // 校验Advisor是否应用到当前方法上 + match = mm.matches(method,actualClass); + if (match) { + MethodInterceptor interceptor = (MethodInterceptor) advisor.getAdvice(); + interceptorList.add(interceptor); + } + } + } + } + return interceptorList; + } +``` + +##### [2.将拦截器封装成ReflectiveMethodInvocation](#2.将拦截器封装成ReflectiveMethodInvocation) + +这里也是重写了ReflectiveMethodInvocation的实现,来支持多切面。 + +```java + public ReflectiveMethodInvocation(Object proxy,Object target, Method method, Object[] arguments,Class targetClass,List chain) { + this.proxy=proxy; + this.target = target; + this.method = method; + this.arguments = arguments; + this.targetClass=targetClass; + this.interceptorsAndDynamicMethodMatchers=chain; + } +``` + + + +##### [3.执行拦截器链](#3.执行拦截器链) + +spring能够保证多个切面同时匹配同一方法的而不出现乱序的关键就在下面一段代码了。 + +ReflectiveMethodInvocation#proceed() + +```java + public Object proceed() throws Throwable { + // 初始currentInterceptorIndex为-1,每调用一次proceed就把currentInterceptorIndex+1 + if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { + // 当调用次数 = 拦截器个数时 + // 触发当前method方法 + return method.invoke(this.target, this.arguments); + } + + Object interceptorOrInterceptionAdvice = + this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); + // 普通拦截器,直接触发拦截器invoke方法 + return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); + } +``` + +我们看到,MethodInvocation只是简单的将拦截器链的所有拦截器一一执行,最后再触发当前的method方法。这是很简单高效的方法,但问题是我们希望某些增强比如AfterReturningAdvice能够在方法执行完才被执行,这就涉及到不同增强的执行顺序的问题了。而MethodInvocation显然没有考虑顺序的问题,一个AfterReturningAdvice很可能在BeforeAdvice之前被调用。那么该如何保证顺序问题呢? + +答案是,控制增强的调用顺序其实由每个拦截器负责,所以我们需要分析`MethodBeforeAdviceInterceptor`和`AfterReturningAdviceInterceptor` + +```java +public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice { + + private MethodBeforeAdvice advice; + + public MethodBeforeAdviceInterceptor() { + } + + public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { + this.advice = advice; + } + + public void setAdvice(MethodBeforeAdvice advice) { + this.advice = advice; + } + + @Override + public Object invoke(MethodInvocation mi) throws Throwable { + this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); + return mi.proceed(); + } +} +``` + +```java +package org.springframework.aop.framework.adapter; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.AfterAdvice; +import org.springframework.aop.AfterReturningAdvice; + +/** + * @author zqc + * @date 2022/12/20 + */ +public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice { + + private AfterReturningAdvice advice; + + public AfterReturningAdviceInterceptor() { + } + + public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) { + this.advice = advice; + } + + + @Override + public Object invoke(MethodInvocation mi) throws Throwable { + Object retVal = mi.proceed(); + this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); + return retVal; + } +} + +``` + +看了源码大家应该就清楚了,拦截器链执行的顺序正时在各个拦截器的`invoke`方法中实现的。`before`会先执行`advice`增强方法再链式调用,这个比较好理解而`after`则是先执行链式调用,再调用`advice`增强方法,也就是一个递归的过程。和二叉树的遍历有些异曲同工之处。 + +![](./assets/chainProceed.png) + +#### [测试](#测试) + +!!!!!!!注意,使用过高版本的java可以因为java版本和cjlib冲突导致报错。建议使用java8进行测试 + +```java +public class WorldServiceImpl implements WorldService { + + private String name; + + @Override + public void explode() { + System.out.println("The " + name + " is going to explode"); + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} +``` + +前置增强: + +```java +public class WorldServiceBeforeAdvice implements MethodBeforeAdvice { + + @Override + public void before(Method method, Object[] args, Object target) throws Throwable { + System.out.println("BeforeAdvice: do something before the earth explodes"); + } +} +``` + +后置返回增强: + +```java +public class WorldServiceAfterReturnAdvice implements AfterReturningAdvice { + @Override + public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { + System.out.println("AfterAdvice: do something after the earth explodes"); + } +} +``` + +测试代码: + +```java +public class ProxyFactoryTest { + @Test + public void testAdvisor() throws Exception { + WorldService worldService = new WorldServiceImpl(); + + //Advisor是Pointcut和Advice的组合 + String expression = "execution(* org.springframework.test.service.WorldService.explode(..))"; + //第一个切面 + AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor(); + advisor.setExpression(expression); + MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(new WorldServiceBeforeAdvice()); + advisor.setAdvice(methodInterceptor); + //第二个切面 + AspectJExpressionPointcutAdvisor advisor1=new AspectJExpressionPointcutAdvisor(); + advisor1.setExpression(expression); + AfterReturningAdviceInterceptor afterReturningAdviceInterceptor=new AfterReturningAdviceInterceptor(new WorldServiceAfterReturnAdvice()); + advisor1.setAdvice(afterReturningAdviceInterceptor); + //通过ProxyFactory来获得代理 + ProxyFactory factory = new ProxyFactory(); + TargetSource targetSource = new TargetSource(worldService); + factory.setTargetSource(targetSource); + factory.setProxyTargetClass(true); + factory.addAdvisor(advisor); + factory.addAdvisor(advisor1); + WorldService proxy = (WorldService) factory.getProxy(); + proxy.explode(); + } +} +``` + +输出: + +``` +BeforeAdvice: do something before the earth explodes +The null is going to explode +AfterAdvice: do something after the earth explodes + +进程已结束,退出代码为 0 +``` + +#### [多切面动态代理融入bean生命周期](#多切面动态代理融入bean生命周期) + +```java + public void testAutoProxy() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:auto-proxy.xml"); + + //获取代理对象 + WorldService worldService = applicationContext.getBean("worldService", WorldService.class); + worldService.explode(); + } +``` + +auto-proxy.xml: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +输出: + +``` +BeforeAdvice: do something before the earth explodes +The null is going to explode +AfterAdvice: do something after the earth explodes + +进程已结束,退出代码为 0 +``` +至此,我们已经解决多切面匹配同一方法的问题。 diff --git a/pom.xml b/pom.xml index 4260b7b..be8be75 100644 --- a/pom.xml +++ b/pom.xml @@ -62,5 +62,11 @@ test + + cglib + cglib + 3.3.0 + + diff --git a/src/main/java/org/springframework/aop/AdvisedSupport.java b/src/main/java/org/springframework/aop/AdvisedSupport.java index 37bb76a..de926ba 100644 --- a/src/main/java/org/springframework/aop/AdvisedSupport.java +++ b/src/main/java/org/springframework/aop/AdvisedSupport.java @@ -1,22 +1,38 @@ package org.springframework.aop; import org.aopalliance.intercept.MethodInterceptor; +import org.springframework.aop.framework.AdvisorChainFactory; +import org.springframework.aop.framework.DefaultAdvisorChainFactory; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** - * @author derekyi - * @date 2020/12/6 + * @author zqc + * @date 2022/12/16 */ public class AdvisedSupport { //是否使用cglib代理 - private boolean proxyTargetClass = false; + private boolean proxyTargetClass = true; private TargetSource targetSource; - private MethodInterceptor methodInterceptor; private MethodMatcher methodMatcher; + private transient Map> methodCache; + + AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory(); + + private List advisors = new ArrayList<>(); + + public AdvisedSupport() { + this.methodCache = new ConcurrentHashMap<>(32); + } public boolean isProxyTargetClass() { return proxyTargetClass; } @@ -25,6 +41,14 @@ public void setProxyTargetClass(boolean proxyTargetClass) { this.proxyTargetClass = proxyTargetClass; } + public void addAdvisor(Advisor advisor) { + advisors.add(advisor); + } + + public List getAdvisors() { + return advisors; + } + public TargetSource getTargetSource() { return targetSource; } @@ -33,13 +57,6 @@ public void setTargetSource(TargetSource targetSource) { this.targetSource = targetSource; } - public MethodInterceptor getMethodInterceptor() { - return methodInterceptor; - } - - public void setMethodInterceptor(MethodInterceptor methodInterceptor) { - this.methodInterceptor = methodInterceptor; - } public MethodMatcher getMethodMatcher() { return methodMatcher; @@ -48,4 +65,17 @@ public MethodMatcher getMethodMatcher() { public void setMethodMatcher(MethodMatcher methodMatcher) { this.methodMatcher = methodMatcher; } + /** + * 用来返回方法的拦截器链 + */ + public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) { + Integer cacheKey=method.hashCode(); + List cached = this.methodCache.get(cacheKey); + if (cached == null) { + cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( + this, method, targetClass); + this.methodCache.put(cacheKey, cached); + } + return cached; + } } diff --git a/src/main/java/org/springframework/aop/AfterAdvice.java b/src/main/java/org/springframework/aop/AfterAdvice.java new file mode 100644 index 0000000..1598270 --- /dev/null +++ b/src/main/java/org/springframework/aop/AfterAdvice.java @@ -0,0 +1,12 @@ +package org.springframework.aop; + +import org.aopalliance.aop.Advice; + +/** + * 后置增强 + * + * @author zqc + * @date 2022/12/16 + */ +public interface AfterAdvice extends Advice { +} diff --git a/src/main/java/org/springframework/aop/AfterReturningAdvice.java b/src/main/java/org/springframework/aop/AfterReturningAdvice.java new file mode 100644 index 0000000..bdb6e8a --- /dev/null +++ b/src/main/java/org/springframework/aop/AfterReturningAdvice.java @@ -0,0 +1,14 @@ +package org.springframework.aop; + +import java.lang.reflect.Method; + +/** + * 后置增强 + * + * @author zqc + * @date 2022/12/16 + */ +public interface AfterReturningAdvice extends AfterAdvice { + + void afterReturning( Object returnValue, Method method, Object[] args, Object target) throws Throwable; +} diff --git a/src/main/java/org/springframework/aop/framework/AdvisorChainFactory.java b/src/main/java/org/springframework/aop/framework/AdvisorChainFactory.java new file mode 100644 index 0000000..cb88dd0 --- /dev/null +++ b/src/main/java/org/springframework/aop/framework/AdvisorChainFactory.java @@ -0,0 +1,17 @@ +package org.springframework.aop.framework; + +import org.springframework.aop.AdvisedSupport; + +import java.lang.reflect.Method; +import java.util.List; + +/** + * @author zqc + * @date 2022/12/16 + */ +public interface AdvisorChainFactory { + + + List getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport config, Method method, Class targetClass); + +} diff --git a/src/main/java/org/springframework/aop/framework/CglibAopProxy.java b/src/main/java/org/springframework/aop/framework/CglibAopProxy.java index 20efa77..21bbf56 100644 --- a/src/main/java/org/springframework/aop/framework/CglibAopProxy.java +++ b/src/main/java/org/springframework/aop/framework/CglibAopProxy.java @@ -1,17 +1,19 @@ package org.springframework.aop.framework; +import java.lang.reflect.Method; +import java.util.List; + import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; -import org.springframework.aop.AdvisedSupport; -import java.lang.reflect.Method; +import org.springframework.aop.AdvisedSupport; /** - * cgli动态代理 + * cglib动态代理 * - * @author derekyi - * @date 2020/12/6 + * @author zqc + * @date 2022/12/17 */ public class CglibAopProxy implements AopProxy { @@ -24,6 +26,7 @@ public CglibAopProxy(AdvisedSupport advised) { @Override public Object getProxy() { + // 创建动态代理增强类 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass()); enhancer.setInterfaces(advised.getTargetSource().getTargetClass()); @@ -43,13 +46,20 @@ private DynamicAdvisedInterceptor(AdvisedSupport advised) { } @Override - public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { - CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy); - if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { + public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { + // 获取目标对象 + Object target = advised.getTargetSource().getTarget(); + Class targetClass = target.getClass(); + Object retVal = null; + List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); + CglibMethodInvocation methodInvocation = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy); + if (chain == null || chain.isEmpty()) { //代理方法 - return advised.getMethodInterceptor().invoke(methodInvocation); + retVal = methodProxy.invoke(target, args); + } else { + retVal = methodInvocation.proceed(); } - return methodInvocation.proceed(); + return retVal; } } @@ -57,14 +67,16 @@ private static class CglibMethodInvocation extends ReflectiveMethodInvocation { private final MethodProxy methodProxy; - public CglibMethodInvocation(Object target, Method method, Object[] arguments, MethodProxy methodProxy) { - super(target, method, arguments); + public CglibMethodInvocation(Object proxy, Object target, Method method, + Object[] arguments, Class targetClass, + List interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) { + super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers); this.methodProxy = methodProxy; } @Override public Object proceed() throws Throwable { - return this.methodProxy.invoke(this.target, this.arguments); + return super.proceed(); } } } diff --git a/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java b/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java new file mode 100644 index 0000000..8d00a95 --- /dev/null +++ b/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java @@ -0,0 +1,44 @@ +package org.springframework.aop.framework; + +import org.aopalliance.intercept.MethodInterceptor; + +import org.springframework.aop.AdvisedSupport; +import org.springframework.aop.Advisor; +import org.springframework.aop.MethodMatcher; +import org.springframework.aop.PointcutAdvisor; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * @author zqc + * @date 2022/12/17 + */ +public class DefaultAdvisorChainFactory implements AdvisorChainFactory { + + @Override + public List getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport config, Method method, Class targetClass) { + Advisor[] advisors = config.getAdvisors().toArray(new Advisor[0]); + List interceptorList = new ArrayList<>(advisors.length); + Class actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); + for (Advisor advisor : advisors) { + if (advisor instanceof PointcutAdvisor) { + // Add it conditionally. + PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; + // 校验当前Advisor是否适用于当前对象 + if (pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { + MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); + boolean match; + // 校验Advisor是否应用到当前方法上 + match = mm.matches(method, actualClass); + if (match) { + MethodInterceptor interceptor = (MethodInterceptor) advisor.getAdvice(); + interceptorList.add(interceptor); + } + } + } + } + return interceptorList; + } +} diff --git a/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java b/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java index 27972cc..2f8e7e5 100644 --- a/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java +++ b/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java @@ -1,17 +1,19 @@ package org.springframework.aop.framework; -import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + import org.springframework.aop.AdvisedSupport; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.List; /** * JDK动态代理 * - * @author derekyi - * @date 2020/12/5 + * @author zqc + * @date 2022/12/19 */ public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { @@ -33,11 +35,22 @@ public Object getProxy() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { - //代理方法 - MethodInterceptor methodInterceptor = advised.getMethodInterceptor(); - return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args)); + // 获取目标对象 + Object target = advised.getTargetSource().getTarget(); + Class targetClass = target.getClass(); + Object retVal = null; + // 获取拦截器链 + List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); + if (chain == null || chain.isEmpty()) { + return method.invoke(target, args); + } else { + // 将拦截器统一封装成ReflectiveMethodInvocation + MethodInvocation invocation = + new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); + // Proceed to the joinpoint through the interceptor chain. + // 执行拦截器链 + retVal = invocation.proceed(); } - return method.invoke(advised.getTargetSource().getTarget(), args); + return retVal; } } diff --git a/src/main/java/org/springframework/aop/framework/ProxyFactory.java b/src/main/java/org/springframework/aop/framework/ProxyFactory.java index 6df930e..f0cc3a3 100644 --- a/src/main/java/org/springframework/aop/framework/ProxyFactory.java +++ b/src/main/java/org/springframework/aop/framework/ProxyFactory.java @@ -3,15 +3,13 @@ import org.springframework.aop.AdvisedSupport; /** - * @author derekyi - * @date 2020/12/6 + * @author zqc + * @date 2022/12/16 */ -public class ProxyFactory { +public class ProxyFactory extends AdvisedSupport { - private AdvisedSupport advisedSupport; - public ProxyFactory(AdvisedSupport advisedSupport) { - this.advisedSupport = advisedSupport; + public ProxyFactory() { } public Object getProxy() { @@ -19,10 +17,10 @@ public Object getProxy() { } private AopProxy createAopProxy() { - if (advisedSupport.isProxyTargetClass()) { - return new CglibAopProxy(advisedSupport); + if (this.isProxyTargetClass() || this.getTargetSource().getTargetClass().length == 0) { + return new CglibAopProxy(this); } - return new JdkDynamicAopProxy(advisedSupport); + return new JdkDynamicAopProxy(this); } } diff --git a/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java b/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java index 97b2121..9a5da41 100644 --- a/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java +++ b/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java @@ -1,31 +1,54 @@ package org.springframework.aop.framework; +import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Method; +import java.util.List; /** - * @author derekyi - * @date 2020/12/6 + * @author zqc + * @date 2022/12/16 */ public class ReflectiveMethodInvocation implements MethodInvocation { + protected final Object proxy; + protected final Object target; protected final Method method; protected final Object[] arguments; - public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments) { + protected final Class targetClass; + + protected final List interceptorsAndDynamicMethodMatchers; + + private int currentInterceptorIndex = -1; + + public ReflectiveMethodInvocation(Object proxy,Object target, Method method, Object[] arguments,Class targetClass,List chain) { + this.proxy=proxy; this.target = target; this.method = method; this.arguments = arguments; + this.targetClass=targetClass; + this.interceptorsAndDynamicMethodMatchers=chain; } @Override public Object proceed() throws Throwable { - return method.invoke(target, arguments); + // 初始currentInterceptorIndex为-1,每调用一次proceed就把currentInterceptorIndex+1 + if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { + // 当调用次数 = 拦截器个数时 + // 触发当前method方法 + return method.invoke(this.target, this.arguments); + } + + Object interceptorOrInterceptionAdvice = + this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); + // 普通拦截器,直接触发拦截器invoke方法 + return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } @Override diff --git a/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java b/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java new file mode 100644 index 0000000..12d4709 --- /dev/null +++ b/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java @@ -0,0 +1,33 @@ +package org.springframework.aop.framework.adapter; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.aop.AfterAdvice; +import org.springframework.aop.AfterReturningAdvice; + +/** + * 后置增强拦截器 + * + * @author zqc + * @date 2022/12/20 + */ +public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice { + + private AfterReturningAdvice advice; + + public AfterReturningAdviceInterceptor() { + } + + public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) { + this.advice = advice; + } + + + @Override + public Object invoke(MethodInvocation mi) throws Throwable { + Object retVal = mi.proceed(); + this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); + return retVal; + } +} diff --git a/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java b/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java index 2b3e365..58b057b 100644 --- a/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java +++ b/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java @@ -2,13 +2,14 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.BeforeAdvice; import org.springframework.aop.MethodBeforeAdvice; /** * @author derekyi * @date 2020/12/6 */ -public class MethodBeforeAdviceInterceptor implements MethodInterceptor { +public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice { private MethodBeforeAdvice advice; @@ -24,9 +25,9 @@ public void setAdvice(MethodBeforeAdvice advice) { } @Override - public Object invoke(MethodInvocation invocation) throws Throwable { + public Object invoke(MethodInvocation mi) throws Throwable { //在执行被代理方法之前,先执行before advice操作 - this.advice.before(invocation.getMethod(), invocation.getArguments(), invocation.getThis()); - return invocation.proceed(); + this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); + return mi.proceed(); } } diff --git a/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java b/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java index 4153838..e66a272 100644 --- a/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java +++ b/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java @@ -1,19 +1,24 @@ package org.springframework.aop.framework.autoproxy; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + import org.aopalliance.aop.Advice; -import org.aopalliance.intercept.MethodInterceptor; -import org.springframework.aop.*; + +import org.springframework.aop.Advisor; +import org.springframework.aop.ClassFilter; +import org.springframework.aop.Pointcut; +import org.springframework.aop.TargetSource; import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import java.util.Collection; - /** * @author derekyi * @date 2020/12/6 @@ -22,35 +27,49 @@ public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPos private DefaultListableBeanFactory beanFactory; + private Set earlyProxyReferences = new HashSet<>(); + @Override - public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (!earlyProxyReferences.contains(beanName)) { + return wrapIfNecessary(bean, beanName); + } + + return bean; + } + + @Override + public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { + earlyProxyReferences.add(beanName); + return wrapIfNecessary(bean, beanName); + } + + protected Object wrapIfNecessary(Object bean, String beanName) { //避免死循环 - if (isInfrastructureClass(beanClass)) { - return null; + if (isInfrastructureClass(bean.getClass())) { + return bean; } - Collection advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values(); + Collection advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class) + .values(); try { + ProxyFactory proxyFactory = new ProxyFactory(); for (AspectJExpressionPointcutAdvisor advisor : advisors) { ClassFilter classFilter = advisor.getPointcut().getClassFilter(); - if (classFilter.matches(beanClass)) { - AdvisedSupport advisedSupport = new AdvisedSupport(); - - BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); - Object bean = beanFactory.getInstantiationStrategy().instantiate(beanDefinition); + if (classFilter.matches(bean.getClass())) { TargetSource targetSource = new TargetSource(bean); - advisedSupport.setTargetSource(targetSource); - advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice()); - advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher()); - - //返回代理对象 - return new ProxyFactory(advisedSupport).getProxy(); + proxyFactory.setTargetSource(targetSource); + proxyFactory.addAdvisor(advisor); + proxyFactory.setMethodMatcher(advisor.getPointcut().getMethodMatcher()); } } + if (!proxyFactory.getAdvisors().isEmpty()) { + return proxyFactory.getProxy(); + } } catch (Exception ex) { throw new BeansException("Error create proxy bean for: " + beanName, ex); } - return null; + return bean; } private boolean isInfrastructureClass(Class beanClass) { @@ -64,13 +83,23 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = (DefaultListableBeanFactory) beanFactory; } + @Override + public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + return null; + } + + @Override + public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { + return true; + } + @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - return bean; + public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException { + return pvs; } } diff --git a/src/main/java/org/springframework/beans/factory/BeanFactory.java b/src/main/java/org/springframework/beans/factory/BeanFactory.java index b3b1c45..ca2384a 100644 --- a/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -29,4 +29,8 @@ public interface BeanFactory { * @throws BeansException */ T getBean(String name, Class requiredType) throws BeansException; + + T getBean(Class requiredType) throws BeansException; + + boolean containsBean(String name); } diff --git a/src/main/java/org/springframework/beans/factory/ObjectFactory.java b/src/main/java/org/springframework/beans/factory/ObjectFactory.java new file mode 100644 index 0000000..8c52b3e --- /dev/null +++ b/src/main/java/org/springframework/beans/factory/ObjectFactory.java @@ -0,0 +1,12 @@ +package org.springframework.beans.factory; + +import org.springframework.beans.BeansException; + +/** + * @author derekyi + * @date 2021/1/30 + */ +public interface ObjectFactory { + + T getObject() throws BeansException; +} \ No newline at end of file diff --git a/src/main/java/org/springframework/beans/factory/PropertyPlaceholderConfigurer.java b/src/main/java/org/springframework/beans/factory/PropertyPlaceholderConfigurer.java index 63fbd30..3ff7fa7 100644 --- a/src/main/java/org/springframework/beans/factory/PropertyPlaceholderConfigurer.java +++ b/src/main/java/org/springframework/beans/factory/PropertyPlaceholderConfigurer.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; +import org.springframework.util.StringValueResolver; import java.io.IOException; import java.util.Properties; @@ -30,6 +31,10 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) //属性值替换占位符 processProperties(beanFactory, properties); + + //往容器中添加字符解析器,供解析@Value注解使用 + StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(properties); + beanFactory.addEmbeddedValueResolver(valueResolver); } /** @@ -69,22 +74,43 @@ private void resolvePropertyValues(BeanDefinition beanDefinition, Properties pro for (PropertyValue propertyValue : propertyValues.getPropertyValues()) { Object value = propertyValue.getValue(); if (value instanceof String) { - //TODO 仅简单支持一个占位符的格式 - String strVal = (String) value; - StringBuffer buf = new StringBuffer(strVal); - int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX); - int endIndex = strVal.indexOf(PLACEHOLDER_SUFFIX); - if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) { - String propKey = strVal.substring(startIndex + 2, endIndex); - String propVal = properties.getProperty(propKey); - buf.replace(startIndex, endIndex + 1, propVal); - propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), buf.toString())); - } + //改进前后进行对比 + String strVal=(String) value; + String res = resolvePlaceholder(strVal, properties); + if(!strVal.equals(res)) + propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), res)); } } } + private String resolvePlaceholder(String value, Properties properties) { + //TODO 仅简单支持一个占位符的格式 + String strVal = value; + StringBuffer buf = new StringBuffer(strVal); + int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX); + int endIndex = strVal.indexOf(PLACEHOLDER_SUFFIX); + if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) { + String propKey = strVal.substring(startIndex + 2, endIndex); + String propVal = properties.getProperty(propKey); + buf.replace(startIndex, endIndex + 1, propVal); + } + return buf.toString(); + } + public void setLocation(String location) { this.location = location; } + + private class PlaceholderResolvingStringValueResolver implements StringValueResolver { + + private final Properties properties; + + public PlaceholderResolvingStringValueResolver(Properties properties) { + this.properties = properties; + } + + public String resolveStringValue(String strVal) throws BeansException { + return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties); + } + } } diff --git a/src/main/java/org/springframework/beans/factory/annotation/Autowired.java b/src/main/java/org/springframework/beans/factory/annotation/Autowired.java new file mode 100644 index 0000000..42c9203 --- /dev/null +++ b/src/main/java/org/springframework/beans/factory/annotation/Autowired.java @@ -0,0 +1,16 @@ +package org.springframework.beans.factory.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author derekyi + * @date 2020/12/27 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD}) +public @interface Autowired { + +} \ No newline at end of file diff --git a/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java new file mode 100644 index 0000000..5b28520 --- /dev/null +++ b/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -0,0 +1,95 @@ +package org.springframework.beans.factory.annotation; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.TypeUtil; +import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValues; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; +import org.springframework.core.convert.ConversionService; + +import java.lang.reflect.Field; + +/** + * 处理@Autowired和@Value注解的BeanPostProcessor + * + * @author derekyi + * @date 2020/12/27 + */ +public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware { + + private ConfigurableListableBeanFactory beanFactory; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + } + + @Override + public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException { + //处理@Value注解 + Class clazz = bean.getClass(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + Value valueAnnotation = field.getAnnotation(Value.class); + if (valueAnnotation != null) { + Object value = valueAnnotation.value(); + value = beanFactory.resolveEmbeddedValue((String) value); + + //类型转换 + Class sourceType = value.getClass(); + Class targetType = (Class) TypeUtil.getType(field); + ConversionService conversionService = beanFactory.getConversionService(); + if (conversionService != null) { + if (conversionService.canConvert(sourceType, targetType)) { + value = conversionService.convert(value, targetType); + } + } + + BeanUtil.setFieldValue(bean, field.getName(), value); + } + } + + //处理@Autowired注解 + for (Field field : fields) { + Autowired autowiredAnnotation = field.getAnnotation(Autowired.class); + if (autowiredAnnotation != null) { + Class fieldType = field.getType(); + String dependentBeanName = null; + Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class); + Object dependentBean = null; + if (qualifierAnnotation != null) { + dependentBeanName = qualifierAnnotation.value(); + dependentBean = beanFactory.getBean(dependentBeanName, fieldType); + } else { + dependentBean = beanFactory.getBean(fieldType); + } + BeanUtil.setFieldValue(bean, field.getName(), dependentBean); + } + } + + return pvs; + } + + @Override + public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + return null; + } + + @Override + public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { + return true; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return null; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return null; + } +} diff --git a/src/main/java/org/springframework/beans/factory/annotation/Qualifier.java b/src/main/java/org/springframework/beans/factory/annotation/Qualifier.java new file mode 100644 index 0000000..ca8c487 --- /dev/null +++ b/src/main/java/org/springframework/beans/factory/annotation/Qualifier.java @@ -0,0 +1,17 @@ +package org.springframework.beans.factory.annotation; + +import java.lang.annotation.*; + +/** + * @author derekyi + * @date 2020/12/27 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Inherited +@Documented +public @interface Qualifier { + + String value() default ""; + +} \ No newline at end of file diff --git a/src/main/java/org/springframework/beans/factory/annotation/Value.java b/src/main/java/org/springframework/beans/factory/annotation/Value.java new file mode 100644 index 0000000..f585874 --- /dev/null +++ b/src/main/java/org/springframework/beans/factory/annotation/Value.java @@ -0,0 +1,17 @@ +package org.springframework.beans.factory.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author derekyi + * @date 2020/12/27 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +public @interface Value { + + String value(); +} \ No newline at end of file diff --git a/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java b/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java index 66a08c8..2440451 100644 --- a/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java +++ b/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java @@ -2,6 +2,8 @@ import org.springframework.beans.PropertyValues; +import java.util.Objects; + /** * BeanDefinition实例保存bean的信息,包括class类型、方法构造参数、bean属性、bean的scope等,此处简化只包含class类型和bean属性 * @@ -14,20 +16,38 @@ public class BeanDefinition { public static String SCOPE_PROTOTYPE = "prototype"; - private Class beanClass; - private PropertyValues propertyValues; + /** + * bean class类 + */ + private Class beanClass; + /** + * class 属性值 + */ + private PropertyValues propertyValues; + /** + * 通过反射 初始化方法名称 + */ private String initMethodName; - + /** + * 销毁方法名称 + */ private String destroyMethodName; - + /** + * 作用域 默认单例Bean + */ private String scope = SCOPE_SINGLETON; private boolean singleton = true; private boolean prototype = false; + /*/* + 懒加载 + */ + private boolean lazyInit=false; + public BeanDefinition(Class beanClass) { this(beanClass, null); } @@ -82,4 +102,25 @@ public String getDestroyMethodName() { public void setDestroyMethodName(String destroyMethodName) { this.destroyMethodName = destroyMethodName; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BeanDefinition that = (BeanDefinition) o; + return beanClass.equals(that.beanClass); + } + + @Override + public int hashCode() { + return Objects.hash(beanClass); + } + + public void setLazyInit(boolean b){ + lazyInit=b; + } + + public boolean isLazyInit(){ + return lazyInit; + } } diff --git a/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java b/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java index 1f7b0c2..939125d 100644 --- a/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java +++ b/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java @@ -1,6 +1,8 @@ package org.springframework.beans.factory.config; import org.springframework.beans.factory.HierarchicalBeanFactory; +import org.springframework.core.convert.ConversionService; +import org.springframework.util.StringValueResolver; /** * @author derekyi @@ -17,4 +19,13 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single * 销毁单例bean */ void destroySingletons(); + + void addEmbeddedValueResolver(StringValueResolver valueResolver); + + String resolveEmbeddedValue(String value); + + void setConversionService(ConversionService conversionService); + + ConversionService getConversionService(); + } diff --git a/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java b/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java index 6025cbc..77fdaff 100644 --- a/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java +++ b/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java @@ -1,6 +1,7 @@ package org.springframework.beans.factory.config; import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValues; /** * @author derekyi @@ -17,4 +18,39 @@ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { * @throws BeansException */ Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException; + + /** + * bean实例化之后,设置属性之前执行 + * + * @param bean + * @param beanName + * @return + * @throws BeansException + */ + boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException; + + /** + * bean实例化之后,设置属性之前执行 + * + * @param pvs + * @param bean + * @param beanName + * @return + * @throws BeansException + */ + PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) + throws BeansException; + + /** + * 提前暴露bean + * + * @param bean + * @param beanName + * @return + * @throws BeansException + */ + default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { + return bean; + } + } diff --git a/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index dcd6910..3c3946b 100644 --- a/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -3,12 +3,16 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.TypeUtil; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValue; +import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.*; +import org.springframework.core.convert.ConversionService; import java.lang.reflect.Method; @@ -61,13 +65,32 @@ protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, Str } protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) { - Object bean = null; + Object bean; try { bean = createBeanInstance(beanDefinition); + + //为解决循环依赖问题,将实例化后的bean放进缓存中提前暴露 + if (beanDefinition.isSingleton()) { + Object finalBean = bean; + addSingletonFactory(beanName, new ObjectFactory() { + @Override + public Object getObject() throws BeansException { + return getEarlyBeanReference(beanName, beanDefinition, finalBean); + } + }); + } + + //实例化bean之后执行 + boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean); + if (!continueWithPropertyPopulation) { + return bean; + } + //在设置bean属性之前,允许BeanPostProcessor修改属性值 + applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition); //为bean填充属性 applyPropertyValues(beanName, bean, beanDefinition); //执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法 - initializeBean(beanName, bean, beanDefinition); + bean = initializeBean(beanName, bean, beanDefinition); } catch (Exception e) { throw new BeansException("Instantiation of bean failed", e); } @@ -75,10 +98,67 @@ protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) { //注册有销毁方法的bean registerDisposableBeanIfNecessary(beanName, bean, beanDefinition); + Object exposedObject = bean; if (beanDefinition.isSingleton()) { - addSingleton(beanName, bean); + //如果有代理对象,此处获取代理对象 + exposedObject = getSingleton(beanName); + addSingleton(beanName, exposedObject); + } + return exposedObject; + } + + protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) { + Object exposedObject = bean; + for (BeanPostProcessor bp : getBeanPostProcessors()) { + if (bp instanceof InstantiationAwareBeanPostProcessor) { + exposedObject = ((InstantiationAwareBeanPostProcessor) bp).getEarlyBeanReference(exposedObject, beanName); + if (exposedObject == null) { + return exposedObject; + } + } + } + + return exposedObject; + } + + /** + * bean实例化后执行,如果返回false,不执行后续设置属性的逻辑 + * + * @param beanName + * @param bean + * @return + */ + private boolean applyBeanPostProcessorsAfterInstantiation(String beanName, Object bean) { + boolean continueWithPropertyPopulation = true; + for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) { + if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { + if (!((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessAfterInstantiation(bean, beanName)) { + continueWithPropertyPopulation = false; + break; + } + } + } + return continueWithPropertyPopulation; + } + + /** + * 在设置bean属性之前,允许BeanPostProcessor修改属性值 + * + * @param beanName + * @param bean + * @param beanDefinition + */ + protected void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) { + for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) { + if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { + PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName); + if (pvs != null) { + for (PropertyValue propertyValue : pvs.getPropertyValues()) { + beanDefinition.getPropertyValues().addPropertyValue(propertyValue); + } + } + } } - return bean; } /** @@ -122,6 +202,16 @@ protected void applyPropertyValues(String beanName, Object bean, BeanDefinition // beanA依赖beanB,先实例化beanB BeanReference beanReference = (BeanReference) value; value = getBean(beanReference.getBeanName()); + } else { + //类型转换 + Class sourceType = value.getClass(); + Class targetType = (Class) TypeUtil.getFieldType(bean.getClass(), name); + ConversionService conversionService = getConversionService(); + if (conversionService != null) { + if (conversionService.canConvert(sourceType, targetType)) { + value = conversionService.convert(value, targetType); + } + } } //通过反射设置属性 @@ -147,7 +237,7 @@ protected Object initializeBean(String beanName, Object bean, BeanDefinition bea } //执行BeanPostProcessor的后置处理 - wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName); + wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); return wrappedBean; } @@ -193,7 +283,7 @@ protected void invokeInitMethods(String beanName, Object bean, BeanDefinition be ((InitializingBean) bean).afterPropertiesSet(); } String initMethodName = beanDefinition.getInitMethodName(); - if (StrUtil.isNotEmpty(initMethodName)) { + if (StrUtil.isNotEmpty(initMethodName) && !(bean instanceof InitializingBean && "afterPropertiesSet".equals(initMethodName))) { Method initMethod = ClassUtil.getPublicMethod(beanDefinition.getBeanClass(), initMethodName); if (initMethod == null) { throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'"); diff --git a/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index f7ca99d..5c12aa8 100644 --- a/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -1,18 +1,17 @@ package org.springframework.beans.factory.support; -import cn.hutool.core.lang.Assert; import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.core.convert.ConversionService; +import org.springframework.util.StringValueResolver; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; /** * @author derekyi @@ -24,6 +23,11 @@ public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry i private final Map factoryBeanObjectCache = new HashMap<>(); + private final List embeddedValueResolvers = new ArrayList(); + + private ConversionService conversionService; + + @Override public Object getBean(String name) throws BeansException { Object sharedInstance = getSingleton(name); @@ -73,6 +77,13 @@ public T getBean(String name, Class requiredType) throws BeansException { return ((T) getBean(name)); } + @Override + public boolean containsBean(String name) { + return containsBeanDefinition(name); + } + + protected abstract boolean containsBeanDefinition(String beanName); + protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException; protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException; @@ -87,4 +98,26 @@ public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) { public List getBeanPostProcessors() { return this.beanPostProcessors; } + + public void addEmbeddedValueResolver(StringValueResolver valueResolver) { + this.embeddedValueResolvers.add(valueResolver); + } + + public String resolveEmbeddedValue(String value) { + String result = value; + for (StringValueResolver resolver : this.embeddedValueResolvers) { + result = resolver.resolveStringValue(result); + } + return result; + } + + @Override + public ConversionService getConversionService() { + return conversionService; + } + + @Override + public void setConversionService(ConversionService conversionService) { + this.conversionService = conversionService; + } } diff --git a/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java b/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java index 96e1b07..b04a011 100644 --- a/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java +++ b/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java @@ -1,5 +1,7 @@ package org.springframework.beans.factory.support; +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.MethodInterceptor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; @@ -18,7 +20,9 @@ public class CglibSubclassingInstantiationStrategy implements InstantiationStrat */ @Override public Object instantiate(BeanDefinition beanDefinition) throws BeansException { - //TODO 感兴趣的小伙伴可以实现下 - throw new UnsupportedOperationException("CGLIB instantiation strategy is not supported"); + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(beanDefinition.getBeanClass()); + enhancer.setCallback((MethodInterceptor) (obj, method, argsTemp, proxy) -> proxy.invokeSuper(obj,argsTemp)); + return enhancer.create(); } } diff --git a/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 7e9c1a0..b40f8ce 100644 --- a/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -4,9 +4,8 @@ import org.springframework.beans.factory.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * @author derekyi @@ -15,7 +14,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry { - private Map beanDefinitionMap = new HashMap<>(); + private Map beanDefinitionMap = new ConcurrentHashMap<>(256); @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) { @@ -50,6 +49,22 @@ public Map getBeansOfType(Class type) throws BeansException { return result; } + public T getBean(Class requiredType) throws BeansException { + List beanNames = new ArrayList<>(); + for (Map.Entry entry : beanDefinitionMap.entrySet()) { + Class beanClass = entry.getValue().getBeanClass(); + if (requiredType.isAssignableFrom(beanClass)) { + beanNames.add(entry.getKey()); + } + } + if (beanNames.size() == 1) { + return getBean(beanNames.get(0), requiredType); + } + + throw new BeansException(requiredType + "expected single bean but found " + + beanNames.size() + ": " + beanNames); + } + @Override public String[] getBeanDefinitionNames() { Set beanNames = beanDefinitionMap.keySet(); @@ -58,6 +73,11 @@ public String[] getBeanDefinitionNames() { @Override public void preInstantiateSingletons() throws BeansException { - beanDefinitionMap.keySet().forEach(this::getBean); + beanDefinitionMap.forEach((beanName, beanDefinition) -> { + //只有当bean是单例且不为懒加载才会被创建 + if (beanDefinition.isSingleton() && !beanDefinition.isLazyInit()) { + getBean(beanName); + } + }); } } diff --git a/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 8ee3656..2f4623c 100644 --- a/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -1,31 +1,64 @@ package org.springframework.beans.factory.support; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.SingletonBeanRegistry; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - /** * @author derekyi * @date 2020/11/22 */ public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry { + /** + * 一级缓存 + */ private Map singletonObjects = new HashMap<>(); + /** + * 二级缓存 + */ + private Map earlySingletonObjects = new HashMap<>(); + + /** + * 三级缓存 + */ + private Map> singletonFactories = new HashMap>(); + private final Map disposableBeans = new HashMap<>(); @Override public Object getSingleton(String beanName) { - return singletonObjects.get(beanName); + Object singletonObject = singletonObjects.get(beanName); + if (singletonObject == null) { + singletonObject = earlySingletonObjects.get(beanName); + if (singletonObject == null) { + ObjectFactory singletonFactory = singletonFactories.get(beanName); + if (singletonFactory != null) { + singletonObject = singletonFactory.getObject(); + //从三级缓存放进二级缓存 + earlySingletonObjects.put(beanName, singletonObject); + singletonFactories.remove(beanName); + } + } + } + return singletonObject; } + @Override public void addSingleton(String beanName, Object singletonObject) { - singletonObjects.put(beanName, singletonObject); + singletonObjects.put(beanName, singletonObject); // 1 + earlySingletonObjects.remove(beanName); // 2 + singletonFactories.remove(beanName); // 3 + } + + protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) { + singletonFactories.put(beanName, singletonFactory); } public void registerDisposableBean(String beanName, DisposableBean bean) { @@ -33,7 +66,7 @@ public void registerDisposableBean(String beanName, DisposableBean bean) { } public void destroySingletons() { - Set beanNames = disposableBeans.keySet(); + ArrayList beanNames = new ArrayList<>(disposableBeans.keySet()); for (String beanName : beanNames) { DisposableBean disposableBean = disposableBeans.remove(beanName); try { diff --git a/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java b/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java index 91c3a00..c63d778 100644 --- a/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java +++ b/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java @@ -6,12 +6,14 @@ import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; + import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -37,6 +39,9 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { public static final String INIT_METHOD_ATTRIBUTE = "init-method"; public static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method"; public static final String SCOPE_ATTRIBUTE = "scope"; + public static final String LAZYINIT_ATTRIBUTE = "lazyInit"; + public static final String BASE_PACKAGE_ATTRIBUTE = "base-package"; + public static final String COMPONENT_SCAN_ELEMENT = "component-scan"; public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); @@ -71,8 +76,19 @@ protected void doLoadBeanDefinitions(InputStream inputStream) throws DocumentExc SAXReader reader = new SAXReader(); Document document = reader.read(inputStream); - Element beans = document.getRootElement(); - List beanList = beans.elements(BEAN_ELEMENT); + Element root = document.getRootElement(); + + //解析context:component-scan标签并扫描指定包中的类,提取类信息,组装成BeanDefinition + Element componentScan = root.element(COMPONENT_SCAN_ELEMENT); + if (componentScan != null) { + String scanPath = componentScan.attributeValue(BASE_PACKAGE_ATTRIBUTE); + if (StrUtil.isEmpty(scanPath)) { + throw new BeansException("The value of base-package attribute can not be empty or null"); + } + scanPackage(scanPath); + } + + List beanList = root.elements(BEAN_ELEMENT); for (Element bean : beanList) { String beanId = bean.attributeValue(ID_ATTRIBUTE); String beanName = bean.attributeValue(NAME_ATTRIBUTE); @@ -80,7 +96,7 @@ protected void doLoadBeanDefinitions(InputStream inputStream) throws DocumentExc String initMethodName = bean.attributeValue(INIT_METHOD_ATTRIBUTE); String destroyMethodName = bean.attributeValue(DESTROY_METHOD_ATTRIBUTE); String beanScope = bean.attributeValue(SCOPE_ATTRIBUTE); - + String lazyInit = bean.attributeValue(LAZYINIT_ATTRIBUTE); Class clazz; try { clazz = Class.forName(className); @@ -97,6 +113,7 @@ protected void doLoadBeanDefinitions(InputStream inputStream) throws DocumentExc BeanDefinition beanDefinition = new BeanDefinition(clazz); beanDefinition.setInitMethodName(initMethodName); beanDefinition.setDestroyMethodName(destroyMethodName); + beanDefinition.setLazyInit(Boolean.parseBoolean(lazyInit)); if (StrUtil.isNotEmpty(beanScope)) { beanDefinition.setScope(beanScope); } @@ -126,4 +143,15 @@ protected void doLoadBeanDefinitions(InputStream inputStream) throws DocumentExc getRegistry().registerBeanDefinition(beanName, beanDefinition); } } + + /** + * 扫描注解Component的类,提取信息,组装成BeanDefinition + * + * @param scanPath + */ + private void scanPackage(String scanPath) { + String[] basePackages = StrUtil.splitToArray(scanPath, ','); + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry()); + scanner.doScan(basePackages); + } } diff --git a/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java new file mode 100644 index 0000000..cfce9cd --- /dev/null +++ b/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java @@ -0,0 +1,77 @@ +package org.springframework.context.annotation; + +import cn.hutool.core.util.StrUtil; +import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * @author derekyi + * @date 2020/12/26 + */ +public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider { + + public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"; + + private BeanDefinitionRegistry registry; + + public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { + this.registry = registry; + } + + public void doScan(String... basePackages) { + for (String basePackage : basePackages) { + Set candidates = findCandidateComponents(basePackage); + for (BeanDefinition candidate : candidates) { + // 解析bean的作用域 + String beanScope = resolveBeanScope(candidate); + if (StrUtil.isNotEmpty(beanScope)) { + candidate.setScope(beanScope); + } + //生成bean的名称 + String beanName = determineBeanName(candidate); + //注册BeanDefinition + registry.registerBeanDefinition(beanName, candidate); + } + } + + //注册处理@Autowired和@Value注解的BeanPostProcessor + registry.registerBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME, new BeanDefinition(AutowiredAnnotationBeanPostProcessor.class)); + } + + /** + * 获取bean的作用域 + * + * @param beanDefinition + * @return + */ + private String resolveBeanScope(BeanDefinition beanDefinition) { + Class beanClass = beanDefinition.getBeanClass(); + Scope scope = beanClass.getAnnotation(Scope.class); + if (scope != null) { + return scope.value(); + } + + return StrUtil.EMPTY; + } + + + /** + * 生成bean的名称 + * + * @param beanDefinition + * @return + */ + private String determineBeanName(BeanDefinition beanDefinition) { + Class beanClass = beanDefinition.getBeanClass(); + Component component = beanClass.getAnnotation(Component.class); + String value = component.value(); + if (StrUtil.isEmpty(value)) { + value = StrUtil.lowerFirst(beanClass.getSimpleName()); + } + return value; + } +} diff --git a/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java new file mode 100644 index 0000000..e66567c --- /dev/null +++ b/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java @@ -0,0 +1,26 @@ +package org.springframework.context.annotation; + +import cn.hutool.core.util.ClassUtil; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * @author derekyi + * @date 2020/12/26 + */ +public class ClassPathScanningCandidateComponentProvider { + + public Set findCandidateComponents(String basePackage) { + Set candidates = new LinkedHashSet(); + // 扫描有org.springframework.stereotype.Component注解的类 + Set> classes = ClassUtil.scanPackageByAnnotation(basePackage, Component.class); + for (Class clazz : classes) { + BeanDefinition beanDefinition = new BeanDefinition(clazz); + candidates.add(beanDefinition); + } + return candidates; + } +} diff --git a/src/main/java/org/springframework/context/annotation/Scope.java b/src/main/java/org/springframework/context/annotation/Scope.java new file mode 100644 index 0000000..20bc7f7 --- /dev/null +++ b/src/main/java/org/springframework/context/annotation/Scope.java @@ -0,0 +1,15 @@ +package org.springframework.context.annotation; + +import java.lang.annotation.*; + +/** + * @author derekyi + * @date 2020/12/26 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Scope { + + String value() default "singleton"; +} diff --git a/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index a971bd4..89ea47a 100644 --- a/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -11,6 +11,7 @@ import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.SimpleApplicationEventMulticaster; +import org.springframework.core.convert.ConversionService; import org.springframework.core.io.DefaultResourceLoader; import java.util.Collection; @@ -26,6 +27,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader i public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster"; + public static final String CONVERSION_SERVICE_BEAN_NAME = "conversionService"; + private ApplicationEventMulticaster applicationEventMulticaster; @Override @@ -49,13 +52,26 @@ public void refresh() throws BeansException { //注册事件监听器 registerListeners(); - //提前实例化单例bean - beanFactory.preInstantiateSingletons(); + //注册类型转换器和提前实例化单例bean + finishBeanFactoryInitialization(beanFactory); //发布容器刷新完成事件 finishRefresh(); } + protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + //设置类型转换器 + if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME)) { + Object conversionService = beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME); + if (conversionService instanceof ConversionService) { + beanFactory.setConversionService((ConversionService) conversionService); + } + } + + //提前实例化单例bean + beanFactory.preInstantiateSingletons(); + } + /** * 创建BeanFactory,并加载BeanDefinition * @@ -118,6 +134,11 @@ public void publishEvent(ApplicationEvent event) { applicationEventMulticaster.multicastEvent(event); } + @Override + public boolean containsBean(String name) { + return getBeanFactory().containsBean(name); + } + @Override public T getBean(String name, Class requiredType) throws BeansException { return getBeanFactory().getBean(name, requiredType); @@ -128,6 +149,11 @@ public Map getBeansOfType(Class type) throws BeansException { return getBeanFactory().getBeansOfType(type); } + public T getBean(Class requiredType) throws BeansException { + return getBeanFactory().getBean(requiredType); + } + + @Override public Object getBean(String name) throws BeansException { return getBeanFactory().getBean(name); } diff --git a/src/main/java/org/springframework/context/support/ConversionServiceFactoryBean.java b/src/main/java/org/springframework/context/support/ConversionServiceFactoryBean.java new file mode 100644 index 0000000..2d8bc4c --- /dev/null +++ b/src/main/java/org/springframework/context/support/ConversionServiceFactoryBean.java @@ -0,0 +1,61 @@ +package org.springframework.context.support; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.ConverterFactory; +import org.springframework.core.convert.converter.ConverterRegistry; +import org.springframework.core.convert.converter.GenericConverter; +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.core.convert.support.GenericConversionService; + +import java.util.Set; + +/** + * @author derekyi + * @date 2021/1/17 + */ +public class ConversionServiceFactoryBean implements FactoryBean, InitializingBean { + + private Set converters; + + private GenericConversionService conversionService; + + @Override + public void afterPropertiesSet() throws Exception { + conversionService = new DefaultConversionService(); + registerConverters(converters, conversionService); + } + + private void registerConverters(Set converters, ConverterRegistry registry) { + if (converters != null) { + for (Object converter : converters) { + if (converter instanceof GenericConverter) { + registry.addConverter((GenericConverter) converter); + } else if (converter instanceof Converter) { + registry.addConverter((Converter) converter); + } else if (converter instanceof ConverterFactory) { + registry.addConverterFactory((ConverterFactory) converter); + } else { + throw new IllegalArgumentException("Each converter object must implement one of the " + + "Converter, ConverterFactory, or GenericConverter interfaces"); + } + } + } + } + + @Override + public ConversionService getObject() throws Exception { + return conversionService; + } + + @Override + public boolean isSingleton() { + return true; + } + + public void setConverters(Set converters) { + this.converters = converters; + } +} diff --git a/src/main/java/org/springframework/core/convert/ConversionService.java b/src/main/java/org/springframework/core/convert/ConversionService.java new file mode 100644 index 0000000..2da3a9a --- /dev/null +++ b/src/main/java/org/springframework/core/convert/ConversionService.java @@ -0,0 +1,14 @@ +package org.springframework.core.convert; + +/** + * 类型转换抽象接口 + * + * @author derekyi + * @date 2021/1/10 + */ +public interface ConversionService { + + boolean canConvert(Class sourceType, Class targetType); + + T convert(Object source, Class targetType); +} diff --git a/src/main/java/org/springframework/core/convert/converter/Converter.java b/src/main/java/org/springframework/core/convert/converter/Converter.java new file mode 100644 index 0000000..b3b4995 --- /dev/null +++ b/src/main/java/org/springframework/core/convert/converter/Converter.java @@ -0,0 +1,15 @@ +package org.springframework.core.convert.converter; + +/** + * 类型转换抽象接口 + * + * @author derekyi + * @date 2021/1/10 + */ +public interface Converter { + + /** + * 类型转换 + */ + T convert(S source); +} diff --git a/src/main/java/org/springframework/core/convert/converter/ConverterFactory.java b/src/main/java/org/springframework/core/convert/converter/ConverterFactory.java new file mode 100644 index 0000000..0d2baa6 --- /dev/null +++ b/src/main/java/org/springframework/core/convert/converter/ConverterFactory.java @@ -0,0 +1,12 @@ +package org.springframework.core.convert.converter; + +/** + * 类型转换工厂 + * + * @author derekyi + * @date 2021/1/10 + */ +public interface ConverterFactory { + + Converter getConverter(Class targetType); +} \ No newline at end of file diff --git a/src/main/java/org/springframework/core/convert/converter/ConverterRegistry.java b/src/main/java/org/springframework/core/convert/converter/ConverterRegistry.java new file mode 100644 index 0000000..dbcff6c --- /dev/null +++ b/src/main/java/org/springframework/core/convert/converter/ConverterRegistry.java @@ -0,0 +1,16 @@ +package org.springframework.core.convert.converter; + +/** + * 类型转换器注册接口 + * + * @author derekyi + * @date 2021/1/10 + */ +public interface ConverterRegistry { + + void addConverter(Converter converter); + + void addConverterFactory(ConverterFactory converterFactory); + + void addConverter(GenericConverter converter); +} diff --git a/src/main/java/org/springframework/core/convert/converter/GenericConverter.java b/src/main/java/org/springframework/core/convert/converter/GenericConverter.java new file mode 100644 index 0000000..15bf723 --- /dev/null +++ b/src/main/java/org/springframework/core/convert/converter/GenericConverter.java @@ -0,0 +1,53 @@ +package org.springframework.core.convert.converter; + +import java.util.Set; + +/** + * @author derekyi + * @date 2021/1/16 + */ +public interface GenericConverter { + + Set getConvertibleTypes(); + + Object convert(Object source, Class sourceType, Class targetType); + + public static final class ConvertiblePair { + + private final Class sourceType; + + private final Class targetType; + + public ConvertiblePair(Class sourceType, Class targetType) { + this.sourceType = sourceType; + this.targetType = targetType; + } + + public Class getSourceType() { + return this.sourceType; + } + + public Class getTargetType() { + return this.targetType; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || obj.getClass() != ConvertiblePair.class) { + return false; + } + ConvertiblePair other = (ConvertiblePair) obj; + return this.sourceType.equals(other.sourceType) && this.targetType.equals(other.targetType); + + } + + @Override + public int hashCode() { + return this.sourceType.hashCode() * 31 + this.targetType.hashCode(); + } + } + +} diff --git a/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java b/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java new file mode 100644 index 0000000..72fe55d --- /dev/null +++ b/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java @@ -0,0 +1,19 @@ +package org.springframework.core.convert.support; + +import org.springframework.core.convert.converter.ConverterRegistry; + +/** + * @author derekyi + * @date 2021/1/16 + */ +public class DefaultConversionService extends GenericConversionService { + + public DefaultConversionService() { + addDefaultConverters(this); + } + + public static void addDefaultConverters(ConverterRegistry converterRegistry) { + converterRegistry.addConverterFactory(new StringToNumberConverterFactory()); + //TODO 添加其他ConverterFactory + } +} diff --git a/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/src/main/java/org/springframework/core/convert/support/GenericConversionService.java new file mode 100644 index 0000000..bcbbd2a --- /dev/null +++ b/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -0,0 +1,140 @@ +package org.springframework.core.convert.support; + +import cn.hutool.core.convert.BasicType; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.ConverterFactory; +import org.springframework.core.convert.converter.ConverterRegistry; +import org.springframework.core.convert.converter.GenericConverter; +import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; + +/** + * @author derekyi + * @date 2021/1/16 + */ +public class GenericConversionService implements ConversionService, ConverterRegistry { + + private Map converters = new HashMap<>(); + + @Override + public boolean canConvert(Class sourceType, Class targetType) { + GenericConverter converter = getConverter(sourceType, targetType); + return converter != null; + } + + @Override + public T convert(Object source, Class targetType) { + Class sourceType = source.getClass(); + targetType = (Class) BasicType.wrap(targetType); + GenericConverter converter = getConverter(sourceType, targetType); + return (T) converter.convert(source, sourceType, targetType); + } + + @Override + public void addConverter(Converter converter) { + ConvertiblePair typeInfo = getRequiredTypeInfo(converter); + ConverterAdapter converterAdapter = new ConverterAdapter(typeInfo, converter); + for (ConvertiblePair convertibleType : converterAdapter.getConvertibleTypes()) { + converters.put(convertibleType, converterAdapter); + } + } + + @Override + public void addConverterFactory(ConverterFactory converterFactory) { + ConvertiblePair typeInfo = getRequiredTypeInfo(converterFactory); + ConverterFactoryAdapter converterFactoryAdapter = new ConverterFactoryAdapter(typeInfo, converterFactory); + for (ConvertiblePair convertibleType : converterFactoryAdapter.getConvertibleTypes()) { + converters.put(convertibleType, converterFactoryAdapter); + } + } + + @Override + public void addConverter(GenericConverter converter) { + for (ConvertiblePair convertibleType : converter.getConvertibleTypes()) { + converters.put(convertibleType, converter); + } + } + + private ConvertiblePair getRequiredTypeInfo(Object object) { + Type[] types = object.getClass().getGenericInterfaces(); + ParameterizedType parameterized = (ParameterizedType) types[0]; + Type[] actualTypeArguments = parameterized.getActualTypeArguments(); + Class sourceType = (Class) actualTypeArguments[0]; + Class targetType = (Class) actualTypeArguments[1]; + return new ConvertiblePair(sourceType, targetType); + } + + protected GenericConverter getConverter(Class sourceType, Class targetType) { + List> sourceCandidates = getClassHierarchy(sourceType); + List> targetCandidates = getClassHierarchy(targetType); + for (Class sourceCandidate : sourceCandidates) { + for (Class targetCandidate : targetCandidates) { + ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate); + GenericConverter converter = converters.get(convertiblePair); + if (converter != null) { + return converter; + } + } + } + return null; + } + + private List> getClassHierarchy(Class clazz) { + List> hierarchy = new ArrayList<>(); + //原始类转为包装类 + clazz = BasicType.wrap(clazz); + while (clazz != null) { + hierarchy.add(clazz); + clazz = clazz.getSuperclass(); + } + return hierarchy; + } + + private final class ConverterAdapter implements GenericConverter { + + private final ConvertiblePair typeInfo; + + private final Converter converter; + + public ConverterAdapter(ConvertiblePair typeInfo, Converter converter) { + this.typeInfo = typeInfo; + this.converter = (Converter) converter; + } + + @Override + public Set getConvertibleTypes() { + return Collections.singleton(typeInfo); + } + + @Override + public Object convert(Object source, Class sourceType, Class targetType) { + return converter.convert(source); + } + } + + private final class ConverterFactoryAdapter implements GenericConverter { + + private final ConvertiblePair typeInfo; + + private final ConverterFactory converterFactory; + + public ConverterFactoryAdapter(ConvertiblePair typeInfo, ConverterFactory converterFactory) { + this.typeInfo = typeInfo; + this.converterFactory = (ConverterFactory) converterFactory; + } + + @Override + public Set getConvertibleTypes() { + return Collections.singleton(typeInfo); + } + + @Override + public Object convert(Object source, Class sourceType, Class targetType) { + return converterFactory.getConverter(targetType).convert(source); + } + } +} diff --git a/src/main/java/org/springframework/core/convert/support/StringToNumberConverterFactory.java b/src/main/java/org/springframework/core/convert/support/StringToNumberConverterFactory.java new file mode 100644 index 0000000..321ff8a --- /dev/null +++ b/src/main/java/org/springframework/core/convert/support/StringToNumberConverterFactory.java @@ -0,0 +1,45 @@ +package org.springframework.core.convert.support; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.ConverterFactory; + +/** + * @author derekyi + * @date 2021/1/10 + */ +public class StringToNumberConverterFactory implements ConverterFactory { + + @Override + public Converter getConverter(Class targetType) { + return new StringToNumber(targetType); + } + + private static final class StringToNumber implements Converter { + + private final Class targetType; + + public StringToNumber(Class targetType) { + this.targetType = targetType; + } + + @Override + public T convert(String source) { + if (source.length() == 0) { + return null; + } + + if (targetType.equals(Integer.class)) { + return (T) Integer.valueOf(source); + } else if (targetType.equals(Long.class)) { + return (T) Long.valueOf(source); + } + //TODO 其他数字类型 + + else { + throw new IllegalArgumentException( + "Cannot convert String [" + source + "] to target class [" + targetType.getName() + "]"); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/springframework/stereotype/Component.java b/src/main/java/org/springframework/stereotype/Component.java new file mode 100644 index 0000000..005c9b4 --- /dev/null +++ b/src/main/java/org/springframework/stereotype/Component.java @@ -0,0 +1,15 @@ +package org.springframework.stereotype; + +import java.lang.annotation.*; + +/** + * @author derekyi + * @date 2020/12/26 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Component { + + String value() default ""; +} \ No newline at end of file diff --git a/src/main/java/org/springframework/util/StringValueResolver.java b/src/main/java/org/springframework/util/StringValueResolver.java new file mode 100644 index 0000000..fed2c1e --- /dev/null +++ b/src/main/java/org/springframework/util/StringValueResolver.java @@ -0,0 +1,10 @@ +package org.springframework.util; + +/** + * @author derekyi + * @date 2020/12/27 + */ +public interface StringValueResolver { + + String resolveStringValue(String strVal); +} diff --git a/src/test/java/org/springframework/test/aop/AutoProxyTest.java b/src/test/java/org/springframework/test/aop/AutoProxyTest.java index 1202ad3..6d8f93a 100644 --- a/src/test/java/org/springframework/test/aop/AutoProxyTest.java +++ b/src/test/java/org/springframework/test/aop/AutoProxyTest.java @@ -4,6 +4,8 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.service.WorldService; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author derekyi * @date 2020/12/6 @@ -18,4 +20,14 @@ public void testAutoProxy() throws Exception { WorldService worldService = applicationContext.getBean("worldService", WorldService.class); worldService.explode(); } + + @Test + public void testPopulateProxyBeanWithPropertyValues() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:populate-proxy-bean-with-property-values.xml"); + + //获取代理对象 + WorldService worldService = applicationContext.getBean("worldService", WorldService.class); + worldService.explode(); + assertThat(worldService.getName()).isEqualTo("earth"); + } } diff --git a/src/test/java/org/springframework/test/aop/DynamicProxyTest.java b/src/test/java/org/springframework/test/aop/DynamicProxyTest.java index bb2bf49..94c8992 100644 --- a/src/test/java/org/springframework/test/aop/DynamicProxyTest.java +++ b/src/test/java/org/springframework/test/aop/DynamicProxyTest.java @@ -1,20 +1,19 @@ package org.springframework.test.aop; -import org.aopalliance.intercept.MethodInterceptor; import org.junit.Before; import org.junit.Test; + import org.springframework.aop.AdvisedSupport; import org.springframework.aop.ClassFilter; -import org.springframework.aop.MethodMatcher; import org.springframework.aop.TargetSource; -import org.springframework.aop.aspectj.AspectJExpressionPointcut; import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor; import org.springframework.aop.framework.CglibAopProxy; import org.springframework.aop.framework.JdkDynamicAopProxy; import org.springframework.aop.framework.ProxyFactory; +import org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor; import org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor; +import org.springframework.test.common.WorldServiceAfterReturnAdvice; import org.springframework.test.common.WorldServiceBeforeAdvice; -import org.springframework.test.common.WorldServiceInterceptor; import org.springframework.test.service.WorldService; import org.springframework.test.service.WorldServiceImpl; @@ -29,14 +28,16 @@ public class DynamicProxyTest { @Before public void setup() { WorldService worldService = new WorldServiceImpl(); - - advisedSupport = new AdvisedSupport(); + advisedSupport = new ProxyFactory(); + //Advisor是Pointcut和Advice的组合 + String expression = "execution(* org.springframework.test.service.WorldService.explode(..))"; + AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor(); + advisor.setExpression(expression); + AfterReturningAdviceInterceptor methodInterceptor = new AfterReturningAdviceInterceptor(new WorldServiceAfterReturnAdvice()); + advisor.setAdvice(methodInterceptor); TargetSource targetSource = new TargetSource(worldService); - WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor(); - MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* org.springframework.test.service.WorldService.explode(..))").getMethodMatcher(); advisedSupport.setTargetSource(targetSource); - advisedSupport.setMethodInterceptor(methodInterceptor); - advisedSupport.setMethodMatcher(methodMatcher); + advisedSupport.addAdvisor(advisor); } @Test @@ -54,24 +55,28 @@ public void testCglibDynamicProxy() throws Exception { @Test public void testProxyFactory() throws Exception { // 使用JDK动态代理 - advisedSupport.setProxyTargetClass(false); - WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy(); + ProxyFactory factory = (ProxyFactory) advisedSupport; + factory.setProxyTargetClass(false); + WorldService proxy = (WorldService) factory.getProxy(); proxy.explode(); // 使用CGLIB动态代理 - advisedSupport.setProxyTargetClass(true); - proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy(); + factory.setProxyTargetClass(true); + proxy = (WorldService) factory.getProxy(); proxy.explode(); } @Test public void testBeforeAdvice() throws Exception { //设置BeforeAdvice - WorldServiceBeforeAdvice beforeAdvice = new WorldServiceBeforeAdvice(); - MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(beforeAdvice); - advisedSupport.setMethodInterceptor(methodInterceptor); - - WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy(); + String expression = "execution(* org.springframework.test.service.WorldService.explode(..))"; + AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor(); + advisor.setExpression(expression); + MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(new WorldServiceBeforeAdvice()); + advisor.setAdvice(methodInterceptor); + advisedSupport.addAdvisor(advisor); + ProxyFactory factory = (ProxyFactory) advisedSupport; + WorldService proxy = (WorldService) factory.getProxy(); proxy.explode(); } @@ -88,15 +93,15 @@ public void testAdvisor() throws Exception { ClassFilter classFilter = advisor.getPointcut().getClassFilter(); if (classFilter.matches(worldService.getClass())) { - AdvisedSupport advisedSupport = new AdvisedSupport(); + ProxyFactory proxyFactory = new ProxyFactory(); TargetSource targetSource = new TargetSource(worldService); - advisedSupport.setTargetSource(targetSource); - advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice()); - advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher()); + proxyFactory.setTargetSource(targetSource); + proxyFactory.addAdvisor(advisor); +// proxyFactory.setMethodMatcher(advisor.getPointcut().getMethodMatcher()); // advisedSupport.setProxyTargetClass(true); //JDK or CGLIB - WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy(); + WorldService proxy = (WorldService) proxyFactory.getProxy(); proxy.explode(); } } diff --git a/src/test/java/org/springframework/test/aop/ProxyFactoryTest.java b/src/test/java/org/springframework/test/aop/ProxyFactoryTest.java new file mode 100644 index 0000000..01d1b14 --- /dev/null +++ b/src/test/java/org/springframework/test/aop/ProxyFactoryTest.java @@ -0,0 +1,42 @@ +package org.springframework.test.aop; + +import org.junit.Test; + +import org.springframework.aop.TargetSource; +import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor; +import org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor; +import org.springframework.test.common.WorldServiceAfterReturnAdvice; +import org.springframework.test.common.WorldServiceBeforeAdvice; +import org.springframework.test.service.WorldService; +import org.springframework.test.service.WorldServiceImpl; + +public class ProxyFactoryTest { + @Test + public void testAdvisor() throws Exception { + WorldService worldService = new WorldServiceImpl(); + + //Advisor是Pointcut和Advice的组合 + String expression = "execution(* org.springframework.test.service.WorldService.explode(..))"; + //第一个切面 + AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor(); + advisor.setExpression(expression); + MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(new WorldServiceBeforeAdvice()); + advisor.setAdvice(methodInterceptor); + //第二个切面 + AspectJExpressionPointcutAdvisor advisor1 = new AspectJExpressionPointcutAdvisor(); + advisor1.setExpression(expression); + AfterReturningAdviceInterceptor afterReturningAdviceInterceptor = new AfterReturningAdviceInterceptor(new WorldServiceAfterReturnAdvice()); + advisor1.setAdvice(afterReturningAdviceInterceptor); + //通过ProxyFactory来获得代理 + ProxyFactory factory = new ProxyFactory(); + TargetSource targetSource = new TargetSource(worldService); + factory.setTargetSource(targetSource); + factory.setProxyTargetClass(true); + factory.addAdvisor(advisor); + factory.addAdvisor(advisor1); + WorldService proxy = (WorldService) factory.getProxy(); + proxy.explode(); + } +} diff --git a/src/test/java/org/springframework/test/bean/A.java b/src/test/java/org/springframework/test/bean/A.java new file mode 100644 index 0000000..36ad3c0 --- /dev/null +++ b/src/test/java/org/springframework/test/bean/A.java @@ -0,0 +1,20 @@ +package org.springframework.test.bean; + +/** + * @author derekyi + * @date 2021/1/25 + */ +public class A { + + private B b; + + public void func(){} + + public B getB() { + return b; + } + + public void setB(B b) { + this.b = b; + } +} diff --git a/src/test/java/org/springframework/test/bean/B.java b/src/test/java/org/springframework/test/bean/B.java new file mode 100644 index 0000000..ba467bf --- /dev/null +++ b/src/test/java/org/springframework/test/bean/B.java @@ -0,0 +1,18 @@ +package org.springframework.test.bean; + +/** + * @author derekyi + * @date 2021/1/25 + */ +public class B { + + private A a; + + public A getA() { + return a; + } + + public void setA(A a) { + this.a = a; + } +} diff --git a/src/test/java/org/springframework/test/bean/Car.java b/src/test/java/org/springframework/test/bean/Car.java index 9406211..53be9ba 100644 --- a/src/test/java/org/springframework/test/bean/Car.java +++ b/src/test/java/org/springframework/test/bean/Car.java @@ -1,11 +1,25 @@ package org.springframework.test.bean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.text.SimpleDateFormat; +import java.time.LocalDate; + /** * @author derekyi * @date 2020/11/24 */ +@Component public class Car { + private int price; + + private LocalDate produceDate; + + private long date; + + @Value("${brand}") private String brand; public String getBrand() { @@ -16,10 +30,37 @@ public void setBrand(String brand) { this.brand = brand; } + public int getPrice() { + return price; + } + + public void setPrice(int price) { + this.price = price; + } + + public LocalDate getProduceDate() { + return produceDate; + } + + public void setProduceDate(LocalDate produceDate) { + this.produceDate = produceDate; + } + + public void init() { + date = System.currentTimeMillis(); + } + + public void showTime() { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd :hh:mm:ss"); + System.out.println(date + ":bean create"); + } + @Override public String toString() { return "Car{" + - "brand='" + brand + '\'' + + "price=" + price + + ", produceDate=" + produceDate + + ", brand='" + brand + '\'' + '}'; } } diff --git a/src/test/java/org/springframework/test/bean/Person.java b/src/test/java/org/springframework/test/bean/Person.java index c6f8684..c83da16 100644 --- a/src/test/java/org/springframework/test/bean/Person.java +++ b/src/test/java/org/springframework/test/bean/Person.java @@ -2,17 +2,21 @@ import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; /** * @author derekyi * @date 2020/11/24 */ +@Component public class Person implements InitializingBean, DisposableBean { private String name; private int age; + @Autowired private Car car; public void customInitMethod() { diff --git a/src/test/java/org/springframework/test/common/ABeforeAdvice.java b/src/test/java/org/springframework/test/common/ABeforeAdvice.java new file mode 100644 index 0000000..86a5237 --- /dev/null +++ b/src/test/java/org/springframework/test/common/ABeforeAdvice.java @@ -0,0 +1,16 @@ +package org.springframework.test.common; + +import org.springframework.aop.MethodBeforeAdvice; + +import java.lang.reflect.Method; + +/** + * @author derekyi + * @date 2021/1/30 + */ +public class ABeforeAdvice implements MethodBeforeAdvice { + @Override + public void before(Method method, Object[] args, Object target) throws Throwable { + + } +} diff --git a/src/test/java/org/springframework/test/common/ConvertersFactoryBean.java b/src/test/java/org/springframework/test/common/ConvertersFactoryBean.java new file mode 100644 index 0000000..a445fb3 --- /dev/null +++ b/src/test/java/org/springframework/test/common/ConvertersFactoryBean.java @@ -0,0 +1,26 @@ +package org.springframework.test.common; + +import org.springframework.beans.factory.FactoryBean; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author derekyi + * @date 2021/1/17 + */ +public class ConvertersFactoryBean implements FactoryBean> { + + @Override + public Set getObject() throws Exception { + HashSet converters = new HashSet<>(); + StringToLocalDateConverter stringToLocalDateConverter = new StringToLocalDateConverter("yyyy-MM-dd"); + converters.add(stringToLocalDateConverter); + return converters; + } + + @Override + public boolean isSingleton() { + return true; + } +} diff --git a/src/test/java/org/springframework/test/common/StringToBooleanConverter.java b/src/test/java/org/springframework/test/common/StringToBooleanConverter.java new file mode 100644 index 0000000..505be83 --- /dev/null +++ b/src/test/java/org/springframework/test/common/StringToBooleanConverter.java @@ -0,0 +1,22 @@ +package org.springframework.test.common; + +import org.springframework.core.convert.converter.GenericConverter; + +import java.util.Collections; +import java.util.Set; + +/** + * @author derekyi + * @date 2021/1/16 + */ +public class StringToBooleanConverter implements GenericConverter { + @Override + public Set getConvertibleTypes() { + return Collections.singleton(new ConvertiblePair(String.class, Boolean.class)); + } + + @Override + public Object convert(Object source, Class sourceType, Class targetType) { + return Boolean.valueOf((String) source); + } +} diff --git a/src/test/java/org/springframework/test/common/StringToIntegerConverter.java b/src/test/java/org/springframework/test/common/StringToIntegerConverter.java new file mode 100644 index 0000000..f656b97 --- /dev/null +++ b/src/test/java/org/springframework/test/common/StringToIntegerConverter.java @@ -0,0 +1,14 @@ +package org.springframework.test.common; + +import org.springframework.core.convert.converter.Converter; + +/** + * @author derekyi + * @date 2021/1/16 + */ +public class StringToIntegerConverter implements Converter { + @Override + public Integer convert(String source) { + return Integer.valueOf(source); + } +} diff --git a/src/test/java/org/springframework/test/common/StringToLocalDateConverter.java b/src/test/java/org/springframework/test/common/StringToLocalDateConverter.java new file mode 100644 index 0000000..ed6f9ec --- /dev/null +++ b/src/test/java/org/springframework/test/common/StringToLocalDateConverter.java @@ -0,0 +1,24 @@ +package org.springframework.test.common; + +import org.springframework.core.convert.converter.Converter; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +/** + * @author derekyi + * @date 2021/1/17 + */ +public class StringToLocalDateConverter implements Converter { + + private final DateTimeFormatter DATE_TIME_FORMATTER; + + public StringToLocalDateConverter(String pattern) { + DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(pattern); + } + + @Override + public LocalDate convert(String source) { + return LocalDate.parse(source, DATE_TIME_FORMATTER); + } +} diff --git a/src/test/java/org/springframework/test/common/WorldServiceAfterReturnAdvice.java b/src/test/java/org/springframework/test/common/WorldServiceAfterReturnAdvice.java new file mode 100644 index 0000000..9d9dd7e --- /dev/null +++ b/src/test/java/org/springframework/test/common/WorldServiceAfterReturnAdvice.java @@ -0,0 +1,13 @@ +package org.springframework.test.common; + +import org.springframework.aop.AfterReturningAdvice; + +import java.lang.reflect.Method; + +public class WorldServiceAfterReturnAdvice implements AfterReturningAdvice { + + @Override + public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { + System.out.println("AfterAdvice: do something after the earth explodes"); + } +} diff --git a/src/test/java/org/springframework/test/ioc/AutowiredAnnotationTest.java b/src/test/java/org/springframework/test/ioc/AutowiredAnnotationTest.java new file mode 100644 index 0000000..f06de4d --- /dev/null +++ b/src/test/java/org/springframework/test/ioc/AutowiredAnnotationTest.java @@ -0,0 +1,22 @@ +package org.springframework.test.ioc; + +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.test.bean.Person; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author derekyi + * @date 2020/12/27 + */ +public class AutowiredAnnotationTest { + + @Test + public void testAutowiredAnnotation() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:autowired-annotation.xml"); + + Person person = applicationContext.getBean(Person.class); + assertThat(person.getCar()).isNotNull(); + } +} diff --git a/src/test/java/org/springframework/test/ioc/CircularReferenceWithProxyBeanTest.java b/src/test/java/org/springframework/test/ioc/CircularReferenceWithProxyBeanTest.java new file mode 100644 index 0000000..bd87d71 --- /dev/null +++ b/src/test/java/org/springframework/test/ioc/CircularReferenceWithProxyBeanTest.java @@ -0,0 +1,24 @@ +package org.springframework.test.ioc; + +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.test.bean.A; +import org.springframework.test.bean.B; + +import static org.assertj.core.api.Java6Assertions.assertThat; + +/** + * @author derekyi + * @date 2021/1/30 + */ +public class CircularReferenceWithProxyBeanTest { + + @Test + public void testCircularReference() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:circular-reference-with-proxy-bean.xml"); + A a = applicationContext.getBean("a", A.class); + B b = applicationContext.getBean("b", B.class); + + assertThat(b.getA() == a).isTrue(); + } +} diff --git a/src/test/java/org/springframework/test/ioc/CircularReferenceWithoutProxyBeanTest.java b/src/test/java/org/springframework/test/ioc/CircularReferenceWithoutProxyBeanTest.java new file mode 100644 index 0000000..9c87899 --- /dev/null +++ b/src/test/java/org/springframework/test/ioc/CircularReferenceWithoutProxyBeanTest.java @@ -0,0 +1,23 @@ +package org.springframework.test.ioc; + +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.test.bean.A; +import org.springframework.test.bean.B; + +import static org.assertj.core.api.Java6Assertions.assertThat; + +/** + * @author derekyi + * @date 2021/1/25 + */ +public class CircularReferenceWithoutProxyBeanTest { + + @Test + public void testCircularReference() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:circular-reference-without-proxy-bean.xml"); + A a = applicationContext.getBean("a", A.class); + B b = applicationContext.getBean("b", B.class); + assertThat(a.getB() == b).isTrue(); + } +} diff --git a/src/test/java/org/springframework/test/ioc/HelloService.java b/src/test/java/org/springframework/test/ioc/HelloService.java new file mode 100644 index 0000000..da03555 --- /dev/null +++ b/src/test/java/org/springframework/test/ioc/HelloService.java @@ -0,0 +1,13 @@ +package org.springframework.test.ioc; + +/** + * @author derekyi + * @date 2020/11/22 + */ +public class HelloService { + + public String sayHello() { + System.out.println("hello"); + return "hello"; + } +} diff --git a/src/test/java/org/springframework/test/ioc/LazyInitTest.java b/src/test/java/org/springframework/test/ioc/LazyInitTest.java new file mode 100644 index 0000000..27fcd76 --- /dev/null +++ b/src/test/java/org/springframework/test/ioc/LazyInitTest.java @@ -0,0 +1,20 @@ +package org.springframework.test.ioc; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.test.bean.Car; + +public class LazyInitTest { + + @Test + public void testLazyInit() throws InterruptedException { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:lazy-test.xml"); + System.out.println(System.currentTimeMillis() + ":applicationContext-over"); + TimeUnit.SECONDS.sleep(1); + Car c = (Car) applicationContext.getBean("car"); + c.showTime();//显示bean的创建时间 + } +} diff --git a/src/test/java/org/springframework/test/ioc/PackageScanTest.java b/src/test/java/org/springframework/test/ioc/PackageScanTest.java new file mode 100644 index 0000000..40d8244 --- /dev/null +++ b/src/test/java/org/springframework/test/ioc/PackageScanTest.java @@ -0,0 +1,22 @@ +package org.springframework.test.ioc; + +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.test.bean.Car; + +import static org.assertj.core.api.Java6Assertions.assertThat; + +/** + * @author derekyi + * @date 2020/12/26 + */ +public class PackageScanTest { + + @Test + public void testScanPackage() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:package-scan.xml"); + + Car car = applicationContext.getBean("car", Car.class); + assertThat(car).isNotNull(); + } +} diff --git a/src/test/java/org/springframework/test/ioc/TypeConversionFirstPartTest.java b/src/test/java/org/springframework/test/ioc/TypeConversionFirstPartTest.java new file mode 100644 index 0000000..d2e77c0 --- /dev/null +++ b/src/test/java/org/springframework/test/ioc/TypeConversionFirstPartTest.java @@ -0,0 +1,66 @@ +package org.springframework.test.ioc; + +import org.junit.Test; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.core.convert.support.StringToNumberConverterFactory; +import org.springframework.test.common.StringToBooleanConverter; +import org.springframework.test.common.StringToIntegerConverter; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author derekyi + * @date 2021/1/16 + */ +public class TypeConversionFirstPartTest { + + @Test + public void testStringToIntegerConverter() throws Exception { + StringToIntegerConverter converter = new StringToIntegerConverter(); + Integer num = converter.convert("8888"); + assertThat(num).isEqualTo(8888); + } + + @Test + public void testStringToNumberConverterFactory() throws Exception { + StringToNumberConverterFactory converterFactory = new StringToNumberConverterFactory(); + + Converter stringToIntegerConverter = converterFactory.getConverter(Integer.class); + Integer intNum = stringToIntegerConverter.convert("8888"); + assertThat(intNum).isEqualTo(8888); + + Converter stringToLongConverter = converterFactory.getConverter(Long.class); + Long longNum = stringToLongConverter.convert("8888"); + assertThat(longNum).isEqualTo(8888L); + } + + @Test + public void testGenericConverter() throws Exception { + StringToBooleanConverter converter = new StringToBooleanConverter(); + + Boolean flag = (Boolean) converter.convert("true", String.class, Boolean.class); + assertThat(flag).isTrue(); + } + + @Test + public void testGenericConversionService() throws Exception { + GenericConversionService conversionService = new GenericConversionService(); + conversionService.addConverter(new StringToIntegerConverter()); + + Integer intNum = conversionService.convert("8888", Integer.class); + assertThat(conversionService.canConvert(String.class, Integer.class)).isTrue(); + assertThat(intNum).isEqualTo(8888); + + conversionService.addConverterFactory(new StringToNumberConverterFactory()); + assertThat(conversionService.canConvert(String.class, Long.class)).isTrue(); + Long longNum = conversionService.convert("8888", Long.class); + assertThat(longNum).isEqualTo(8888L); + + conversionService.addConverter(new StringToBooleanConverter()); + assertThat(conversionService.canConvert(String.class, Boolean.class)).isTrue(); + Boolean flag = conversionService.convert("true", Boolean.class); + assertThat(flag).isTrue(); + } +} \ No newline at end of file diff --git a/src/test/java/org/springframework/test/ioc/TypeConversionSecondPartTest.java b/src/test/java/org/springframework/test/ioc/TypeConversionSecondPartTest.java new file mode 100644 index 0000000..d5ffe95 --- /dev/null +++ b/src/test/java/org/springframework/test/ioc/TypeConversionSecondPartTest.java @@ -0,0 +1,25 @@ +package org.springframework.test.ioc; + +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.test.bean.Car; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author derekyi + * @date 2021/1/17 + */ +public class TypeConversionSecondPartTest { + + @Test + public void testConversionService() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:type-conversion-second-part.xml"); + + Car car = applicationContext.getBean("car", Car.class); + assertThat(car.getPrice()).isEqualTo(1000000); + assertThat(car.getProduceDate()).isEqualTo(LocalDate.of(2021, 1, 1)); + } +} diff --git a/src/test/java/org/springframework/test/ioc/ValueAnnotationTest.java b/src/test/java/org/springframework/test/ioc/ValueAnnotationTest.java new file mode 100644 index 0000000..09e9211 --- /dev/null +++ b/src/test/java/org/springframework/test/ioc/ValueAnnotationTest.java @@ -0,0 +1,22 @@ +package org.springframework.test.ioc; + +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.test.bean.Car; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author derekyi + * @date 2020/12/27 + */ +public class ValueAnnotationTest { + + @Test + public void testValueAnnotation() throws Exception { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:value-annotation.xml"); + + Car car = applicationContext.getBean("car", Car.class); + assertThat(car.getBrand()).isEqualTo("lamborghini"); + } +} diff --git a/src/test/java/org/springframework/test/service/WorldService.java b/src/test/java/org/springframework/test/service/WorldService.java index fe3646f..6ee4e0f 100644 --- a/src/test/java/org/springframework/test/service/WorldService.java +++ b/src/test/java/org/springframework/test/service/WorldService.java @@ -7,4 +7,6 @@ public interface WorldService { void explode(); + + String getName(); } diff --git a/src/test/java/org/springframework/test/service/WorldServiceImpl.java b/src/test/java/org/springframework/test/service/WorldServiceImpl.java index dc15e0c..5e81d8a 100644 --- a/src/test/java/org/springframework/test/service/WorldServiceImpl.java +++ b/src/test/java/org/springframework/test/service/WorldServiceImpl.java @@ -6,8 +6,19 @@ */ public class WorldServiceImpl implements WorldService { + private String name; + @Override public void explode() { - System.out.println("The Earth is going to explode"); + System.out.println("The " + name + " is going to explode"); + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; } } diff --git a/src/test/resources/auto-proxy.xml b/src/test/resources/auto-proxy.xml index ffa3baa..2f65c7b 100644 --- a/src/test/resources/auto-proxy.xml +++ b/src/test/resources/auto-proxy.xml @@ -15,12 +15,21 @@ - + + + + + + + + + + - \ No newline at end of file + diff --git a/src/test/resources/autowired-annotation.xml b/src/test/resources/autowired-annotation.xml new file mode 100644 index 0000000..3f9f12d --- /dev/null +++ b/src/test/resources/autowired-annotation.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/circular-reference-with-proxy-bean.xml b/src/test/resources/circular-reference-with-proxy-bean.xml new file mode 100644 index 0000000..c479df9 --- /dev/null +++ b/src/test/resources/circular-reference-with-proxy-bean.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/circular-reference-without-proxy-bean.xml b/src/test/resources/circular-reference-without-proxy-bean.xml new file mode 100644 index 0000000..9babe20 --- /dev/null +++ b/src/test/resources/circular-reference-without-proxy-bean.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/lazy-test.xml b/src/test/resources/lazy-test.xml new file mode 100644 index 0000000..57ca4f8 --- /dev/null +++ b/src/test/resources/lazy-test.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/test/resources/package-scan.xml b/src/test/resources/package-scan.xml new file mode 100644 index 0000000..98cf664 --- /dev/null +++ b/src/test/resources/package-scan.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/src/test/resources/populate-proxy-bean-with-property-values.xml b/src/test/resources/populate-proxy-bean-with-property-values.xml new file mode 100644 index 0000000..20c0f01 --- /dev/null +++ b/src/test/resources/populate-proxy-bean-with-property-values.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/type-conversion-second-part.xml b/src/test/resources/type-conversion-second-part.xml new file mode 100644 index 0000000..2764aac --- /dev/null +++ b/src/test/resources/type-conversion-second-part.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/value-annotation.xml b/src/test/resources/value-annotation.xml new file mode 100644 index 0000000..3f9f12d --- /dev/null +++ b/src/test/resources/value-annotation.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file