├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── LICENSE ├── README.md ├── changelog.md ├── pom.xml └── src ├── main └── java │ └── us │ └── codecraft │ └── tinyioc │ ├── BeanReference.java │ ├── aop │ ├── AbstractAopProxy.java │ ├── AdvisedSupport.java │ ├── Advisor.java │ ├── AopProxy.java │ ├── AspectJAroundAdvice.java │ ├── AspectJAwareAdvisorAutoProxyCreator.java │ ├── AspectJExpressionPointcut.java │ ├── AspectJExpressionPointcutAdvisor.java │ ├── BeanFactoryAware.java │ ├── Cglib2AopProxy.java │ ├── ClassFilter.java │ ├── JdkDynamicAopProxy.java │ ├── MethodMatcher.java │ ├── Pointcut.java │ ├── PointcutAdvisor.java │ ├── ProxyFactory.java │ ├── ReflectiveMethodInvocation.java │ └── TargetSource.java │ ├── beans │ ├── AbstractBeanDefinitionReader.java │ ├── BeanDefinition.java │ ├── BeanDefinitionReader.java │ ├── BeanPostProcessor.java │ ├── PropertyValue.java │ ├── PropertyValues.java │ ├── factory │ │ ├── AbstractBeanFactory.java │ │ ├── AutowireCapableBeanFactory.java │ │ └── BeanFactory.java │ ├── io │ │ ├── Resource.java │ │ ├── ResourceLoader.java │ │ └── UrlResource.java │ └── xml │ │ └── XmlBeanDefinitionReader.java │ └── context │ ├── AbstractApplicationContext.java │ ├── ApplicationContext.java │ └── ClassPathXmlApplicationContext.java └── test ├── java └── us │ └── codecraft │ └── tinyioc │ ├── BeanFactoryTest.java │ ├── BeanInitializeLogger.java │ ├── HelloWorldService.java │ ├── HelloWorldServiceImpl.java │ ├── OutputService.java │ ├── OutputServiceImpl.java │ ├── aop │ ├── AspectJExpressionPointcutTest.java │ ├── Cglib2AopProxyTest.java │ ├── JdkDynamicAopProxyTest.java │ └── TimerInterceptor.java │ ├── beans │ ├── io │ │ └── ResourceLoaderTest.java │ └── xml │ │ └── XmlBeanDefinitionReaderTest.java │ └── context │ └── ApplicationContextTest.java └── resources ├── tinyioc-postbeanprocessor.xml └── tinyioc.xml /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | tiny-spring 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/test/java=UTF-8 4 | encoding//src/test/resources=UTF-8 5 | encoding/=UTF-8 6 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 3 | org.eclipse.jdt.core.compiler.compliance=1.6 4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 5 | org.eclipse.jdt.core.compiler.source=1.6 6 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tiny-spring 2 | ======= 3 | 4 | >A tiny IoC container refer to Spring. 5 | 6 | ## 关于 7 | 8 | `tiny-spring`是为了学习Spring的而开发的,可以认为是一个Spring的精简版。Spring的代码很多,层次复杂,阅读起来费劲。我尝试从使用功能的角度出发,参考Spring的实现,一步一步构建,最终完成一个精简版的Spring。有人把程序员与画家做比较,画家有门基本功叫临摹,tiny-spring可以算是一个程序的临摹版本-从自己的需求出发,进行程序设计,同时对著名项目进行参考。 9 | 10 | ## 功能 11 | 12 | 1. 支持singleton类型的bean,包括初始化、属性注入、以及依赖bean注入。 13 | 2. 可从xml中读取配置。 14 | 3. 可以使用Aspectj的方式进行AOP编写,支持接口和类代理。 15 | 16 | tiny-spring 17 | ===== 18 | 19 | # 第一部分:IoC容器 20 | 21 | ## 1.step1-最基本的容器 22 | 23 | git checkout step-1-container-register-and-get 24 | 25 | IoC最基本的角色有两个:容器(`BeanFactory`)和Bean本身。这里使用`BeanDefinition`来封装了bean对象,这样可以保存一些额外的元信息。测试代码: 26 | 27 | ```java 28 | // 1.初始化beanfactory 29 | BeanFactory beanFactory = new BeanFactory(); 30 | 31 | // 2.注入bean 32 | BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService()); 33 | beanFactory.registerBeanDefinition("helloWorldService", beanDefinition); 34 | 35 | // 3.获取bean 36 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 37 | helloWorldService.helloWorld(); 38 | ``` 39 | 40 | ## 2.step2-将bean创建放入工厂 41 | 42 | git checkout step-2-abstract-beanfactory-and-do-bean-initilizing-in-it 43 | 44 | step1中的bean是初始化好之后再set进去的,实际使用中,我们希望容器来管理bean的创建。于是我们将bean的初始化放入BeanFactory中。为了保证扩展性,我们使用Extract Interface的方法,将`BeanFactory`替换成接口,而使用`AbstractBeanFactory`和`AutowireCapableBeanFactory`作为其实现。"AutowireCapable"的意思是“可自动装配的”,为我们后面注入属性做准备。 45 | 46 | ```java 47 | // 1.初始化beanfactory 48 | BeanFactory beanFactory = new AutowireCapableBeanFactory(); 49 | 50 | // 2.注入bean 51 | BeanDefinition beanDefinition = new BeanDefinition(); 52 | beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService"); 53 | beanFactory.registerBeanDefinition("helloWorldService", beanDefinition); 54 | 55 | // 3.获取bean 56 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 57 | helloWorldService.helloWorld(); 58 | ``` 59 | 60 | ## 3.step3-为bean注入属性 61 | 62 | git checkout step-3-inject-bean-with-property 63 | 64 | 这一步,我们想要为bean注入属性。我们选择将属性注入信息保存成`PropertyValue`对象,并且保存到`BeanDefinition`中。这样在初始化bean的时候,我们就可以根据PropertyValue来进行bean属性的注入。Spring本身使用了setter来进行注入,这里为了代码简洁,我们使用Field的形式来注入。 65 | 66 | ```java 67 | // 1.初始化beanfactory 68 | BeanFactory beanFactory = new AutowireCapableBeanFactory(); 69 | 70 | // 2.bean定义 71 | BeanDefinition beanDefinition = new BeanDefinition(); 72 | beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService"); 73 | 74 | // 3.设置属性 75 | PropertyValues propertyValues = new PropertyValues(); 76 | propertyValues.addPropertyValue(new PropertyValue("text", "Hello World!")); 77 | beanDefinition.setPropertyValues(propertyValues); 78 | 79 | // 4.生成bean 80 | beanFactory.registerBeanDefinition("helloWorldService", beanDefinition); 81 | 82 | // 5.获取bean 83 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 84 | helloWorldService.helloWorld(); 85 | 86 | ``` 87 | 88 | ## 4.step4-读取xml配置来初始化bean 89 | 90 | git checkout step-4-config-beanfactory-with-xml 91 | 92 | 这么大一坨初始化代码让人心烦。这里的`BeanDefinition`只是一些配置,我们还是用xml来初始化吧。我们定义了`BeanDefinitionReader`初始化bean,它有一个实现是`XmlBeanDefinitionReader`。 93 | 94 | ```java 95 | // 1.读取配置 96 | XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); 97 | xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml"); 98 | 99 | // 2.初始化BeanFactory并注册bean 100 | BeanFactory beanFactory = new AutowireCapableBeanFactory(); 101 | for (Map.Entry beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { 102 | beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); 103 | } 104 | 105 | // 3.获取bean 106 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 107 | helloWorldService.helloWorld(); 108 | ``` 109 | 110 | ## 5.step5-为bean注入bean 111 | 112 | git checkout step-5-inject-bean-to-bean 113 | 114 | 使用xml配置之后,似乎里我们熟知的Spring更近了一步!但是现在有一个大问题没有解决:我们无法处理bean之间的依赖,无法将bean注入到bean中,所以它无法称之为完整的IoC容器!如何实现呢?我们定义一个`BeanReference`,来表示这个属性是对另一个bean的引用。这个在读取xml的时候初始化,并在初始化bean的时候,进行解析和真实bean的注入。 115 | 116 | ```java 117 | for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) { 118 | Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName()); 119 | declaredField.setAccessible(true); 120 | Object value = propertyValue.getValue(); 121 | if (value instanceof BeanReference) { 122 | BeanReference beanReference = (BeanReference) value; 123 | value = getBean(beanReference.getName()); 124 | } 125 | declaredField.set(bean, value); 126 | } 127 | ``` 128 | 129 | 同时为了解决循环依赖的问题,我们使用lazy-init的方式,将createBean的事情放到`getBean`的时候才执行,是不是一下子方便很多?这样在注入bean的时候,如果该属性对应的bean找不到,那么就先创建!因为总是先创建后注入,所以不会存在两个循环依赖的bean创建死锁的问题。 130 | 131 | ```java 132 | // 1.读取配置 133 | XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); 134 | xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml"); 135 | 136 | // 2.初始化BeanFactory并注册bean 137 | AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory(); 138 | for (Map.Entry beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { 139 | beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); 140 | } 141 | 142 | // 3.初始化bean 143 | beanFactory.preInstantiateSingletons(); 144 | 145 | // 4.获取bean 146 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 147 | helloWorldService.helloWorld(); 148 | ``` 149 | 150 | ## 6.step6-ApplicationContext登场 151 | 152 | git checkout step-6-invite-application-context 153 | 154 | 现在BeanFactory的功能齐全了,但是使用起来有点麻烦。于是我们引入熟悉的`ApplicationContext`接口,并在`AbstractApplicationContext`的`refresh()`方法中进行bean的初始化工作。 155 | 156 | ```java 157 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml"); 158 | HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService"); 159 | helloWorldService.helloWorld(); 160 | ``` 161 | 162 | 是不是非常熟悉?至此为止,我们的tiny-spring的IoC部分可说完工了。这部分的类、方法命名和作用,都是对应Spring中相应的组件。虽然代码量只有400多行,但是已经有了基本的IoC功能! 163 | 164 | # 第二部分:AOP及实现 165 | 166 | AOP相关概念较多,我不会一一列举,但是会在每一步对概念做一点解释。 167 | 168 | AOP分为配置(Pointcut,Advice),织入(Weave)两部分工作,当然还有一部分是将AOP整合到整个容器的生命周期中。 169 | 170 | ## 7.step7-使用JDK动态代理实现AOP织入 171 | git checkout step-7-method-interceptor-by-jdk-dynamic-proxy 172 | 173 | 织入(weave)相对简单,我们先从它开始。Spring AOP的织入点是`AopProxy`,它包含一个方法`Object getProxy()`来获取代理后的对象。 174 | 175 | 在Spring AOP中,我觉得最重要的两个角色,就是我们熟悉的`MethodInterceptor`和`MethodInvocation`(这两个角色都是AOP联盟的标准),它们分别对应AOP中两个基本角色:`Advice`和`Joinpoint`。Advice定义了在切点指定的逻辑,而Joinpoint则代表切点。 176 | 177 | ```java 178 | public interface MethodInterceptor extends Interceptor { 179 | 180 | Object invoke(MethodInvocation invocation) throws Throwable; 181 | } 182 | ``` 183 | 184 | Spring的AOP只支持方法级别的调用,所以其实在AopProxy里,我们只需要将MethodInterceptor放入对象的方法调用即可。 185 | 186 | 我们称被代理对象为`TargetSource`,而`AdvisedSupport`就是保存TargetSource和MethodInterceptor的元数据对象。这一步我们先实现一个基于JDK动态代理的`JdkDynamicAopProxy`,它可以对接口进行代理。于是我们就有了基本的织入功能。 187 | 188 | ```java 189 | @Test 190 | public void testInterceptor() throws Exception { 191 | // --------- helloWorldService without AOP 192 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml"); 193 | HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService"); 194 | helloWorldService.helloWorld(); 195 | 196 | // --------- helloWorldService with AOP 197 | // 1. 设置被代理对象(Joinpoint) 198 | AdvisedSupport advisedSupport = new AdvisedSupport(); 199 | TargetSource targetSource = new TargetSource(helloWorldService, HelloWorldServiceImpl.class, 200 | HelloWorldService.class); 201 | advisedSupport.setTargetSource(targetSource); 202 | 203 | // 2. 设置拦截器(Advice) 204 | TimerInterceptor timerInterceptor = new TimerInterceptor(); 205 | advisedSupport.setMethodInterceptor(timerInterceptor); 206 | 207 | // 3. 创建代理(Proxy) 208 | JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport); 209 | HelloWorldService helloWorldServiceProxy = (HelloWorldService) jdkDynamicAopProxy.getProxy(); 210 | 211 | // 4. 基于AOP的调用 212 | helloWorldServiceProxy.helloWorld(); 213 | 214 | } 215 | ``` 216 | 217 | ## 8.step8-使用AspectJ管理切面 218 | git checkout step-8-invite-pointcut-and-aspectj 219 | 220 | 完成了织入之后,我们要考虑另外一个问题:对什么类以及什么方法进行AOP?对于“在哪切”这一问题的定义,我们又叫做“Pointcut”。Spring中关于Pointcut包含两个角色:`ClassFilter`和`MethodMatcher`,分别是对类和方法做匹配。Pointcut有很多种定义方法,例如类名匹配、正则匹配等,但是应用比较广泛的应该是和`AspectJ`表达式的方式。 221 | 222 | `AspectJ`是一个“对Java的AOP增强”。它最早是其实是一门语言,我们跟写Java代码一样写它,然后静态编译之后,就有了AOP的功能。下面是一段AspectJ代码: 223 | 224 | ```java 225 | aspect PointObserving { 226 | private Vector Point.observers = new Vector(); 227 | 228 | public static void addObserver(Point p, Screen s) { 229 | p.observers.add(s); 230 | } 231 | public static void removeObserver(Point p, Screen s) { 232 | p.observers.remove(s); 233 | } 234 | ... 235 | } 236 | ``` 237 | 238 | 这种方式无疑太重了,为了AOP,还要适应一种语言?所以现在使用也不多,但是它的`Pointcut`表达式被Spring借鉴了过来。于是我们实现了一个`AspectJExpressionPointcut`: 239 | 240 | ```java 241 | @Test 242 | public void testMethodInterceptor() throws Exception { 243 | String expression = "execution(* us.codecraft.tinyioc.*.*(..))"; 244 | AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut(); 245 | aspectJExpressionPointcut.setExpression(expression); 246 | boolean matches = aspectJExpressionPointcut.getMethodMatcher().matches(HelloWorldServiceImpl.class.getDeclaredMethod("helloWorld"),HelloWorldServiceImpl.class); 247 | Assert.assertTrue(matches); 248 | } 249 | ``` 250 | 251 | ## 9.step9-将AOP融入Bean的创建过程 252 | git checkout step-9-auto-create-aop-proxy 253 | 254 | 万事俱备,只欠东风!现在我们有了Pointcut和Weave技术,一个AOP已经算是完成了,但是它还没有结合到Spring中去。怎么进行结合呢?Spring给了一个巧妙的答案:使用`BeanPostProcessor`。 255 | 256 | BeanPostProcessor是BeanFactory提供的,在Bean初始化过程中进行扩展的接口。只要你的Bean实现了`BeanPostProcessor`接口,那么Spring在初始化时,会优先找到它们,并且在Bean的初始化过程中,调用这个接口,从而实现对BeanFactory核心无侵入的扩展。 257 | 258 | 那么我们的AOP是怎么实现的呢?我们知道,在AOP的xml配置中,我们会写这样一句话: 259 | 260 | ```xml 261 | 262 | ``` 263 | 264 | 它其实相当于: 265 | 266 | ```xml 267 | 268 | ``` 269 | 270 | `AspectJAwareAdvisorAutoProxyCreator`就是AspectJ方式实现织入的核心。它其实是一个BeanPostProcessor。在这里它会扫描所有Pointcut,并对bean做织入。 271 | 272 | 为了简化xml配置,我在tiny-spring中直接使用Bean的方式,而不是用aop前缀进行配置: 273 | 274 | ```xml 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | ``` 284 | 285 | `TimerInterceptor`实现了`MethodInterceptor`(实际上Spring中还有`Advice`这样一个角色,为了简单,就直接用MethodInterceptor了)。 286 | 287 | 至此,一个AOP基本完工。 288 | 289 | 290 | ## 10.step10-使用CGLib进行类的织入 291 | git checkout step-10-invite-cglib-and-aopproxy-factory 292 | 293 | 前面的JDK动态代理只能对接口进行代理,对于类则无能为力。这里我们需要一些字节码操作技术。这方面大概有几种选择:`ASM`,`CGLib`和`javassist`,后两者是对`ASM`的封装。Spring中使用了CGLib。 294 | 295 | 在这一步,我们还要定义一个工厂类`ProxyFactory`,用于根据TargetSource类型自动创建代理,这样就需要在调用者代码中去进行判断。 296 | 297 | 另外我们实现了`Cglib2AopProxy`,使用方式和`JdkDynamicAopProxy`是完全相同的。 298 | 299 | *有一个细节是CGLib创建的代理是没有注入属性的, 300 | Spring的解决方式是:CGLib仅作代理,任何属性都保存在TargetSource中,使用MethodInterceptor=>TargetSource的方式进行调用。* 301 | 302 | 至此,AOP部分完工。 303 | 304 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | tiny-spring 2 | ===== 3 | 4 | # 第一部分:IoC容器 5 | 6 | ## 1.step1-最基本的容器 7 | 8 | git checkout step-1-container-register-and-get 9 | 10 | IoC最基本的角色有两个:容器(`BeanFactory`)和Bean本身。这里使用`BeanDefinition`来封装了bean对象,这样可以保存一些额外的元信息。测试代码: 11 | 12 | ```java 13 | // 1.初始化beanfactory 14 | BeanFactory beanFactory = new BeanFactory(); 15 | 16 | // 2.注入bean 17 | BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService()); 18 | beanFactory.registerBeanDefinition("helloWorldService", beanDefinition); 19 | 20 | // 3.获取bean 21 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 22 | helloWorldService.helloWorld(); 23 | ``` 24 | 25 | ## 2.step2-将bean创建放入工厂 26 | 27 | git checkout step-2-abstract-beanfactory-and-do-bean-initilizing-in-it 28 | 29 | step1中的bean是初始化好之后再set进去的,实际使用中,我们希望容器来管理bean的创建。于是我们将bean的初始化放入BeanFactory中。为了保证扩展性,我们使用Extract Interface的方法,将`BeanFactory`替换成接口,而使用`AbstractBeanFactory`和`AutowireCapableBeanFactory`作为其实现。"AutowireCapable"的意思是“可自动装配的”,为我们后面注入属性做准备。 30 | 31 | ```java 32 | // 1.初始化beanfactory 33 | BeanFactory beanFactory = new AutowireCapableBeanFactory(); 34 | 35 | // 2.注入bean 36 | BeanDefinition beanDefinition = new BeanDefinition(); 37 | beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService"); 38 | beanFactory.registerBeanDefinition("helloWorldService", beanDefinition); 39 | 40 | // 3.获取bean 41 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 42 | helloWorldService.helloWorld(); 43 | ``` 44 | 45 | ## 3.step3-为bean注入属性 46 | 47 | git checkout step-3-inject-bean-with-property 48 | 49 | 这一步,我们想要为bean注入属性。我们选择将属性注入信息保存成`PropertyValue`对象,并且保存到`BeanDefinition`中。这样在初始化bean的时候,我们就可以根据PropertyValue来进行bean属性的注入。Spring本身使用了setter来进行注入,这里为了代码简洁,我们使用Field的形式来注入。 50 | 51 | ```java 52 | // 1.初始化beanfactory 53 | BeanFactory beanFactory = new AutowireCapableBeanFactory(); 54 | 55 | // 2.bean定义 56 | BeanDefinition beanDefinition = new BeanDefinition(); 57 | beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService"); 58 | 59 | // 3.设置属性 60 | PropertyValues propertyValues = new PropertyValues(); 61 | propertyValues.addPropertyValue(new PropertyValue("text", "Hello World!")); 62 | beanDefinition.setPropertyValues(propertyValues); 63 | 64 | // 4.生成bean 65 | beanFactory.registerBeanDefinition("helloWorldService", beanDefinition); 66 | 67 | // 5.获取bean 68 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 69 | helloWorldService.helloWorld(); 70 | 71 | ``` 72 | 73 | ## 4.step4-读取xml配置来初始化bean 74 | 75 | git checkout step-4-config-beanfactory-with-xml 76 | 77 | 这么大一坨初始化代码让人心烦。这里的`BeanDefinition`只是一些配置,我们还是用xml来初始化吧。我们定义了`BeanDefinitionReader`初始化bean,它有一个实现是`XmlBeanDefinitionReader`。 78 | 79 | ```java 80 | // 1.读取配置 81 | XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); 82 | xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml"); 83 | 84 | // 2.初始化BeanFactory并注册bean 85 | BeanFactory beanFactory = new AutowireCapableBeanFactory(); 86 | for (Map.Entry beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { 87 | beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); 88 | } 89 | 90 | // 3.获取bean 91 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 92 | helloWorldService.helloWorld(); 93 | ``` 94 | 95 | ## 5.step5-为bean注入bean 96 | 97 | git checkout step-5-inject-bean-to-bean 98 | 99 | 使用xml配置之后,似乎里我们熟知的Spring更近了一步!但是现在有一个大问题没有解决:我们无法处理bean之间的依赖,无法将bean注入到bean中,所以它无法称之为完整的IoC容器!如何实现呢?我们定义一个`BeanReference`,来表示这个属性是对另一个bean的引用。这个在读取xml的时候初始化,并在初始化bean的时候,进行解析和真实bean的注入。 100 | 101 | ```java 102 | for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) { 103 | Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName()); 104 | declaredField.setAccessible(true); 105 | Object value = propertyValue.getValue(); 106 | if (value instanceof BeanReference) { 107 | BeanReference beanReference = (BeanReference) value; 108 | value = getBean(beanReference.getName()); 109 | } 110 | declaredField.set(bean, value); 111 | } 112 | ``` 113 | 114 | 同时为了解决循环依赖的问题,我们使用lazy-init的方式,将createBean的事情放到`getBean`的时候才执行,是不是一下子方便很多?这样在注入bean的时候,如果该属性对应的bean找不到,那么就先创建!因为总是先创建后注入,所以不会存在两个循环依赖的bean创建死锁的问题。 115 | 116 | ```java 117 | // 1.读取配置 118 | XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); 119 | xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml"); 120 | 121 | // 2.初始化BeanFactory并注册bean 122 | AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory(); 123 | for (Map.Entry beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { 124 | beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); 125 | } 126 | 127 | // 3.初始化bean 128 | beanFactory.preInstantiateSingletons(); 129 | 130 | // 4.获取bean 131 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 132 | helloWorldService.helloWorld(); 133 | ``` 134 | 135 | ## 6.step6-ApplicationContext登场 136 | 137 | git checkout step-6-invite-application-context 138 | 139 | 现在BeanFactory的功能齐全了,但是使用起来有点麻烦。于是我们引入熟悉的`ApplicationContext`接口,并在`AbstractApplicationContext`的`refresh()`方法中进行bean的初始化工作。 140 | 141 | ```java 142 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml"); 143 | HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService"); 144 | helloWorldService.helloWorld(); 145 | ``` 146 | 147 | 是不是非常熟悉?至此为止,我们的tiny-spring的IoC部分可说完工了。这部分的类、方法命名和作用,都是对应Spring中相应的组件。虽然代码量只有400多行,但是已经有了基本的IoC功能! 148 | 149 | # 第二部分:AOP及实现 150 | 151 | AOP相关概念较多,我不会一一列举,但是会在每一步对概念做一点解释。 152 | 153 | AOP分为配置(Pointcut,Advice),织入(Weave)两部分工作,当然还有一部分是将AOP整合到整个容器的生命周期中。 154 | 155 | ## 7.step7-使用JDK动态代理实现AOP织入 156 | git checkout step-7-method-interceptor-by-jdk-dynamic-proxy 157 | 158 | 织入(weave)相对简单,我们先从它开始。Spring AOP的织入点是`AopProxy`,它包含一个方法`Object getProxy()`来获取代理后的对象。 159 | 160 | 在Spring AOP中,我觉得最重要的两个角色,就是我们熟悉的`MethodInterceptor`和`MethodInvocation`(这两个角色都是AOP联盟的标准),它们分别对应AOP中两个基本角色:`Advice`和`Joinpoint`。Advice定义了在切点指定的逻辑,而Joinpoint则代表切点。 161 | 162 | ```java 163 | public interface MethodInterceptor extends Interceptor { 164 | 165 | Object invoke(MethodInvocation invocation) throws Throwable; 166 | } 167 | ``` 168 | 169 | Spring的AOP只支持方法级别的调用,所以其实在AopProxy里,我们只需要将MethodInterceptor放入对象的方法调用即可。 170 | 171 | 我们称被代理对象为`TargetSource`,而`AdvisedSupport`就是保存TargetSource和MethodInterceptor的元数据对象。这一步我们先实现一个基于JDK动态代理的`JdkDynamicAopProxy`,它可以对接口进行代理。于是我们就有了基本的织入功能。 172 | 173 | ```java 174 | @Test 175 | public void testInterceptor() throws Exception { 176 | // --------- helloWorldService without AOP 177 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml"); 178 | HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService"); 179 | helloWorldService.helloWorld(); 180 | 181 | // --------- helloWorldService with AOP 182 | // 1. 设置被代理对象(Joinpoint) 183 | AdvisedSupport advisedSupport = new AdvisedSupport(); 184 | TargetSource targetSource = new TargetSource(helloWorldService, HelloWorldServiceImpl.class, 185 | HelloWorldService.class); 186 | advisedSupport.setTargetSource(targetSource); 187 | 188 | // 2. 设置拦截器(Advice) 189 | TimerInterceptor timerInterceptor = new TimerInterceptor(); 190 | advisedSupport.setMethodInterceptor(timerInterceptor); 191 | 192 | // 3. 创建代理(Proxy) 193 | JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport); 194 | HelloWorldService helloWorldServiceProxy = (HelloWorldService) jdkDynamicAopProxy.getProxy(); 195 | 196 | // 4. 基于AOP的调用 197 | helloWorldServiceProxy.helloWorld(); 198 | 199 | } 200 | ``` 201 | 202 | ## 8.step8-使用AspectJ管理切面 203 | git checkout step-8-invite-pointcut-and-aspectj 204 | 205 | 完成了织入之后,我们要考虑另外一个问题:对什么类以及什么方法进行AOP?对于“在哪切”这一问题的定义,我们又叫做“Pointcut”。Spring中关于Pointcut包含两个角色:`ClassFilter`和`MethodMatcher`,分别是对类和方法做匹配。Pointcut有很多种定义方法,例如类名匹配、正则匹配等,但是应用比较广泛的应该是和`AspectJ`表达式的方式。 206 | 207 | `AspectJ`是一个“对Java的AOP增强”。它最早是其实是一门语言,我们跟写Java代码一样写它,然后静态编译之后,就有了AOP的功能。下面是一段AspectJ代码: 208 | 209 | ```java 210 | aspect PointObserving { 211 | private Vector Point.observers = new Vector(); 212 | 213 | public static void addObserver(Point p, Screen s) { 214 | p.observers.add(s); 215 | } 216 | public static void removeObserver(Point p, Screen s) { 217 | p.observers.remove(s); 218 | } 219 | ... 220 | } 221 | ``` 222 | 223 | 这种方式无疑太重了,为了AOP,还要适应一种语言?所以现在使用也不多,但是它的`Pointcut`表达式被Spring借鉴了过来。于是我们实现了一个`AspectJExpressionPointcut`: 224 | 225 | ```java 226 | @Test 227 | public void testMethodInterceptor() throws Exception { 228 | String expression = "execution(* us.codecraft.tinyioc.*.*(..))"; 229 | AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut(); 230 | aspectJExpressionPointcut.setExpression(expression); 231 | boolean matches = aspectJExpressionPointcut.getMethodMatcher().matches(HelloWorldServiceImpl.class.getDeclaredMethod("helloWorld"),HelloWorldServiceImpl.class); 232 | Assert.assertTrue(matches); 233 | } 234 | ``` 235 | 236 | ## 9.step9-将AOP融入Bean的创建过程 237 | git checkout step-9-auto-create-aop-proxy 238 | 239 | 万事俱备,只欠东风!现在我们有了Pointcut和Weave技术,一个AOP已经算是完成了,但是它还没有结合到Spring中去。怎么进行结合呢?Spring给了一个巧妙的答案:使用`BeanPostProcessor`。 240 | 241 | BeanPostProcessor是BeanFactory提供的,在Bean初始化过程中进行扩展的接口。只要你的Bean实现了`BeanPostProcessor`接口,那么Spring在初始化时,会优先找到它们,并且在Bean的初始化过程中,调用这个接口,从而实现对BeanFactory核心无侵入的扩展。 242 | 243 | 那么我们的AOP是怎么实现的呢?我们知道,在AOP的xml配置中,我们会写这样一句话: 244 | 245 | ```xml 246 | 247 | ``` 248 | 249 | 它其实相当于: 250 | 251 | ```xml 252 | 253 | ``` 254 | 255 | `AspectJAwareAdvisorAutoProxyCreator`就是AspectJ方式实现织入的核心。它其实是一个BeanPostProcessor。在这里它会扫描所有Pointcut,并对bean做织入。 256 | 257 | 为了简化xml配置,我在tiny-spring中直接使用Bean的方式,而不是用aop前缀进行配置: 258 | 259 | ```xml 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | ``` 269 | 270 | `TimerInterceptor`实现了`MethodInterceptor`(实际上Spring中还有`Advice`这样一个角色,为了简单,就直接用MethodInterceptor了)。 271 | 272 | 至此,一个AOP基本完工。 273 | 274 | 275 | ## 10.step10-使用CGLib进行类的织入 276 | git checkout step-10-invite-cglib-and-aopproxy-factory 277 | 278 | 前面的JDK动态代理只能对接口进行代理,对于类则无能为力。这里我们需要一些字节码操作技术。这方面大概有几种选择:`ASM`,`CGLib`和`javassist`,后两者是对`ASM`的封装。Spring中使用了CGLib。 279 | 280 | 在这一步,我们还要定义一个工厂类`ProxyFactory`,用于根据TargetSource类型自动创建代理,这样就需要在调用者代码中去进行判断。 281 | 282 | 另外我们实现了`Cglib2AopProxy`,使用方式和`JdkDynamicAopProxy`是完全相同的。 283 | 284 | *有一个细节是CGLib创建的代理是没有注入属性的, 285 | Spring的解决方式是:CGLib仅作代理,任何属性都保存在TargetSource中,使用MethodInterceptor=>TargetSource的方式进行调用。* 286 | 287 | 至此,AOP部分完工。 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | us.codecraft 4 | tiny-spring 5 | 0.0.1-SNAPSHOT 6 | 4.0.0 7 | jar 8 | 9 | UTF-8 10 | UTF-8 11 | 12 | tiny-spring 13 | 14 | A tiny implementation of Spring for study. 15 | 16 | https://github.com/code4craft/xsoup/ 17 | 18 | 19 | code4craft 20 | Yihua huang 21 | code4crafer@gmail.com 22 | 23 | 24 | 25 | 26 | The MIT License 27 | repo 28 | 29 | 30 | 31 | 32 | 33 | junit 34 | junit 35 | 4.7 36 | test 37 | 38 | 39 | aopalliance 40 | aopalliance 41 | 1.0 42 | 43 | 44 | org.aspectj 45 | aspectjweaver 46 | 1.6.11 47 | 48 | 49 | cglib 50 | cglib-nodep 51 | 2.1_3 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-compiler-plugin 60 | 3.1 61 | 62 | 1.6 63 | 1.6 64 | UTF-8 65 | 66 | 67 | 68 | org.apache.maven.plugins 69 | maven-resources-plugin 70 | 2.6 71 | 72 | UTF-8 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-source-plugin 78 | 2.2.1 79 | 80 | 81 | attach-sources 82 | 83 | jar 84 | 85 | 86 | 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-javadoc-plugin 91 | 2.9.1 92 | 93 | UTF-8 94 | 95 | 96 | 97 | attach-javadocs 98 | 99 | jar 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/BeanReference.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | */ 6 | /** 7 | * 保存引用,bean的引用 8 | * @author tengyu 9 | * 10 | */ 11 | public class BeanReference { 12 | 13 | private String name; 14 | 15 | private Object bean; 16 | 17 | public BeanReference(String name) { 18 | this.name = name; 19 | } 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public void setName(String name) { 26 | this.name = name; 27 | } 28 | 29 | public Object getBean() { 30 | return bean; 31 | } 32 | 33 | public void setBean(Object bean) { 34 | this.bean = bean; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/AbstractAopProxy.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | */ 6 | public abstract class AbstractAopProxy implements AopProxy { 7 | 8 | 9 | protected AdvisedSupport advised; 10 | 11 | //传入通知事件 12 | public AbstractAopProxy(AdvisedSupport advised) { 13 | this.advised = advised; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/AdvisedSupport.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.aopalliance.intercept.MethodInterceptor; 4 | 5 | /** 6 | * 代理相关的元数据 7 | * @author yihua.huang@dianping.com 8 | */ 9 | public class AdvisedSupport { 10 | 11 | //原始对象的实例,类的Class和接口 12 | private TargetSource targetSource; 13 | 14 | //方法拦截器 15 | private MethodInterceptor methodInterceptor; 16 | 17 | //方法匹配器?拦截那些方法,这个就是去找那些方法的。 18 | private MethodMatcher methodMatcher; 19 | 20 | public TargetSource getTargetSource() { 21 | return targetSource; 22 | } 23 | 24 | public void setTargetSource(TargetSource targetSource) { 25 | this.targetSource = targetSource; 26 | } 27 | 28 | public MethodInterceptor getMethodInterceptor() { 29 | return methodInterceptor; 30 | } 31 | 32 | public void setMethodInterceptor(MethodInterceptor methodInterceptor) { 33 | this.methodInterceptor = methodInterceptor; 34 | } 35 | 36 | public MethodMatcher getMethodMatcher() { 37 | return methodMatcher; 38 | } 39 | 40 | public void setMethodMatcher(MethodMatcher methodMatcher) { 41 | this.methodMatcher = methodMatcher; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/Advisor.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.aopalliance.aop.Advice; 4 | 5 | /** 6 | * @author yihua.huang@dianping.com 7 | */ 8 | public interface Advisor { 9 | 10 | //获取通知事件 11 | Advice getAdvice(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/AopProxy.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | /** 4 | * AOP代理 5 | * @author yihua.huang@dianping.com 6 | */ 7 | //获取aop代理,实例对象? 8 | public interface AopProxy { 9 | 10 | Object getProxy(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/AspectJAroundAdvice.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.aopalliance.aop.Advice; 4 | import org.aopalliance.intercept.MethodInterceptor; 5 | import org.aopalliance.intercept.MethodInvocation; 6 | import us.codecraft.tinyioc.beans.factory.BeanFactory; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * @author yihua.huang@dianping.com 12 | */ 13 | /** 14 | * 环绕通知事件 15 | * @author tengyu 16 | * 17 | */ 18 | public class AspectJAroundAdvice implements Advice, MethodInterceptor { 19 | 20 | private BeanFactory beanFactory; 21 | 22 | private Method aspectJAdviceMethod; 23 | 24 | private String aspectInstanceName; 25 | 26 | @Override 27 | public Object invoke(MethodInvocation invocation) throws Throwable { 28 | return aspectJAdviceMethod.invoke(beanFactory.getBean(aspectInstanceName), invocation); 29 | } 30 | 31 | public BeanFactory getBeanFactory() { 32 | return beanFactory; 33 | } 34 | 35 | public void setBeanFactory(BeanFactory beanFactory) { 36 | this.beanFactory = beanFactory; 37 | } 38 | 39 | public Method getAspectJAdviceMethod() { 40 | return aspectJAdviceMethod; 41 | } 42 | 43 | public void setAspectJAdviceMethod(Method aspectJAdviceMethod) { 44 | this.aspectJAdviceMethod = aspectJAdviceMethod; 45 | } 46 | 47 | public String getAspectInstanceName() { 48 | return aspectInstanceName; 49 | } 50 | 51 | public void setAspectInstanceName(String aspectInstanceName) { 52 | this.aspectInstanceName = aspectInstanceName; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/AspectJAwareAdvisorAutoProxyCreator.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.aopalliance.intercept.MethodInterceptor; 4 | import us.codecraft.tinyioc.beans.BeanPostProcessor; 5 | import us.codecraft.tinyioc.beans.factory.AbstractBeanFactory; 6 | import us.codecraft.tinyioc.beans.factory.BeanFactory; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author yihua.huang@dianping.com 12 | */ 13 | //?未知 14 | public class AspectJAwareAdvisorAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware { 15 | 16 | private AbstractBeanFactory beanFactory; 17 | 18 | @Override 19 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception { 20 | return bean; 21 | } 22 | 23 | @Override 24 | public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception { 25 | if (bean instanceof AspectJExpressionPointcutAdvisor) { 26 | return bean; 27 | } 28 | if (bean instanceof MethodInterceptor) { 29 | return bean; 30 | } 31 | List advisors = beanFactory 32 | .getBeansForType(AspectJExpressionPointcutAdvisor.class); 33 | for (AspectJExpressionPointcutAdvisor advisor : advisors) { 34 | if (advisor.getPointcut().getClassFilter().matches(bean.getClass())) { 35 | ProxyFactory advisedSupport = new ProxyFactory(); 36 | advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice()); 37 | advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher()); 38 | 39 | TargetSource targetSource = new TargetSource(bean, bean.getClass(), bean.getClass().getInterfaces()); 40 | advisedSupport.setTargetSource(targetSource); 41 | 42 | return advisedSupport.getProxy(); 43 | } 44 | } 45 | return bean; 46 | } 47 | 48 | @Override 49 | public void setBeanFactory(BeanFactory beanFactory) throws Exception { 50 | this.beanFactory = (AbstractBeanFactory) beanFactory; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/AspectJExpressionPointcut.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.aspectj.weaver.tools.PointcutExpression; 4 | import org.aspectj.weaver.tools.PointcutParser; 5 | import org.aspectj.weaver.tools.PointcutPrimitive; 6 | import org.aspectj.weaver.tools.ShadowMatch; 7 | 8 | import java.lang.reflect.Method; 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | 12 | /** 13 | * @author yihua.huang@dianping.com 14 | */ 15 | /** 16 | * 切点表达式,调用aspectj的类库,匹配要拦截方法 17 | * @author tengyu 18 | * 19 | */ 20 | public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher { 21 | 22 | private PointcutParser pointcutParser; 23 | 24 | private String expression; 25 | 26 | private PointcutExpression pointcutExpression; 27 | 28 | private static final Set DEFAULT_SUPPORTED_PRIMITIVES = new HashSet(); 29 | 30 | static { 31 | DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); 32 | DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS); 33 | DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE); 34 | DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS); 35 | DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET); 36 | DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN); 37 | DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION); 38 | DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN); 39 | DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS); 40 | DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET); 41 | } 42 | 43 | public AspectJExpressionPointcut() { 44 | this(DEFAULT_SUPPORTED_PRIMITIVES); 45 | } 46 | 47 | public AspectJExpressionPointcut(Set supportedPrimitives) { 48 | pointcutParser = PointcutParser 49 | .getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives); 50 | } 51 | 52 | protected void checkReadyToMatch() { 53 | if (pointcutExpression == null) { 54 | pointcutExpression = buildPointcutExpression(); 55 | } 56 | } 57 | 58 | private PointcutExpression buildPointcutExpression() { 59 | return pointcutParser.parsePointcutExpression(expression); 60 | } 61 | 62 | public void setExpression(String expression) { 63 | this.expression = expression; 64 | } 65 | 66 | @Override 67 | public ClassFilter getClassFilter() { 68 | return this; 69 | } 70 | 71 | @Override 72 | public MethodMatcher getMethodMatcher() { 73 | return this; 74 | } 75 | 76 | @Override 77 | public boolean matches(Class targetClass) { 78 | checkReadyToMatch(); 79 | return pointcutExpression.couldMatchJoinPointsInType(targetClass); 80 | } 81 | 82 | @Override 83 | public boolean matches(Method method, Class targetClass) { 84 | checkReadyToMatch(); 85 | ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method); 86 | if (shadowMatch.alwaysMatches()) { 87 | return true; 88 | } else if (shadowMatch.neverMatches()) { 89 | return false; 90 | } 91 | // TODO:其他情况不判断了!见org.springframework.aop.aspectj.RuntimeTestWalker 92 | return false; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/AspectJExpressionPointcutAdvisor.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.aopalliance.aop.Advice; 4 | 5 | /** 6 | * @author yihua.huang@dianping.com 7 | */ 8 | /** 9 | * 切点通知方法?未知 10 | * @author tengyu 11 | * 12 | */ 13 | public class AspectJExpressionPointcutAdvisor implements PointcutAdvisor { 14 | 15 | private AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); 16 | 17 | private Advice advice; 18 | 19 | public void setAdvice(Advice advice) { 20 | this.advice = advice; 21 | } 22 | 23 | public void setExpression(String expression) { 24 | this.pointcut.setExpression(expression); 25 | } 26 | 27 | @Override 28 | public Advice getAdvice() { 29 | return advice; 30 | } 31 | 32 | @Override 33 | public Pointcut getPointcut() { 34 | return pointcut; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/BeanFactoryAware.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import us.codecraft.tinyioc.beans.factory.BeanFactory; 4 | 5 | /** 6 | * @author yihua.huang@dianping.com 7 | */ 8 | /** 9 | * 设置bean工厂 10 | * @author tengyu 11 | * 12 | */ 13 | public interface BeanFactoryAware { 14 | 15 | void setBeanFactory(BeanFactory beanFactory) throws Exception; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/Cglib2AopProxy.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import net.sf.cglib.proxy.Enhancer; 4 | import net.sf.cglib.proxy.MethodInterceptor; 5 | import net.sf.cglib.proxy.MethodProxy; 6 | 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | * @author yihua.huang@dianping.com 11 | */ 12 | public class Cglib2AopProxy extends AbstractAopProxy { 13 | 14 | public Cglib2AopProxy(AdvisedSupport advised) { 15 | super(advised); 16 | } 17 | 18 | //通过cglib类库创建了一个代理类的实例 19 | @Override 20 | public Object getProxy() { 21 | Enhancer enhancer = new Enhancer(); 22 | enhancer.setSuperclass(advised.getTargetSource().getTargetClass()); 23 | enhancer.setInterfaces(advised.getTargetSource().getInterfaces()); 24 | //设置代理类的通知方法,相当于设置拦截器方法 25 | enhancer.setCallback(new DynamicAdvisedInterceptor(advised)); 26 | Object enhanced = enhancer.create(); 27 | return enhanced; 28 | } 29 | 30 | //方法拦截器 31 | private static class DynamicAdvisedInterceptor implements MethodInterceptor { 32 | 33 | private AdvisedSupport advised; 34 | 35 | private org.aopalliance.intercept.MethodInterceptor delegateMethodInterceptor; 36 | 37 | private DynamicAdvisedInterceptor(AdvisedSupport advised) { 38 | this.advised = advised; 39 | this.delegateMethodInterceptor = advised.getMethodInterceptor(); 40 | } 41 | 42 | //调用代理类的方法(代理类与原始类是父子关系,还有一种是兄弟关系,调用实质是调用原始类的方法) 43 | @Override 44 | public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 45 | if (advised.getMethodMatcher() == null 46 | || advised.getMethodMatcher().matches(method, advised.getTargetSource().getTargetClass())) { 47 | //这里也应该是先调用拦截方法,然后调用原始对象的方法 48 | return delegateMethodInterceptor.invoke(new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, args, proxy)); 49 | } 50 | return new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, args, proxy).proceed(); 51 | } 52 | } 53 | 54 | private static class CglibMethodInvocation extends ReflectiveMethodInvocation { 55 | 56 | private final MethodProxy methodProxy; 57 | 58 | public CglibMethodInvocation(Object target, Method method, Object[] args, MethodProxy methodProxy) { 59 | super(target, method, args); 60 | this.methodProxy = methodProxy; 61 | } 62 | 63 | @Override 64 | public Object proceed() throws Throwable { 65 | return this.methodProxy.invoke(this.target, this.arguments); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/ClassFilter.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | */ 6 | /** 7 | * 匹配Class实例 8 | * @author tengyu 9 | * 10 | */ 11 | public interface ClassFilter { 12 | 13 | boolean matches(Class targetClass); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/JdkDynamicAopProxy.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.aopalliance.intercept.MethodInterceptor; 4 | 5 | import java.lang.reflect.InvocationHandler; 6 | import java.lang.reflect.Method; 7 | import java.lang.reflect.Proxy; 8 | 9 | /** 10 | * 基于jdk的动态代理 11 | * 12 | * @author yihua.huang@dianping.com 13 | */ 14 | public class JdkDynamicAopProxy extends AbstractAopProxy implements InvocationHandler { 15 | 16 | public JdkDynamicAopProxy(AdvisedSupport advised) { 17 | super(advised); 18 | } 19 | 20 | //获取代理类的实例,调用代理的类的方法,会自定先调用拦截器方法吗? 21 | @Override 22 | public Object getProxy() { 23 | return Proxy.newProxyInstance(getClass().getClassLoader(), advised.getTargetSource().getInterfaces(), this); 24 | } 25 | 26 | //调用代理 类方法,实质调用原始类的方法 27 | @Override 28 | public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { 29 | //提取拦截method方法 30 | MethodInterceptor methodInterceptor = advised.getMethodInterceptor(); 31 | //比较传入的方法和原始对象的方法是否一致,如果一致则调用传入的方法,那拦截的方法什么时候调用? 32 | if (advised.getMethodMatcher() != null 33 | && advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { 34 | //这里应该是先调用拦截的方法,然后调用原始对象的方法。但是一般括号里的东西不是优先吗?括号里面好像就只有赋值操作而已。 35 | return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(),method, args)); 36 | } else { 37 | return method.invoke(advised.getTargetSource().getTarget(), args); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/MethodMatcher.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * @author yihua.huang@dianping.com 7 | */ 8 | /** 9 | * 匹配方法 10 | * @author tengyu 11 | * 12 | */ 13 | public interface MethodMatcher { 14 | 15 | boolean matches(Method method, Class targetClass); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/Pointcut.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | */ 6 | /** 7 | * 切点接口,Class匹配和方法匹配 8 | * @author tengyu 9 | * 10 | */ 11 | public interface Pointcut { 12 | 13 | ClassFilter getClassFilter(); 14 | 15 | MethodMatcher getMethodMatcher(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/PointcutAdvisor.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | */ 6 | /** 7 | * ?未知 8 | * @author tengyu 9 | * 10 | */ 11 | public interface PointcutAdvisor extends Advisor{ 12 | 13 | Pointcut getPointcut(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/ProxyFactory.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | */ 6 | //获取代理 7 | public class ProxyFactory extends AdvisedSupport implements AopProxy { 8 | 9 | @Override 10 | public Object getProxy() { 11 | return createAopProxy().getProxy(); 12 | } 13 | 14 | protected final AopProxy createAopProxy() { 15 | return new Cglib2AopProxy(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/ReflectiveMethodInvocation.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.aopalliance.intercept.MethodInvocation; 4 | 5 | import java.lang.reflect.AccessibleObject; 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * @author yihua.huang@dianping.com 10 | */ 11 | /** 12 | * 方法调用 13 | * @author tengyu 14 | * 15 | */ 16 | public class ReflectiveMethodInvocation implements MethodInvocation { 17 | 18 | //目标对象 19 | protected Object target; 20 | 21 | protected Method method; 22 | 23 | protected Object[] arguments; 24 | 25 | public ReflectiveMethodInvocation(Object target, Method method, Object[] arguments) { 26 | this.target = target; 27 | this.method = method; 28 | this.arguments = arguments; 29 | } 30 | 31 | @Override 32 | public Method getMethod() { 33 | return method; 34 | } 35 | 36 | @Override 37 | public Object[] getArguments() { 38 | return arguments; 39 | } 40 | 41 | @Override 42 | public Object proceed() throws Throwable { 43 | //对象的方法调用(参数) 44 | //p.say("hello");和方法调用一样,只是采用反射的方式 45 | return method.invoke(target, arguments); 46 | } 47 | 48 | @Override 49 | public Object getThis() { 50 | return target; 51 | } 52 | 53 | @Override 54 | public AccessibleObject getStaticPart() { 55 | return method; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/aop/TargetSource.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | /** 4 | * 被代理的对象 5 | * @author yihua.huang@dianping.com 6 | */ 7 | //被代理对象的Class档案和接口 8 | public class TargetSource { 9 | 10 | //类的Class对象 11 | private Class targetClass; 12 | 13 | //类的接口 14 | private Class[] interfaces; 15 | 16 | //类的实例 17 | private Object target; 18 | 19 | public TargetSource(Object target, Class targetClass,Class... interfaces) { 20 | this.target = target; 21 | this.targetClass = targetClass; 22 | this.interfaces = interfaces; 23 | } 24 | 25 | public Class getTargetClass() { 26 | return targetClass; 27 | } 28 | 29 | public Object getTarget() { 30 | return target; 31 | } 32 | 33 | public Class[] getInterfaces() { 34 | return interfaces; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/AbstractBeanDefinitionReader.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans; 2 | 3 | import us.codecraft.tinyioc.beans.io.ResourceLoader; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * 从配置中读取BeanDefinition,抽象类 10 | * 11 | * @author yihua.huang@dianping.com 12 | */ 13 | public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader { 14 | 15 | //bean集合 16 | private Map registry; 17 | 18 | //资源加载器 19 | private ResourceLoader resourceLoader; 20 | 21 | protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader) { 22 | this.registry = new HashMap(); 23 | this.resourceLoader = resourceLoader; 24 | } 25 | 26 | public Map getRegistry() { 27 | return registry; 28 | } 29 | 30 | public ResourceLoader getResourceLoader() { 31 | return resourceLoader; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/BeanDefinition.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans; 2 | 3 | /** 4 | * bean的内容及元数据,保存在BeanFactory中,包装bean的实体 5 | * 6 | * @author yihua.huang@dianping.com 7 | */ 8 | public class BeanDefinition { 9 | 10 | private Object bean; 11 | 12 | //类的Class对象 13 | private Class beanClass; 14 | 15 | //类名 16 | private String beanClassName; 17 | 18 | //保存所有的属性,bean的引用也算一种属性吧。 19 | private PropertyValues propertyValues = new PropertyValues(); 20 | 21 | public BeanDefinition() { 22 | } 23 | 24 | public void setBean(Object bean) { 25 | this.bean = bean; 26 | } 27 | 28 | public Class getBeanClass() { 29 | return beanClass; 30 | } 31 | 32 | public void setBeanClass(Class beanClass) { 33 | this.beanClass = beanClass; 34 | } 35 | 36 | public String getBeanClassName() { 37 | return beanClassName; 38 | } 39 | 40 | public void setBeanClassName(String beanClassName) { 41 | this.beanClassName = beanClassName; 42 | try { 43 | //加载类,并返回class对象 44 | //这里已经有了类的实例了,但是没有引用,怎么可以获取这个引用呢? 45 | this.beanClass = Class.forName(beanClassName); 46 | 47 | } catch (ClassNotFoundException e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | 52 | public Object getBean() { 53 | return bean; 54 | } 55 | 56 | public PropertyValues getPropertyValues() { 57 | return propertyValues; 58 | } 59 | 60 | public void setPropertyValues(PropertyValues propertyValues) { 61 | this.propertyValues = propertyValues; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/BeanDefinitionReader.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans; 2 | 3 | /** 4 | * 从配置中读取BeanDefinition,加载所有bean 5 | * @author yihua.huang@dianping.com 6 | */ 7 | public interface BeanDefinitionReader { 8 | 9 | void loadBeanDefinitions(String location) throws Exception; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/BeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans; 2 | 3 | /** 4 | * 初始化前后处理器 5 | * @author tengyu 6 | * 7 | */ 8 | public interface BeanPostProcessor { 9 | 10 | Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception; 11 | 12 | Object postProcessAfterInitialization(Object bean, String beanName) throws Exception; 13 | 14 | } -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/PropertyValue.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans; 2 | 3 | /** 4 | * 用于bean的属性注入,配置属性? 5 | * @author yihua.huang@dianping.com 6 | */ 7 | public class PropertyValue { 8 | 9 | private final String name; 10 | 11 | private final Object value; 12 | 13 | public PropertyValue(String name, Object value) { 14 | this.name = name; 15 | this.value = value; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public Object getValue() { 23 | return value; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/PropertyValues.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 包装一个对象所有的PropertyValue。
8 | * 为什么封装而不是直接用List?因为可以封装一些操作。//所有配置属性 9 | * @author yihua.huang@dianping.com 10 | */ 11 | public class PropertyValues { 12 | 13 | private final List propertyValueList = new ArrayList(); 14 | 15 | public PropertyValues() { 16 | } 17 | 18 | public void addPropertyValue(PropertyValue pv) { 19 | //TODO:这里可以对于重复propertyName进行判断,直接用list没法做到 20 | this.propertyValueList.add(pv); 21 | } 22 | 23 | public List getPropertyValues() { 24 | return this.propertyValueList; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/factory/AbstractBeanFactory.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans.factory; 2 | 3 | import us.codecraft.tinyioc.beans.BeanDefinition; 4 | import us.codecraft.tinyioc.beans.BeanPostProcessor; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | /** 13 | * 抽象bean工厂 14 | * @author yihua.huang@dianping.com 15 | */ 16 | public abstract class AbstractBeanFactory implements BeanFactory { 17 | 18 | //bean工厂里维护类的字典,类名+Class对象 19 | private Map beanDefinitionMap = new ConcurrentHashMap(); 20 | 21 | private final List beanDefinitionNames = new ArrayList(); 22 | 23 | private List beanPostProcessors = new ArrayList(); 24 | 25 | /** 26 | * 获取bean的时候,才创建类的实例对象,原来只是保存类名和类的Class对象,到这一步会根据Class对象创建类的实例 27 | */ 28 | @Override 29 | public Object getBean(String name) throws Exception { 30 | BeanDefinition beanDefinition = beanDefinitionMap.get(name); 31 | if (beanDefinition == null) { 32 | throw new IllegalArgumentException("No bean named " + name + " is defined"); 33 | } 34 | Object bean = beanDefinition.getBean(); 35 | if (bean == null) { 36 | //刚创建对象,其他什么都还没做 37 | bean = doCreateBean(beanDefinition); 38 | //初始化bean对象 39 | bean = initializeBean(bean, name); 40 | 41 | //这的bean是初始化之后的的bean,与刚开始创建的bean不一样的 42 | beanDefinition.setBean(bean); 43 | } 44 | return bean; 45 | } 46 | 47 | //初始化bean,即做一些初始化的工作 48 | protected Object initializeBean(Object bean, String name) throws Exception { 49 | for (BeanPostProcessor beanPostProcessor : beanPostProcessors) { 50 | bean = beanPostProcessor.postProcessBeforeInitialization(bean, name); 51 | } 52 | // TODO:call initialize method 53 | for (BeanPostProcessor beanPostProcessor : beanPostProcessors) { 54 | bean = beanPostProcessor.postProcessAfterInitialization(bean, name); 55 | } 56 | return bean; 57 | } 58 | 59 | //创建bean的实例 60 | protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception { 61 | return beanDefinition.getBeanClass().newInstance(); 62 | } 63 | 64 | //注册bean,即将类名和类的定义保存到内存(map对象)中 65 | public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception { 66 | beanDefinitionMap.put(name, beanDefinition); 67 | //保存一份做备份吧。 68 | beanDefinitionNames.add(name); 69 | } 70 | 71 | //重新验证一下,以免被GC回收了,如果被回收的话就重新创建类的实例 72 | public void preInstantiateSingletons() throws Exception { 73 | for (Iterator it = this.beanDefinitionNames.iterator(); it.hasNext();) { 74 | String beanName = (String) it.next(); 75 | getBean(beanName); 76 | } 77 | } 78 | 79 | protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception { 80 | //这里会创建bean的实例对象 81 | Object bean = createBeanInstance(beanDefinition); 82 | 83 | //将bean的实例对象设置到beandefinition中去 84 | beanDefinition.setBean(bean); 85 | //设置bean的引用的实例对象 86 | applyPropertyValues(bean, beanDefinition); 87 | 88 | return bean; 89 | } 90 | 91 | protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception { 92 | 93 | } 94 | 95 | public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) throws Exception { 96 | this.beanPostProcessors.add(beanPostProcessor); 97 | } 98 | 99 | //根据类型返回beans 100 | public List getBeansForType(Class type) throws Exception { 101 | List beans = new ArrayList(); 102 | for (String beanDefinitionName : beanDefinitionNames) { 103 | if (type.isAssignableFrom(beanDefinitionMap.get(beanDefinitionName).getBeanClass())) { 104 | beans.add(getBean(beanDefinitionName)); 105 | } 106 | } 107 | return beans; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/factory/AutowireCapableBeanFactory.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans.factory; 2 | 3 | import us.codecraft.tinyioc.BeanReference; 4 | import us.codecraft.tinyioc.aop.BeanFactoryAware; 5 | import us.codecraft.tinyioc.beans.BeanDefinition; 6 | import us.codecraft.tinyioc.beans.PropertyValue; 7 | 8 | import java.lang.reflect.Field; 9 | import java.lang.reflect.Method; 10 | 11 | /** 12 | * 可自动装配内容的BeanFactory 13 | * 14 | * @author yihua.huang@dianping.com 15 | */ 16 | /** 17 | * 自定加载引用的类,然后关联起来 18 | * @author tengyu 19 | * 20 | */ 21 | public class AutowireCapableBeanFactory extends AbstractBeanFactory { 22 | 23 | protected void applyPropertyValues(Object bean, BeanDefinition mbd) throws Exception { 24 | if (bean instanceof BeanFactoryAware) { 25 | ((BeanFactoryAware) bean).setBeanFactory(this); 26 | } 27 | for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) { 28 | Object value = propertyValue.getValue(); 29 | //获取bean的引用类名 30 | if (value instanceof BeanReference) { 31 | BeanReference beanReference = (BeanReference) value; 32 | //这里返回的是引用对象的实例,即先初始化第A类,如果A类关联B类,就去找到 33 | value = getBean(beanReference.getName()); 34 | } 35 | 36 | //引用的关联,这里先找有没有setXX(),即设置其他类引用的方法,如果有就调用这个方法, 37 | //如果没有就看有没有其他类引用的字段,如果有就直接引用把设置到对应的字段 38 | try { 39 | Method declaredMethod = bean.getClass().getDeclaredMethod( 40 | "set" + propertyValue.getName().substring(0, 1).toUpperCase() 41 | + propertyValue.getName().substring(1), value.getClass()); 42 | declaredMethod.setAccessible(true); 43 | 44 | //直接调用bean的方法setXXX(类名)方法 45 | declaredMethod.invoke(bean, value); 46 | } catch (NoSuchMethodException e) { 47 | Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName()); 48 | declaredField.setAccessible(true); 49 | declaredField.set(bean, value); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/factory/BeanFactory.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans.factory; 2 | 3 | /** 4 | * bean的容器,工厂 5 | * @author yihua.huang@dianping.com 6 | */ 7 | public interface BeanFactory { 8 | 9 | //获取bean的实例对象 10 | Object getBean(String name) throws Exception; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/io/Resource.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans.io; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | /** 7 | * Resource是spring内部定位资源的接口。接口 8 | * @author yihua.huang@dianping.com 9 | */ 10 | public interface Resource { 11 | 12 | InputStream getInputStream() throws IOException; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/io/ResourceLoader.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans.io; 2 | 3 | import java.net.URL; 4 | 5 | /** 6 | * @author yihua.huang@dianping.com 7 | */ 8 | public class ResourceLoader { 9 | 10 | //获取资源 11 | public Resource getResource(String location){ 12 | URL resource = this.getClass().getClassLoader().getResource(location); 13 | return new UrlResource(resource); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/io/UrlResource.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans.io; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.URL; 6 | import java.net.URLConnection; 7 | 8 | /** 9 | * @author yihua.huang@dianping.com 10 | */ 11 | public class UrlResource implements Resource { 12 | 13 | private final URL url; 14 | 15 | public UrlResource(URL url) { 16 | this.url = url; 17 | } 18 | 19 | @Override 20 | //根据URL载入输入流 21 | public InputStream getInputStream() throws IOException{ 22 | URLConnection urlConnection = url.openConnection(); 23 | urlConnection.connect(); 24 | return urlConnection.getInputStream(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/beans/xml/XmlBeanDefinitionReader.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans.xml; 2 | 3 | import org.w3c.dom.Document; 4 | import org.w3c.dom.Element; 5 | import org.w3c.dom.Node; 6 | import org.w3c.dom.NodeList; 7 | import us.codecraft.tinyioc.BeanReference; 8 | import us.codecraft.tinyioc.beans.AbstractBeanDefinitionReader; 9 | import us.codecraft.tinyioc.beans.BeanDefinition; 10 | import us.codecraft.tinyioc.beans.PropertyValue; 11 | import us.codecraft.tinyioc.beans.io.ResourceLoader; 12 | 13 | import javax.xml.parsers.DocumentBuilder; 14 | import javax.xml.parsers.DocumentBuilderFactory; 15 | import java.io.InputStream; 16 | 17 | /** 18 | * @author yihua.huang@dianping.com 19 | */ 20 | public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { 21 | 22 | public XmlBeanDefinitionReader(ResourceLoader resourceLoader) { 23 | super(resourceLoader); 24 | } 25 | 26 | @Override 27 | public void loadBeanDefinitions(String location) throws Exception { 28 | //加载输入流 29 | InputStream inputStream = getResourceLoader().getResource(location).getInputStream(); 30 | doLoadBeanDefinitions(inputStream); 31 | } 32 | 33 | protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception { 34 | //xml解析 35 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 36 | DocumentBuilder docBuilder = factory.newDocumentBuilder(); 37 | Document doc = docBuilder.parse(inputStream); 38 | // 解析bean 39 | registerBeanDefinitions(doc); 40 | inputStream.close(); 41 | } 42 | 43 | public void registerBeanDefinitions(Document doc) { 44 | Element root = doc.getDocumentElement(); 45 | 46 | parseBeanDefinitions(root); 47 | } 48 | 49 | protected void parseBeanDefinitions(Element root) { 50 | NodeList nl = root.getChildNodes(); 51 | for (int i = 0; i < nl.getLength(); i++) { 52 | Node node = nl.item(i); 53 | if (node instanceof Element) { 54 | Element ele = (Element) node; 55 | processBeanDefinition(ele); 56 | } 57 | } 58 | } 59 | 60 | protected void processBeanDefinition(Element ele) { 61 | //获取id和classname 62 | String name = ele.getAttribute("id"); 63 | String className = ele.getAttribute("class"); 64 | BeanDefinition beanDefinition = new BeanDefinition(); 65 | //处理属性 66 | processProperty(ele, beanDefinition); 67 | //注册Class 68 | beanDefinition.setBeanClassName(className); 69 | getRegistry().put(name, beanDefinition); 70 | } 71 | 72 | //添加bean的属性,和ref引用 73 | private void processProperty(Element ele, BeanDefinition beanDefinition) { 74 | NodeList propertyNode = ele.getElementsByTagName("property"); 75 | for (int i = 0; i < propertyNode.getLength(); i++) { 76 | Node node = propertyNode.item(i); 77 | if (node instanceof Element) { 78 | Element propertyEle = (Element) node; 79 | String name = propertyEle.getAttribute("name"); 80 | String value = propertyEle.getAttribute("value"); 81 | if (value != null && value.length() > 0) { 82 | beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value)); 83 | } else { 84 | String ref = propertyEle.getAttribute("ref"); 85 | if (ref == null || ref.length() == 0) { 86 | throw new IllegalArgumentException("Configuration problem: element for property '" 87 | + name + "' must specify a ref or value"); 88 | } 89 | //bean对其他对象的引用,直接放到自己的属性里面 90 | BeanReference beanReference = new BeanReference(ref); 91 | beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference)); 92 | } 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/context/AbstractApplicationContext.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.context; 2 | 3 | import us.codecraft.tinyioc.beans.BeanPostProcessor; 4 | import us.codecraft.tinyioc.beans.factory.AbstractBeanFactory; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author yihua.huang@dianping.com 10 | */ 11 | public abstract class AbstractApplicationContext implements ApplicationContext { 12 | protected AbstractBeanFactory beanFactory; 13 | 14 | public AbstractApplicationContext(AbstractBeanFactory beanFactory) { 15 | this.beanFactory = beanFactory; 16 | } 17 | 18 | public void refresh() throws Exception { 19 | //加载bean 20 | loadBeanDefinitions(beanFactory); 21 | //注册之前,干点什么事情 22 | registerBeanPostProcessors(beanFactory); 23 | onRefresh(); 24 | } 25 | 26 | protected abstract void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception; 27 | 28 | protected void registerBeanPostProcessors(AbstractBeanFactory beanFactory) throws Exception { 29 | List beanPostProcessors = beanFactory.getBeansForType(BeanPostProcessor.class); 30 | for (Object beanPostProcessor : beanPostProcessors) { 31 | beanFactory.addBeanPostProcessor((BeanPostProcessor) beanPostProcessor); 32 | } 33 | } 34 | 35 | protected void onRefresh() throws Exception{ 36 | beanFactory.preInstantiateSingletons(); 37 | } 38 | 39 | //调用beanfactory工厂获取bean的实例对象 40 | @Override 41 | public Object getBean(String name) throws Exception { 42 | return beanFactory.getBean(name); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/context/ApplicationContext.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.context; 2 | 3 | import us.codecraft.tinyioc.beans.factory.BeanFactory; 4 | 5 | /** 6 | * @author yihua.huang@dianping.com 7 | */ 8 | /** 9 | * 继承beanFactory,继承了factory所有的遗产 10 | * @author tengyu 11 | * 12 | */ 13 | public interface ApplicationContext extends BeanFactory { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/us/codecraft/tinyioc/context/ClassPathXmlApplicationContext.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.context; 2 | 3 | import us.codecraft.tinyioc.beans.BeanDefinition; 4 | import us.codecraft.tinyioc.beans.factory.AbstractBeanFactory; 5 | import us.codecraft.tinyioc.beans.factory.AutowireCapableBeanFactory; 6 | import us.codecraft.tinyioc.beans.io.ResourceLoader; 7 | import us.codecraft.tinyioc.beans.xml.XmlBeanDefinitionReader; 8 | 9 | import java.util.Map; 10 | 11 | /** 12 | * @author yihua.huang@dianping.com 13 | */ 14 | public class ClassPathXmlApplicationContext extends AbstractApplicationContext { 15 | 16 | private String configLocation; 17 | 18 | public ClassPathXmlApplicationContext(String configLocation) throws Exception { 19 | this(configLocation, new AutowireCapableBeanFactory()); 20 | } 21 | 22 | public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception { 23 | super(beanFactory); 24 | this.configLocation = configLocation; 25 | //直接全部初始化 26 | refresh(); 27 | } 28 | 29 | @Override 30 | protected void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception { 31 | //定位bean,然后加载bean 32 | XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); 33 | xmlBeanDefinitionReader.loadBeanDefinitions(configLocation); 34 | //注册bean,这里bean已经加载到虚拟机中,但还没有实例化对象,先不急嘛。 35 | for (Map.Entry beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { 36 | beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/BeanFactoryTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc; 2 | 3 | import java.util.Map; 4 | import org.junit.Test; 5 | import us.codecraft.tinyioc.beans.BeanDefinition; 6 | import us.codecraft.tinyioc.beans.factory.AbstractBeanFactory; 7 | import us.codecraft.tinyioc.beans.factory.AutowireCapableBeanFactory; 8 | import us.codecraft.tinyioc.beans.io.ResourceLoader; 9 | import us.codecraft.tinyioc.beans.xml.XmlBeanDefinitionReader; 10 | 11 | /** 12 | * @author yihua.huang@dianping.com 13 | */ 14 | public class BeanFactoryTest { 15 | 16 | @Test 17 | public void testLazy() throws Exception { 18 | // 1.读取配置 19 | XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); 20 | xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml"); 21 | 22 | // 2.初始化BeanFactory,并注册所有bean相关信息,无论是直接引用还间接引用 23 | //这里有一种办法是扫描所有的类,然后全部注册到工厂中,但效率太低吧。 24 | //另外就是制定注册那些类,方式的话通过三种方式:1.xml配置2.标记注解3.继承继承某个接口 25 | AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory(); 26 | for (Map.Entry beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { 27 | beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); 28 | } 29 | 30 | // 3.获取bean,这里从工厂里找到对应类的相关信息,然后创建类的实例,如果类有其他引用,就实例化其他引用,然后调用相关方法 31 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 32 | helloWorldService.helloWorld(); 33 | } 34 | 35 | @Test 36 | public void testPreInstantiate() throws Exception { 37 | // 1.读取配置 38 | XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); 39 | xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml"); 40 | 41 | // 2.初始化BeanFactory并注册bean 42 | AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory(); 43 | for (Map.Entry beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) { 44 | beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); 45 | } 46 | 47 | // 3.初始化bean,提前预初始化所有注册的类。 48 | beanFactory.preInstantiateSingletons(); 49 | 50 | // 4.获取bean 51 | HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService"); 52 | helloWorldService.helloWorld(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/BeanInitializeLogger.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc; 2 | 3 | import us.codecraft.tinyioc.beans.BeanPostProcessor; 4 | 5 | /** 6 | * @author yihua.huang@dianping.com 7 | */ 8 | public class BeanInitializeLogger implements BeanPostProcessor { 9 | @Override 10 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception { 11 | System.out.println("Initialize bean " + beanName + " start!"); 12 | return bean; 13 | } 14 | 15 | @Override 16 | public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception { 17 | System.out.println("Initialize bean " + beanName + " end!"); 18 | return bean; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/HelloWorldService.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | */ 6 | public interface HelloWorldService { 7 | 8 | void helloWorld(); 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/HelloWorldServiceImpl.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | */ 6 | public class HelloWorldServiceImpl implements HelloWorldService { 7 | 8 | private String text; 9 | 10 | private OutputService outputService; 11 | 12 | @Override 13 | public void helloWorld(){ 14 | outputService.output(text); 15 | } 16 | 17 | public void setText(String text) { 18 | this.text = text; 19 | } 20 | 21 | public void setOutputService(OutputService outputService) { 22 | this.outputService = outputService; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/OutputService.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | */ 6 | public interface OutputService { 7 | void output(String text); 8 | } 9 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/OutputServiceImpl.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | */ 6 | public class OutputServiceImpl implements OutputService { 7 | 8 | @Override 9 | public void output(String text){ 10 | System.out.println(text); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/aop/AspectJExpressionPointcutTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import us.codecraft.tinyioc.HelloWorldService; 6 | import us.codecraft.tinyioc.HelloWorldServiceImpl; 7 | 8 | /** 9 | * @author yihua.huang@dianping.com 10 | */ 11 | public class AspectJExpressionPointcutTest { 12 | 13 | @Test 14 | public void testClassFilter() throws Exception { 15 | String expression = "execution(* us.codecraft.tinyioc.*.*(..))"; 16 | AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut(); 17 | aspectJExpressionPointcut.setExpression(expression); 18 | boolean matches = aspectJExpressionPointcut.getClassFilter().matches(HelloWorldService.class); 19 | Assert.assertTrue(matches); 20 | } 21 | 22 | @Test 23 | public void testMethodInterceptor() throws Exception { 24 | String expression = "execution(* us.codecraft.tinyioc.*.*(..))"; 25 | AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut(); 26 | aspectJExpressionPointcut.setExpression(expression); 27 | boolean matches = aspectJExpressionPointcut.getMethodMatcher().matches(HelloWorldServiceImpl.class.getDeclaredMethod("helloWorld"),HelloWorldServiceImpl.class); 28 | Assert.assertTrue(matches); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/aop/Cglib2AopProxyTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.junit.Test; 4 | import us.codecraft.tinyioc.HelloWorldService; 5 | import us.codecraft.tinyioc.HelloWorldServiceImpl; 6 | import us.codecraft.tinyioc.context.ApplicationContext; 7 | import us.codecraft.tinyioc.context.ClassPathXmlApplicationContext; 8 | 9 | /** 10 | * @author yihua.huang@dianping.com 11 | */ 12 | public class Cglib2AopProxyTest { 13 | 14 | @Test 15 | public void testInterceptor() throws Exception { 16 | // --------- helloWorldService without AOP 17 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml"); 18 | HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService"); 19 | helloWorldService.helloWorld(); 20 | 21 | // --------- helloWorldService with AOP 22 | // 1. 设置被代理对象(Joinpoint) 23 | AdvisedSupport advisedSupport = new AdvisedSupport(); 24 | TargetSource targetSource = new TargetSource(helloWorldService, HelloWorldServiceImpl.class, 25 | HelloWorldService.class); 26 | advisedSupport.setTargetSource(targetSource); 27 | 28 | // 2. 设置拦截器(Advice) 29 | TimerInterceptor timerInterceptor = new TimerInterceptor(); 30 | advisedSupport.setMethodInterceptor(timerInterceptor); 31 | 32 | // 3. 创建代理(Proxy) 33 | Cglib2AopProxy cglib2AopProxy = new Cglib2AopProxy(advisedSupport); 34 | HelloWorldService helloWorldServiceProxy = (HelloWorldService) cglib2AopProxy.getProxy(); 35 | 36 | // 4. 基于AOP的调用 37 | helloWorldServiceProxy.helloWorld(); 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/aop/JdkDynamicAopProxyTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.junit.Test; 4 | import us.codecraft.tinyioc.HelloWorldService; 5 | import us.codecraft.tinyioc.HelloWorldServiceImpl; 6 | import us.codecraft.tinyioc.context.ApplicationContext; 7 | import us.codecraft.tinyioc.context.ClassPathXmlApplicationContext; 8 | 9 | /** 10 | * @author yihua.huang@dianping.com 11 | */ 12 | public class JdkDynamicAopProxyTest { 13 | 14 | @Test 15 | public void testInterceptor() throws Exception { 16 | // --------- helloWorldService without AOP,这里是和IOC结合的吧 17 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml"); 18 | HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService"); 19 | helloWorldService.helloWorld(); 20 | 21 | System.out.println("-----------分割-----------------------------"); 22 | 23 | 24 | 25 | // --------- helloWorldService with AOP,这里是纯粹的代理模式了 26 | // 1. 设置被代理对象(Joinpoint) 27 | AdvisedSupport advisedSupport = new AdvisedSupport(); 28 | TargetSource targetSource = new TargetSource(helloWorldService, HelloWorldServiceImpl.class, 29 | HelloWorldService.class); 30 | //设置原始对象 31 | advisedSupport.setTargetSource(targetSource); 32 | 33 | // 2. 设置拦截器(Advice) 34 | TimerInterceptor timerInterceptor = new TimerInterceptor(); 35 | //设置拦截方法 36 | advisedSupport.setMethodInterceptor(timerInterceptor); 37 | 38 | // 3. 创建代理(Proxy) 39 | JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport); 40 | //获取代理类实例,调用代理类的方法时,应该会自动取调用代理类invoke方法。 41 | HelloWorldService helloWorldServiceProxy = (HelloWorldService) jdkDynamicAopProxy.getProxy(); 42 | 43 | // 4. 基于AOP的调用 44 | helloWorldServiceProxy.helloWorld(); 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/aop/TimerInterceptor.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.aop; 2 | 3 | import org.aopalliance.intercept.MethodInterceptor; 4 | import org.aopalliance.intercept.MethodInvocation; 5 | 6 | /** 7 | * @author yihua.huang@dianping.com 8 | */ 9 | public class TimerInterceptor implements MethodInterceptor { 10 | 11 | @Override 12 | public Object invoke(MethodInvocation invocation) throws Throwable { 13 | long time = System.nanoTime(); 14 | System.out.println("Invocation of Method " + invocation.getMethod().getName() + " start!"); 15 | Object proceed = invocation.proceed(); 16 | System.out.println("Invocation of Method " + invocation.getMethod().getName() + " end! takes " + (System.nanoTime() - time) 17 | + " nanoseconds."); 18 | return proceed; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/beans/io/ResourceLoaderTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans.io; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | /** 10 | * @author yihua.huang@dianping.com 11 | */ 12 | public class ResourceLoaderTest { 13 | 14 | @Test 15 | public void test() throws IOException { 16 | ResourceLoader resourceLoader = new ResourceLoader(); 17 | Resource resource = resourceLoader.getResource("tinyioc.xml"); 18 | InputStream inputStream = resource.getInputStream(); 19 | Assert.assertNotNull(inputStream); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/beans/xml/XmlBeanDefinitionReaderTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.beans.xml; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import us.codecraft.tinyioc.beans.BeanDefinition; 6 | import us.codecraft.tinyioc.beans.io.ResourceLoader; 7 | 8 | import java.util.Map; 9 | 10 | /** 11 | * @author yihua.huang@dianping.com 12 | */ 13 | public class XmlBeanDefinitionReaderTest { 14 | 15 | @Test 16 | public void test() throws Exception { 17 | XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); 18 | xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml"); 19 | Map registry = xmlBeanDefinitionReader.getRegistry(); 20 | Assert.assertTrue(registry.size() > 0); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/us/codecraft/tinyioc/context/ApplicationContextTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.tinyioc.context; 2 | 3 | import org.junit.Test; 4 | import us.codecraft.tinyioc.HelloWorldService; 5 | 6 | /** 7 | * @author yihua.huang@dianping.com 8 | */ 9 | public class ApplicationContextTest { 10 | 11 | @Test 12 | public void test() throws Exception { 13 | //就是把beanfactory封装一下,使调用更加方便。注册,全部初始化。 14 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml"); 15 | HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService"); 16 | helloWorldService.helloWorld(); 17 | } 18 | 19 | @Test 20 | public void testPostBeanProcessor() throws Exception { 21 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc-postbeanprocessor.xml"); 22 | HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService"); 23 | helloWorldService.helloWorld(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/resources/tinyioc-postbeanprocessor.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/test/resources/tinyioc.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | --------------------------------------------------------------------------------