前面實現了java包的掃描,掃描以後天然就到了bean的加載,以及spring mvc的一大特性 IoC依賴注入的實現;java
這裏則將在以前的基礎上,實現bean的加載和依賴注入的實現git
咱們模仿的輪子就是spring mvc,簡化一些複雜的場景,這裏只實現註解的形式github
Service
, Component
, Repository
, Bean
全部類上有上面註解的,都表示須要實例的beanspring
Autowired
表示該屬性用一個bean對象來實例化spring-mvc
經過Class建立Bean對象mvc
掃描每一個Bean的屬性,若包含 @Autowired
註解,則用bean進行賦值ui
最多見的根據beanName,bean類型來獲取Bean.net
好比業務方依賴第三方的jar包中的某個類,想將它也註冊爲一個bean,由於不能修改第三方類,因此能夠用動態註冊的方式來加載bean設計
這個比較簡單,直接貼一下幾個相關的註解code
幾個聲明類爲Bean的註解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Bean { String value() default ""; } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Bean public @interface Component { String value() default ""; } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Bean public @interface Service { String value() default ""; } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Bean public @interface Repository { String value() default ""; }
Autowired.java
, value對應的是業務方定義的beanName
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { String value() default ""; }
咱們定義一個 BeanFactory
對象,來管理class的加載,bean的建立
掃描包基本上就是以前一篇博文的內容,不作多說,直接看Bean的實例化
實現思路比較清晰,大體流程以下;
class
上是否有幾個定義爲Bean的註解爲了不每次使用時都掃描一遍,因此這個掃描的結果會保存下來,放在內存中
/** * 全部自動實例化的bean的映射表, * key爲bean name * - (若是註解中有指定value值,則bean name就是value值;若沒有指定,則是首字母小寫的簡單類名) * - bean name 區分大小寫 * <p> * 爲了不bean name相同的問題,將value也保存爲一個Map映射表 */ private Map<String, Map<Class, Object>> nameBeanMap; /** * class到bean的映射表 */ private Map<Class, Object> clzBeanMap;
實際加載過程以下
/** * 實例化自動加載的bean * * @return */ private Map<String, Map<Class, Object>> instanceBean() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { nameBeanMap = new ConcurrentHashMap<>(); clzBeanMap = new ConcurrentHashMap<>(); Annotation[] typeAnos; String tmpBeanName; Method tmpMethod; Object tmpBean; Map<Class, Object> tmpClzMap; for (Class clz : beanClasses) { if (clz.isInterface()) { continue; } // 獲取類上註解 typeAnos = clz.getAnnotations(); if (typeAnos.length == 0) { continue; } for (Annotation ano : typeAnos) { if (ano instanceof Bean || ano.annotationType().isAnnotationPresent(Bean.class)) { // 須要加載bean tmpMethod = ano.annotationType().getMethod("value", null); if (tmpMethod != null) { tmpBeanName = (String) tmpMethod.invoke(ano, null); } else { tmpBeanName = null; } if (StringUtils.isEmpty(tmpBeanName)) { tmpBeanName = StrUtil.lowerFirstChar(clz.getSimpleName()); } if (nameBeanMap.containsKey(tmpBeanName)) { tmpClzMap = nameBeanMap.get(tmpBeanName); } else { tmpClzMap = new ConcurrentHashMap<>(); } if (tmpClzMap.containsKey(clz)) { throw new BeanAlreadyDefinedException("bean " + tmpBeanName + " class: " + clz.getName() + " has already defined!"); } tmpBean = clz.newInstance(); tmpClzMap.put(clz, tmpBean); clzBeanMap.put(clz, tmpBean); nameBeanMap.put(tmpBeanName, tmpClzMap); break; } } } return nameBeanMap; }
上面的實現比較簡單,惟一須要注意下的是判斷是否包含咱們期待的幾個註解, 不知道是否有更優雅的寫法,下面這種對於自定義一個註解,上面加上 @Service
的狀況時,將不太適用
if(ano instanceof Bean || ano.annotationType().isAnnotationPresent(Bean.class)) { // xxx }
其次就是BeanName
的生成規則
這個也比較簡單,掃描每一個bean的屬性,將擁有 @Autowired
註解的拎出來, 而後查對應的Bean,賦值便可
/** * 依賴注入 */ private void ioc() throws IllegalAccessException { Field[] fields; String beanName; Object bean; for (Object obj : nameBeanMap.values()) { fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { if (!field.isAnnotationPresent(Autowired.class)) { continue; } Autowired autowired = field.getAnnotation(Autowired.class); beanName = StringUtils.isBlank(autowired.value()) ? StrUtil.lowerFirstChar(field.getName()) : autowired.value(); bean = nameBeanMap.get(beanName); if (bean == null) { throw new BeanNotFoundException("bean: " + beanName + " not found! bean class: " + field.getClass().getName()); } field.setAccessible(true); field.set(obj, nameBeanMap.get(beanName)); } } }
屬性賦值,關注下兩行代碼便可
// 強制設置可訪問,這樣私有的變量也能夠修改其內容了 field.setAccessible(true); field.set(obj, nameBeanMap.get(beanName));
查詢的幾個接口就比較簡單了,單純的從Map中獲取對象; 註冊也就是向Map中塞對象
源碼地址: https://github.com/liuyueyi/quick-mvc
相關博文:
我的博客:一灰的我的博客
公衆號獲取更多: