javascript
Spring——原理解析-利用反射和注解模拟IoC的自动装配
解析Spring的IoC容器基于注解實(shí)現(xiàn)的自動(dòng)裝配(自動(dòng)注入依賴)的原理
1.本文案例
使用注解和反射機(jī)制來模擬Spring中IoC的自動(dòng)裝配功能
定義兩個(gè)注解:@Component,用來標(biāo)注組件;@Autowired,用來標(biāo)記需要被織入的屬性。
定義一個(gè)@Component注解處理器,用來掃描所有組件。
定義一個(gè)bean工廠,用來實(shí)例化組件。
測(cè)試:有兩個(gè)組件,一個(gè)組件被設(shè)置到另一個(gè)組件的屬性中。
2.定義注解
2.1.定義@Component注解
這個(gè)注解表示被標(biāo)注的就是一個(gè)組件,將會(huì)被容器自動(dòng)掃描并創(chuàng)建實(shí)例
注解的定義有點(diǎn)類似于接口的定義
注解定義
接口定義
public interface Component { } 區(qū)別只是在于interface這個(gè)標(biāo)識(shí)符前面有沒有@符號(hào)。
并且注解的定義,還需要使用到幾個(gè)原生注解:
@Target(ElementType.TYPE)這個(gè)注解表明自定義的注解Component是用來標(biāo)記誰的,其中ElementType.TYPE表示這個(gè)注解使用來標(biāo)記類型的,也就是可以標(biāo)記類、接口等。此外還有FIELD、METHOD等,分別表示用來標(biāo)記字段、方法等。
@Retention(RetentionPolicy.RUNTIME) 表示這個(gè)自定義的注解需要保留到什么時(shí)候,如只保留到源碼中,編譯之后就沒有了;或者保留到運(yùn)行時(shí),就是在運(yùn)行的時(shí)候也一直有。這里設(shè)置為運(yùn)行時(shí)。
然后這個(gè)注解中有這樣一行:
有點(diǎn)類似于接口中方法的聲明,不過在注解中,這個(gè)表示注解的一個(gè)屬性,后面用到的時(shí)候可以看看是怎么使用的,就明白了。
2.2.定義 @Autowired注解
這個(gè)注解是一個(gè)針對(duì)成員變量的注解,使用這個(gè)注解則表示,這個(gè)字段需要由程序來為其賦值的。
?
3.定義 Beanfactory(也就是注解處理器)
自定義注解完之后,實(shí)際上并沒有什么用處。要想讓注解發(fā)揮用處,重點(diǎn)在于注解處理器。
首先來明確下這個(gè)處理器干了那些事情,首先根據(jù)給定的組件的包名,掃描這個(gè)包,找出其中所有的被@Component注解標(biāo)注的類,將類型的信息保存下來。
然后提供一個(gè)getBean()方法,允許根據(jù)bean的id來獲取bean。
接下來看看這個(gè)BeanFactory是如何編寫的。
3.1.BeanFactory.java
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap;public class BeanFactory {private HashMap<String, Object> beanPool;private HashMap<String, String> components;public BeanFactory(String packageName) {beanPool = new HashMap<>();scanComponents(packageName);}private void scanComponents(String packageName) {components = ComponentScanner.getComponentClassName(packageName);}public Object getBean(String id) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {if (beanPool.containsKey(id)) {return beanPool.get(id);}if (components.containsKey(id)) {Object bean = Class.forName(components.get(id)).newInstance();bean = assemblyMember(bean);beanPool.put(id, bean);return getBean(id);}throw new ClassNotFoundException();}private Object assemblyMember(Object obj) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {Class cl = obj.getClass();for (Field f : cl.getDeclaredFields()) {Autowire at = f.getAnnotation(Autowire.class);if (at != null) {Method setMethod = cl.getMethod("set" + captureName(f.getName()), f.getType());setMethod.invoke(obj, getBean(at.id()));}}return obj;}public static String captureName(String name) {char[] cs=name.toCharArray();cs[0]-=32;return String.valueOf(cs);}}?
?
3.2.ComponentScann.java
這個(gè)BeanFactory在構(gòu)造函數(shù)中使用到了一個(gè)類,用來掃描出一個(gè)包中所有的類的信息。
?
4.測(cè)試
定義一個(gè)模擬的數(shù)據(jù)庫(kù)訪問接口
@Component(id = "dataAccessInterface") public class DataAccessInterface {public String queryFromTableA() {return "query result";} } 這個(gè)類使用了Component這個(gè)注解,并且注意,這里使用了這個(gè)注解的id屬性。
定義一個(gè)模擬的業(yè)務(wù)接口
這個(gè)接口除了使用@Component這個(gè)注解標(biāo)注之外,還有個(gè)成員變量,使用了Autowire這個(gè)注解標(biāo)注。使用這個(gè)注解標(biāo)注,表示這個(gè)成員變量的初始化將會(huì)交給BeanFactory來進(jìn)行。
測(cè)試
public class BeanFactoryTester {public static void main(String[] args) {BeanFactory beanFactory = new BeanFactory("com.oolong.javase.annotation");BusinessObject obj = (BusinessObject) beanFactory.getBean("businessObject");obj.print();}} 這里使用BeanFactory創(chuàng)建了一個(gè)BusinessObject的對(duì)象之后,調(diào)用這個(gè)對(duì)象的print方法,最終打印出來一個(gè)結(jié)果。
而回到這個(gè)類的定義中,可以看到:
這個(gè)方法調(diào)用的是成員變量dai的queryFromTableA方法。而在這個(gè)類中,只有這個(gè)成員變量的聲明,而沒有賦值。
這個(gè)賦值又是在哪里進(jìn)行的呢?
這個(gè)就是有我們編寫的這個(gè)BeanFactory執(zhí)行的。通過注解和反射機(jī)制,自動(dòng)為類注入依賴。
?
轉(zhuǎn)載于:https://www.cnblogs.com/weilu2/p/spring_ioc_analysis_principle_bsici_on_reflection_annotation.html
總結(jié)
以上是生活随笔為你收集整理的Spring——原理解析-利用反射和注解模拟IoC的自动装配的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新密码
- 下一篇: [转]JavaScript构造函数及原型