MVCを実現する:2.bean負荷,IoC依存注入

6175 ワード

背景
前にjavaパケットのスキャンを実現し、スキャン後に自然にbeanのロードに達し、spring mvcの大きな特性IoC依存注入の実現を実現した.
ここでは,beanの負荷と依存注入の実現を以前に基づいて実現する.
デザイン
私たちが真似した車輪はspring mvcで、いくつかの複雑なシーンを簡略化して、ここでは注釈の形式だけを実現します
1.spring-mvcの使用姿勢に依存し、いくつかの注釈を定義する必要があります.
  • クラス上注記Service,Component,Repository,Bean,Autowired
  • すべてのクラスに上記の注記があり、インスタンスが必要なbeanを表します.
  • 属性注記@Autowired
  • このプロパティをbeanオブジェクトでインスタンス化することを示します.
    2.インスタンス化bean
    ClassによるBeanオブジェクトの作成
    3.ロード依存
    各Beanのプロパティをスキャンし、Autowired.java注記が含まれている場合はbeanで割り当てます.
    4.beanを取得するための様々な方法を提供する
    最も一般的なのはbeanName、beanタイプによってBeanを取得します
    5.動的登録beanの提供
    たとえば、サードパーティのjarパッケージのクラスに依存して、サードパーティのクラスを変更できないため、動的登録でbeanをロードすることもできます.
    インプリメンテーション
    1.注記定義
    これは比較的に簡単で、直接いくつかの関連する注釈を貼ります
    いくつかの宣言クラスが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 "";
    }
    
    BeanFactory,valueは事業者定義のbeanNameに対応
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Autowired {
        String value() default "";
    }
    

    2.スキャンパッケージ、クラスロード、Bean作成
    classのロード、beanの作成を管理するclassオブジェクトを定義します.
    スキャンパッケージは基本的に前のブログの内容で、あまり言わないで、Beanのインスタンス化を直接見ます.
    実現構想は比較的明確で、大まかな流れは以下の通りである.
  • Classコレクションを巡る
  • 判断@ServiceにBeanと定義された注釈がいくつかあるかどうか
  • beanと決定されると、インスタンス化
  • 使用するたびにスキャンしないようにするため、このスキャンの結果は保存され、メモリに保存されます.
    /**
     *         bean    ,
     * key bean name
     * - (        value , bean name  value ;     ,            )
     * - bean name      
     * 

    * bean name , value Map */ private Map> nameBeanMap; /** * class bean */ private Map clzBeanMap;


    実際のロード手順は次のとおりです.
    /**
     *         bean
     *
     * @return
     */
    private Map> instanceBean() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        nameBeanMap = new ConcurrentHashMap<>();
        clzBeanMap = new ConcurrentHashMap<>();
    
    
        Annotation[] typeAnos;
        String tmpBeanName;
        Method tmpMethod;
        Object tmpBean;
        Map 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;
    }
    

    上記の実現は比較的簡単で、唯一注意しなければならないのは私たちが期待しているいくつかの注釈が含まれているかどうかを判断することであり、より優雅な書き方があるかどうか分からない.
    if(ano instanceof Bean || ano.annotationType().isAnnotationPresent(Bean.class)) {
      // xxx
    }
    

    次にBeanNameの生成ルール
  • 注記のvalue属性が指定されている場合、beanNameは指定された値です.
  • そうでなければ、class名によって、頭文字を小文字にすればいい
  • 3.IoC依存注入
    これも比較的簡単で、各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));
            }
        }
    }
    

    属性は値を割り当てて、下の2行のコードに注目すればいいです
    //        ,                
    field.setAccessible(true);
    field.set(obj, nameBeanMap.get(beanName));
    

    4.クエリーbean&動的登録
    クエリーのいくつかのインタフェースは比較的に簡単で、単純にMapからオブジェクトを取得します;登録とは、Mapにオブジェクトを挿入することです.
    その他
    ソースアドレス:https://github.com/liuyueyi/quick-mvc
    関連ブログ:
  • Javaパッケージパスの下classファイル
  • をスキャンしてロード
    個人ブログ:一灰の個人ブログ
    公衆番号の詳細:
    個人情報