├── .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