【Spring系列】- 手寫模擬Spring框架

簡單模擬Spring

😄生命不息,寫作不止
🔥 繼續踏上學習之路,學之分享筆記
👊 總有一天我也能像各位大佬一樣
🏆 一個有夢有戲的人 @怒放吧德德
🌝分享學習心得,歡迎指正,大家一起學習成長!

spring.jpg

前言

上次已經學習了Java的設計模式,接下來就先來學習一下如何手寫模擬簡易的Spring,通過動手實踐,才會更好的了解spring底層原理,今天就簡單的模擬Spring容器是如何創建,bean又是如何注入的。
來看一下本次案例的spring類圖
image.png

Spring容器

模擬spring,首先就是需要一個容器,是Spring的核心,一切Spring bean都存儲在Spring容器內,並由其通過IoC技術管理。Spring容器也就是一個bean工廠(BeanFactory)。應用中bean的實例化,獲取,銷毀等都是由這個bean工廠管理的。就像我們剛開始學習的時候接觸的ApplicationContext,就是spring的容器,他就是為了完成容器的配置,初始化,管理bean的。因此筆者自己創建了一個LydApplicationContext來模擬簡單的spring容器。

開始使用

首先通過new LydApplicationContext(AppConfig.class)實例化對象,在通過applicationContext.getBean("userService")去獲得bean對象。然而在容器的初始化可是做了許多的事情,包括掃描、實例化bean等等操作。
初始容器創建:

public class LydApplicationContext {
    private Class configClass; 
    public LydApplicationContext(Class configClass) { // 構造方法
        this.configClass = configClass;
    }
}

Spring掃描底層實現

Spring容器建好之後我們就需要通過配置文件的註解獲取掃描路徑,我們需要獲取所有的bean,並且需要實例對象。在此我們需要一個配置文件,就是使用new LydApplicationContext(AppConfig.class) 實例攜帶的配置類,當然這裡有好多的形式,也可以是通過xml文件來處理。

配置文件AppConfig.java

這個就是為了提供掃描的包路徑的,不做任何操作,所以不需要其他程式碼。

@ComponentScan("com.lyd.service") // 掃描路徑,掃描這個包下的
public class AppConfig {
}

通過註解存放這個包路徑,在後面可以通過這個註解來獲取包路徑,所以就需要我們創建一個ComponentScan註解。

編寫ComponentScan註解

這個註解是用來spring容器掃描包為之提供包路徑。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    // 指定掃描路徑
    String value() default "";
}

編寫Component註解

在Spring中,通過Component註解將bean注入Spring容器中,這裡我們也採用高這個註解。

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

獲取包路徑

既然已經通過註解將包路徑存在配置類中,接下來就可以通過這個註解來得到。但是,在這裡需要注意的是,我們掃描的並非是java源文件,而是編譯後的class文件。我們需要在LydApplicationContext的構造方法中去實現。
首先,我們需要通過isAnnotationPresent方法先判斷是否存在ComponentScan註解,在通過類的getAnnotation方法來得到註解。這樣就可以直接得到註解上的值。這個值就是我們寫入的包路徑,注意,這裡的路徑是com.lyd.service,而我們需要用替換方法將’.’替換成’/’,因為在後面獲取資源路徑的時候,用的是com/lyd/service這種形式,也就是相對路徑。
接下來需要獲取資源路徑,這個時候就需要用到類載入器LydApplicationContext.class.getClassLoader(),類載入器中有一個getResource(path)方法,這個可以根據傳入的路徑獲取相應的資源,最後是能夠拼出我們需要的絕對路徑。

if (configClass.isAnnotationPresent(ComponentScan.class)) {
    ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
    // 1.1 掃描路徑:只是個包名,掃描的是java的class文件,而並非源文件,com.lyd.service
    String path = componentScanAnnotation.value();
    // 1.2 將路徑文件替換成/的形式
    path = path.replace(".","/");
    // 1.3 通過類載入器獲取資源路徑
    ClassLoader classLoader = LydApplicationContext.class.getClassLoader();
    URL resource = classLoader.getResource(path);
    // 1.4 轉成文件形式,主要是為了獲取他的絕對地址
    File file = new File(resource.getFile());
    System.out.println(file);
}

