1. 程式人生 > 其它 >Spring原始碼簡易手寫實現(學習過程記錄)(二)

Spring原始碼簡易手寫實現(學習過程記錄)(二)

2.1 建立Scope註解

 context.getBean("userService");

在建立bean物件的時候,會傳入一個beanName,根據這個beanName,首先框架會去判斷這個beanName是單例bean還是原型bean

在實體類中被componen註解的類,我們需要一個Scope註解,來表示這個類是單例還是原型,如果沒有Scope註解預設是單例

@Component("userService")
@Scope("prototype")
public class UserService {
}

首先我們來建立一個Scope註解

package com.rainwood.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
    String value();
}

value並不是預設的

那麼如何通過簡單得context.getBean("userService")

就可以知道這個beanName是單例還是原型呢

單例代表著,每次getBean取出來的是同一個bean物件,而原型每次取出來的都是新建立的bean物件

我們可以通過map (key value)存取的方式來存bean物件,這樣getBean的時候,傳進一個beanName,就可以通過map取出這個bean物件

//定義一個map,來存放單例的bean物件
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//單例池

可以建立一個單例池來存放單例bean物件

那麼如何根據beanName來判斷是單例還是原型

2.2 beanDefination物件

我們需要建立一個beanDefination物件來實現

package com.rainwood.spring;

public class BeanDefination {

    private Class clazz;
    private String scope;


    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }
}

這個物件儲存了bean所對應的class類還有它的scope

在掃描包的時候,建立該beanDefination物件,然後將beanDefination物件存在map裡

   private ConcurrentHashMap<String, BeanDefination> beanDefinationMap = new ConcurrentHashMap<>();//儲存所有掃描到的bean的定義

呼叫getBean時候:

 public Object getBean(String beanName) {

        if(beanDefinationMap.containsKey(beanName)) {
            BeanDefination beanDefination = beanDefinationMap.get(beanName);
            String scope = beanDefination.getScope();
            Class clazz = beanDefination.getClazz();
            System.out.println(clazz);
            if(scope == "singleton") {
                Object o = singletonObjects.get(beanName);
                return o;
            } else {
                Object bean = createBean(beanDefination);
                return bean;
            }
        } else {
            //不存在對應的Bean
            throw new NullPointerException();
        }
    }

先是根據beanName取到對應的class和scope,判斷scope是單例還是原型

如果是單例,從單例池裡拿到對應的bean物件,singletonObjects.get(beanName);