通過類載入器得到的資源有個獲取File的方法,然後我們通過File file = new File(resource.getFile());將資源轉成File類型,因為他可以表示一個地址或者是具體文件。掃描就是掃描文件路徑,也就是文件夾。我們可以列印看一下這個地址:
image.png

判斷是否為文件夾,如果是,那就獲取裡面的所有文件,在通過遍歷這些文件,獲取絕對路徑。

if (file.isDirectory()) { // 如果是文件夾
    File[] files = file.listFiles();
    for (File f : files) {
        String absolutePath = f.getAbsolutePath(); // 獲取絕對路徑
        System.out.println("絕對路徑:" + absolutePath);
    }
}

我們可以列印出來看一下:
image.png

因為我們要的是編譯的.class文件,因此需要在遍歷文件的時候進行判斷文件是否為.class文件。為了拿到這個類,需要通過全限定名使用類載入器獲取類 ,也就是利用反射機制。我們都知道,spring是通過Component註解來將bean注入spring的,因此最後就是通過判斷是否有這個註解來得到一個bean。

for (File f : files) {
	String absolutePath = f.getAbsolutePath(); // 獲取絕對路徑
	System.out.println("絕對路徑:" + absolutePath);
	// 1.5 對編譯文件進行處理
	if (absolutePath.endsWith(".class")) { // 判斷是否為編譯文件
		/**
		 * 需要拿到的是編譯文件,通過類載入器去獲取
		 * 需要將com\lyd\service\UserService轉成com.lyd.service.UserService
		 */
		String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
		className = className.replace("\\", ".");
		System.out.println("類名:" + className);
		try {
			// 1.6 通過全限定名使用類載入器獲取類 (利用反射機制)
			Class<?> clazz = classLoader.loadClass(className);
			// 1.7 在通過這個clazz(類)來判斷是否有component註解,有則是bean
			if (clazz.isAnnotationPresent(Component.class)) {
				// 到這裡就是一個bean
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

最後我們需要的地址就是如下:
image.png

Spring生成BeanDefinition

在我們Spring容器啟動或者是掃描的時候,並不建議直接實例化bean對象,因為bean是區分單例和多例的,多例bean我們是需要用到的時候再去創建。這個時候就需要生成BeanDefinition,即bean的定義,這個類存儲了類和作用域(單例還是多例)。

定義Scope註解

通過Scope這個註解來標明是單例bean還是多例bean。

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

定義BeanDefinition

在spring中,不會在獲取bean的時候再去解析是否為單例,而是通過BeanDefinition類來操作。對bean的定義,記錄了bean類和作用域。

public class BeanDefinition {
    private Class type;
    private String scope; // 單例多例
    public Class getType() {
        return type;
    }
    public void setType(Class type) {
        this.type = type;
    }
    public String getScope() {
        return scope;
    }
    public void setScope(String scope) {
        this.scope = scope;
    }
}

因為我們注入springbean對象是有Component註解,因此在掃描的時候,我們會通過這個獲得到bean,在這時候去創建BeanDefinition對象。通過判斷註解上的值來賦值其作用域,如果沒有設置,就默認是單例模式。
創建好的bean對象,我們還需要將他進行保存起來,這個就需要定義ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();存儲beanDefinition。key是component的值,如果component沒有傳入beanName的值,那就使用spring的命名規則,採用類名首字母小寫。

// 1.7 在通過這個clazz(類)來判斷是否有component註解,有則是bean
if (clazz.isAnnotationPresent(Component.class)) {
	Component annotation = clazz.getAnnotation(Component.class);
	String beanName = annotation.value();
    // * 默認生成bean,如果只使用Component註解,沒有寫上beanName的值,那麼就需要自動生成
    if (beanName.equals("")) {
        // 默認開頭小字母
        beanName = Introspector.decapitalize(clazz.getSimpleName());
    }
	/**
	 * 這就是一個bean了
	 * 然而在這裡並不是直接就創建bean了,bean分為了單例bean和多例bean
	 */
	BeanDefinition beanDefinition = new BeanDefinition();
	beanDefinition.setType(clazz);
	if (clazz.isAnnotationPresent(Scope.class)) { // 判斷是單例還是多例
		Scope scope = clazz.getAnnotation(Scope.class);
		beanDefinition.setScope(scope.value());
	} else {
		beanDefinition.setScope("singleton");
	}
    beanDefinitionMap.put(beanName, beanDefinition);
}

getBean底層

通過傳來一個beanName,通過beanDefinitionMap中獲取key為beanName的BeanDefinition對象,進行判空處理。這樣我們可以通過這個BeanDefinition對象去獲取作用域,判斷是否為單例。

獲取bean

在此,我們創建單例的時候,是需要將單例保存起來的,需要定義ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();單例池來保存單例bean。然後在getBean的時候,如果是單例bean,就可以先去單例池中尋找,如果沒找到,再去創建對象。而多例模式就需要每次都去創建。

// 獲取bean對象
public Object getBean(String beanName) {
    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
    if (beanDefinition == null) {
        throw new NullPointerException("找不到bean名字為[" + beanName + "]的bean對象");
    } else { // 找到就做相應的操作
        String scope = beanDefinition.getScope();
        if (scope.equals("singleton")) {
            // 通過單例池獲取
            Object bean = singletonObjects.get(beanName);
            if (bean == null) {
                // 單例池中如果沒有bean,就需要去創建
                bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
            return bean;
        } else {
            // 多例的就不需要記錄,每次都是通過創建
            return createBean(beanName, beanDefinition);
        }
    }
}

創建bean

利用反射機制,通過無參構造方法去獲取實例,這裡就是bean對象實例了。就可以直接返回。

// 創建bean
private Object createBean(String beanName, BeanDefinition definition) {
	// 利用反射獲取實例,採用無參構造方法
	Class clazz = definition.getType();
	// 通過無參構造方法獲取實例
	try {
		Object instance = clazz.getConstructor().newInstance(); // 到這裡直接返回,bean的對象也就創建完成

		return instance;

	} catch (InstantiationException e) {
		e.printStackTrace();
	}
	return null;
}

在構造方法掃描後要根據生成的beanDefinitionMap去創建單例bean對象。

// 2 創建單例bean對象
for (String beanName : beanDefinitionMap.keySet()) {
    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
    if (beanDefinition.getScope().equals("singleton")) { // 那麼,如何保證單例呢?就需要有個單例池,singletonObjects
        Object bean = createBean(beanName, beanDefinition);
        singletonObjects.put(beanName, bean); // 將單例bean存到單例池中
    }
}

運行

public class Test {
    public static void main(String[] args) {
        LydApplicationContext applicationContext = new LydApplicationContext(AppConfig.class);
        System.out.println(applicationContext.getBean("userService"));
        System.out.println(applicationContext.getBean("userService"));
        System.out.println(applicationContext.getBean("userService"));
        System.out.println(applicationContext.getBean("userService"));
        System.out.println(applicationContext.getBean("userService"));
    }
}

我們可以先使用單例模式進行測試,我們不加scope註解,並且獲取多個bean,可以看到得到的bean對象是同一個。
image.png
多例的時候在scope標上值,就可以看到每次獲取的bean對象都是不一樣的。
image.png

Autowired自動依賴注入

當我們在UserService中使用RoleService,我們就需要通過Autowired註解進行依賴注入。就是需要在createBean方法中,在創建實例之後,獲取類中的欄位,進行依賴注入,要通過判斷Autowried註解。通過欄位的set方法,將bean對象注入,而這個對象如何獲得呢?那就是用過getBean()方法。

// 3 依賴注入
for (Field field : clazz.getDeclaredFields()) {
    // 判斷欄位上是否存在Autowried註解
    if (field.isAnnotationPresent(Autowired.class)) {
        /**
         * 值為 true 則指示反射的對象在使用時應該取消 Java 語言訪問檢查。
         * 值為 false 則指示反射的對象應該實施 Java 語言訪問檢查;
         * 實際上setAccessible是啟用和禁用訪問安全檢查的開關,並不是為true就能訪問為false就不能訪問 ;
         */
        field.setAccessible(true); // 反射需要設置這個,不然無法賦值
        // 用其屬性名,這就意味著private RoleService roleService;roleService不能亂取
        field.set(instance, getBean(field.getName()));
    }
}

Aware回調機制與初始化

Aware回調

那如果我們需要獲取當前bean的名字呢?那就得通過Aware回調機制。我們需要創建一個BeanNameAware介面,裡面提供一個setBeanName方法。

public interface BeanNameAware {
    public void setBeanName(String beanName);
}

createBean方法中去編寫回調機制,通過判斷這個實例是否有BeanNameAware這個類,通過setName方法間接傳遞了beanName。

// 4 Aware回調機制
if (instance instanceof BeanNameAware) {
    ((BeanNameAware)instance).setBeanName(beanName);
}

UserService方法中去實現這個BeanNameAware方法,這就能夠在UserService里的beanName欄位中得到這個bean對象的真實的beanName了。

@Component("userService") // 注入spring
@Scope("property")
public class UserService implements BeanNameAware, InitializingBean {
    @Autowired
    private RoleService roleService; // 依賴注入,加上@Autowired註解

    private String beanName;

    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    public void test() {
        System.out.println("RoleService: " + roleService);
    }

    @Override
    public void afterPropertiesSet() {
        // ......
        System.out.println("初始化");
    }
}

初始化

在spring容器中,不只是完成bean對象的創建,還需要能夠對bean進行初始化。需要創建InitializingBean介面類。

public interface InitializingBean {
    public void afterPropertiesSet();
}

在需要進行初始化的bean對象去實現這個介面的方法,這裡main可以進行一些操作。在createBean中,會根據判斷是否有InitializingBean類,會在實例化之後調用這個方法進行初始化。

// 5 初始化
if (instance instanceof InitializingBean) {
    ((InitializingBean)instance).afterPropertiesSet();
}

結果
image.png

BeanPostProcessor

BeanPostProcessor可以對spring中bean的創建去做一些操作。

BeanPostProcessor介面

在spring中定義一個BeanPostProcessor介面,裡面會有初始化前後操作的方法,並且將beanNamebean對象帶入進行自定義的操作。

public interface BeanPostProcessor {
    public Object postBeforeProcessor(String beanName, Object bean); // 初始化前
    public Object postAfterProcessor(String beanName, Object bean); // 初始化後
}

自定義BeanPostProcessor

這裡可以定義自己的類去實現spring中的BeanPostProcessor,對初始化進行一些相應操作。並且能夠根據某個bean對象來做不同的操作。原理就是將MyBeanPostProcessor注入到容器中,在掃描的時候將這個對象保存起來,在創建bean的時候去遍歷BeanPostProcessor集合,在去調用這個實例的方法。

@Component // 需要注入spring
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postBeforeProcessor(String beanName, Object bean) {
        System.out.println("初始化前的bean:" + beanName + " -- " + bean);
        return bean;
    }

    @Override
    public Object postAfterProcessor(String beanName, Object bean) {
        System.out.println("初始化後的bean:" + beanName + " -- " + bean);
        return bean;
    }
}

然而,在spring掃描的時候會進行操作,因為自己實現的BeanPostProcessor是通過Component註解注入spring容器的。因此可以通過判斷有Component註解時候,進行判斷是否含有BeanPostProcessor類,如果有生成BeanPostProcessor對象,並且將其實例添加到beanPostProcessorList容器中。在此就需要定義ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();來記錄BeanPostProcessor。

// 6 判斷是否是並加入beanPostProcessorList,這裡不能使用instanceof
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
    // 直接生成對象
    BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
    // 然後保存進去
    beanPostProcessorList.add(instance);
}

接著就可以在創建bean對象的時候,在初始化前後去遍歷這個BeanPostProcessor鏈表,調用相應的方法,就能夠調用自定義的MyBeanPostProcessor的方法。

// 6 BeanPostProcessor 初始化前 AOP 遍歷beanPostProcessorList
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
    instance = beanPostProcessor.postBeforeProcessor(beanName, instance);
}

// 5 初始化
if (instance instanceof InitializingBean) {
    ((InitializingBean)instance).afterPropertiesSet();
}

// 6 BeanPostProcessor 初始化後 AOP
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
    instance = beanPostProcessor.postAfterProcessor(beanName, instance);
}

運行結果:.
image.png

AOP機制

需要通過代理對象。這樣如果不進行操作,返回的對象還是原來的,如果是通過操作了,那麼返回的就是代理的對象。這裡就簡單的列印一句話。在掃描的時候,掃到userService這個bean的時候,返回的實例就是代理對象。

@Override
public Object postAfterProcessor(String beanName, Object bean) {
    System.out.println("初始化後的bean:" + beanName + " -- " + bean);

    if (beanName.equals("userService")) {
        // 創建一個代理對象, 代理的是UserInterface這個
        Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            /**
             * proxy:代理對象
             * method:代理對象當前正在執行的方法
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("切面邏輯");
                return method.invoke(bean, args);
            }
        });
        return proxyInstance;
    }
    return bean;
}

源碼備註

spring

LydApplicationContext

public class LydApplicationContext {
    private Class configClass; // 注入的配置類
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); // key是component名字
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>(); // 單例池
    private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>(); // 記錄BeanPostProcessor,在掃描的時候判斷,使用

    public LydApplicationContext(Class configClass) { // 構造方法
        this.configClass = configClass;
        // spring容器創建之後
        // 1 掃描
        // 通過配置文件的註解獲取掃描路徑
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            // 1.1 掃描路徑:只是個包名,掃描的是java的class文件,而並非源文件,com.lyd.service
            String path = componentScanAnnotation.value();
            // 1.2 將路徑文件替換成/的形式
            path = path.replace(".","/");
            // 1.3 通過類載入器獲取資源路徑
            ClassLoader classLoader = LydApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            // 1.4 轉成文件形式,主要是為了獲取他的絕對地址
            File file = new File(resource.getFile());
//            System.out.println(file);
            if (file.isDirectory()) { // 如果是文件夾
                File[] files = file.listFiles();
                for (File f : files) {
                    String absolutePath = f.getAbsolutePath(); // 獲取絕對路徑
                    System.out.println("絕對路徑:" + absolutePath);
                    // 1.5 對編譯文件進行處理
                    if (absolutePath.endsWith(".class")) { // 判斷是否為編譯文件
                        /**
                         * 需要拿到的是編譯文件,通過類載入器去獲取
                         * 需要將com\lyd\service\UserService轉成com.lyd.service.UserService
                         */
                        String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                        className = className.replace("\\", ".");
                        System.out.println("類名:" + className);
                        try {
                            // 1.6 通過全限定名使用類載入器獲取類 (利用反射機制)
                            Class<?> clazz = classLoader.loadClass(className);
                            // 1.7 在通過這個clazz(類)來判斷是否有component註解,有則是bean
                            if (clazz.isAnnotationPresent(Component.class)) {
                                // 6 判斷是否是並加入beanPostProcessorList,這裡不能使用instanceof
                                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                    // 直接生成對象
                                    BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
                                    // 然後保存進去
                                    beanPostProcessorList.add(instance);
                                }
                                Component annotation = clazz.getAnnotation(Component.class);
                                String beanName = annotation.value();
                                // * 默認生成bean,如果只使用Component註解,沒有寫上beanName的值,那麼就需要自動生成
                                if (beanName.equals("")) {
                                    // 默認開頭小字母
                                    beanName = Introspector.decapitalize(clazz.getSimpleName());
                                }
                                /**
                                 * 這就是一個bean了
                                 * 然而在這裡並不是直接就創建bean了,bean分為了單例bean和多例bean
                                 */
                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(clazz);
                                if (clazz.isAnnotationPresent(Scope.class)) { // 判斷是單例還是多例
                                    Scope scope = clazz.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scope.value());
                                } else {
                                    beanDefinition.setScope("singleton");
                                }
                                beanDefinitionMap.put(beanName, beanDefinition);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        // 2 創建單例bean對象
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")) { // 那麼,如何保證單例呢?就需要有個單例池,singletonObjects
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean); // 將單例bean存到單例池中
            }
        }
    }

    // 創建bean
    private Object createBean(String beanName, BeanDefinition definition) {
        // 利用反射獲取實例,採用無參構造方法
        Class clazz = definition.getType();
        // 通過無參構造方法獲取實例
        try {
            Object instance = clazz.getConstructor().newInstance(); // 到這裡直接返回,bean的對象也就創建完成

            // 3 依賴注入
            for (Field field : clazz.getDeclaredFields()) {
                // 判斷欄位上是否存在Autowried註解
                if (field.isAnnotationPresent(Autowired.class)) {
                    /**
                     * 值為 true 則指示反射的對象在使用時應該取消 Java 語言訪問檢查。
                     * 值為 false 則指示反射的對象應該實施 Java 語言訪問檢查;
                     * 實際上setAccessible是啟用和禁用訪問安全檢查的開關,並不是為true就能訪問為false就不能訪問 ;
                     */
                    field.setAccessible(true); // 反射需要設置這個,不然無法賦值
                    // 用其屬性名,這就意味著private RoleService roleService;roleService不能亂取
                    field.set(instance, getBean(field.getName()));
                }
            }
            // * instanceof:是針對某個對象去判斷是否實現某個類
            // 4 Aware回調機制
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware)instance).setBeanName(beanName);
            }

            // 6 BeanPostProcessor 初始化前 AOP 遍歷beanPostProcessorList
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postBeforeProcessor(beanName, instance);
            }

            // 5 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean)instance).afterPropertiesSet();
            }

            // 6 BeanPostProcessor 初始化後 AOP
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postAfterProcessor(beanName, instance);
            }

            return instance;

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

    // 獲取bean對象
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException("找不到bean名字為[" + beanName + "]的bean對象");
        } else { // 找到就做相應的操作
            String scope = beanDefinition.getScope();
            if (scope.equals("singleton")) {
                // 通過單例池獲取
                Object bean = singletonObjects.get(beanName);
                if (bean == null) {
                    // 單例池中如果沒有bean,就需要去創建
                    bean = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName, bean);
                }
                return bean;
            } else {
                // 多例的就不需要記錄,每次都是通過創建
                return createBean(beanName, beanDefinition);
            }
        }
    }
}

service

test

public class Test {
    public static void main(String[] args) {
        LydApplicationContext applicationContext = new LydApplicationContext(AppConfig.class);
//        UserService userService = (UserService) applicationContext.getBean("userService");
        UserInterface userService = (UserInterface) applicationContext.getBean("userService");
//        System.out.println(applicationContext.getBean("userService"));
//        System.out.println(applicationContext.getBean("roleService"));
        userService.test();
    }
}

👍創作不易,如有錯誤請指正,感謝觀看!記得點贊哦!👍