如果是原型,我們需要建立一個createBean方法,將beanDefination傳進去進一步處理(下一節再學)

 public  Object createBean(BeanDefination beanDefination) {
        Class clazz = beanDefination.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

2.3 掃描並且建立單例池和beanDefinationMap

上一節中,我們已經通過掃描獲取了Component註解的類

掃描:

 try {
                        //這個地方不是放的class檔案的url,而是這個類的全限定名
                        Class<?> aClass = classLoader.loadClass(className);
                        //判斷這個類是不是有Component註解
                        if(aClass.isAnnotationPresent(Component.class)) {
                            //表示當前這個類是一個Bean
                            //解析類,判斷當前bean是單例bean還是prototype的bean 解析之後會生成一個BeanDefination物件
                            //BeanDefination

                            //拿到Component 對應的beanName
                            Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();

                            BeanDefination beanDefination = new BeanDefination();
                            beanDefination.setClazz(aClass);
                            //如果可以拿到Scope註解
                            //如果拿不到表示這個類預設是一個單例
                            if(aClass.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);
                                String scope = scopeAnnotation.value();
                                beanDefination.setScope(scope);

                            } else {
                                beanDefination.setScope("singleton");
                            }
                            beanDefinationMap.put(beanName, beanDefination);


                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

呼叫scan方法掃描後,已經將beanDefinationMap中放入了所有的beanDefination物件和beanName

還需要將bean單例依次放入單例池中:

for (Map.Entry<String, BeanDefination> entry : beanDefinationMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefination beanDefination = entry.getValue();

            if(beanDefination.getScope().equals("singleton")) {
                Object bean = createBean(beanDefination); // 建立單例bean物件
                singletonObjects.put(beanName, bean);
            }
        }

下面是limingApplicationContext.java:

package com.rainwood.spring;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class limingApplicationContext {
    private  Class configClass;

    //定義一個map,來存放單例的bean物件
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//單例池

    private ConcurrentHashMap<String, BeanDefination> beanDefinationMap = new ConcurrentHashMap<>();//儲存所有掃描到的bean的定義


    public limingApplicationContext(Class configClass) throws ClassNotFoundException {
        this.configClass = configClass;
        //解析配置類
        //ComponentScan註解--》掃描路徑 -->掃描-->Beandefination--->BeanDefinationMap
        scan(configClass);

        for (Map.Entry<String, BeanDefination> entry : beanDefinationMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefination beanDefination = entry.getValue();

            if(beanDefination.getScope().equals("singleton")) {
                Object bean = createBean(beanDefination); // 建立單例bean物件
                singletonObjects.put(beanName, bean);
            }
        }
    }

    public  Object createBean(BeanDefination beanDefination) {
        Class clazz = beanDefination.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void scan(Class configClass) {
        //解析配置類
        //ComponentScan註解--》掃描路徑
        ComponentScan componentScanAnnotation = (ComponentScan) 		           configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScanAnnotation.value(); //掃描路徑
        path = path.replace(".","/");

        System.out.println(path);
        //掃描
        //類載入器:
        //Bootstrap -->jre/lib
        //Ext----->jre/ext/lib
        //App---->classpath
        ClassLoader classLoader = limingApplicationContext.class.getClassLoader(); //app 取得的是classpath對應的類載入器
        //resource對應的是一個目錄 file:/D:/spring%e6%ba%90%e7%a0%81%e5%ad%a6%e4%b9%a0/target/classes/com/rainwood/liming/service
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());

        //判斷是不是一個目錄
        if(file.isDirectory()) {
            //可以獲取到這個目錄下所有的編譯好的class檔案路徑
            File[] files = file.listFiles();
            for (File file1 : files) {
                System.out.println(file1);

                //D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class 轉化成com.rainwood.liming.service.UserService
                String fileName = file1.getAbsolutePath();

                //判斷是不是class檔案再進一步處理
                if(fileName.endsWith(".class")) {
                    String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
                    className = className.replace("\\",".");

                    try {
                        //這個地方不是放的class檔案的url,而是這個類的全限定名
                        Class<?> aClass = classLoader.loadClass(className);
                        //判斷這個類是不是有Component註解
                        if(aClass.isAnnotationPresent(Component.class)) {
                            //表示當前這個類是一個Bean
                            //解析類,判斷當前bean是單例bean還是prototype的bean 解析之後會生成一個BeanDefination物件
                            //BeanDefination

                            //拿到Component 對應的beanName
                            Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();

                            BeanDefination beanDefination = new BeanDefination();
                            beanDefination.setClazz(aClass);
                            //如果可以拿到Scope註解
                            //如果拿不到表示這個類預設是一個單例
                            if(aClass.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);
                                String scope = scopeAnnotation.value();
                                beanDefination.setScope(scope);

                            } else {
                                beanDefination.setScope("singleton");
                            }
                            beanDefinationMap.put(beanName, beanDefination);


                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

            }

        }
    }

    public Object getBean(String beanName) {

        if(beanDefinationMap.containsKey(beanName)) {
            BeanDefination beanDefination = beanDefinationMap.get(beanName);
            String scope = beanDefination.getScope();
            Class clazz = beanDefination.getClazz();
            System.out.println(clazz);
            if(scope == "singleton") {
                Object o = singletonObjects.get(beanName);
                return o;
            } else {
                Object bean = createBean(beanDefination);
                return bean;
            }
        } else {
            //不存在對應的Bean
            throw new NullPointerException();
        }
    }
}

test

package com.rainwood.liming;
import com.rainwood.spring.limingApplicationContext;
public class test {
    public static void main(String[] args) throws ClassNotFoundException {
        limingApplicationContext context = new limingApplicationContext(AppConfig.class);

        //如果是單例bean getBean呼叫多次獲得的都是同一個bean物件 (可以通過map來實現, key就是beanName, 對應的value就是bean物件) 單例池
        //如果是原型bean 會返回不同的bean物件
//        Object bean = context.getBean("userService");
        System.out.println(context.getBean("userService"));
        System.out.println(context.getBean("userService"));
        System.out.println(context.getBean("userService"));
//        System.out.println(bean);
    }
}

打印出getBean,如果是原型的話執行結果:

可見雜湊值不同

com/rainwood/liming/service
D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class
D:\springResourceLearn\target\classes\com\rainwood\liming\service\XxUtil.class
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@5e2de80c
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@1d44bcfa
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@266474c2

如果是單例則:

com/rainwood/liming/service
D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class
D:\springResourceLearn\target\classes\com\rainwood\liming\service\XxUtil.class
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@60e53b93
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@60e53b93
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@60e53b93

雜湊值相同