spring最核心思想--ioc控制反轉

来源:https://www.cnblogs.com/jjdyzz/archive/2020/03/30/12563342.html
-Advertisement-
Play Games

一核心概念 控制反轉:將bean的生成交給容器,程式可以從容器中獲取指定的bean。 個人理解:此優勢也是spring能夠流行併成為java主流框架的主要原因,java是幫助java程式員以對象的方式管理 記憶體,而spring則是一個管理對象的框架。如果使用spring,在開發中基本上不需要考慮記憶體 ...


一核心概念    

控制反轉:將bean的生成交給容器,程式可以從容器中獲取指定的bean。

個人理解:此優勢也是spring能夠流行併成為java主流框架的主要原因,java是幫助java程式員以對象的方式管理 記憶體,而spring則是一個管理對象的框架。如果使用spring,在開發中基本上不需要考慮記憶體相關的問題。

接下來從一個設計者的思路去交流下spring的設計思路

 

二需求

基於以上的需求,我們需要做的核心的需求的是:生成,管理bean,向使用者提供。

三設計

看到這,是不是第一反應就是可以用工廠模式,沒錯,spring框架中針對此設計也是工廠模式。核心類為Beanactory,核心方法為getBean()。

1 public interface BeanFactory {
2     /**
3      * 獲取bean
4      * @param name bean的名字
5      * @return bean 實例
6      * @throws Exception
7      */
8     Object getBean(String name) throws Exception;
9 }

現在有個工廠,同時我們面對2個問題:

1BeanFactory如何知道生成什麼樣的bean(bean是由用戶指定的)?

2用戶如何定義一個bean,便於用戶使用也便於beanFactory?

 

接下來先考慮第二個問題,現在誰也不知道用戶將定義怎樣的bean,那就來個萬能大法,先定義的一個介面專門做這個事,介面為BeanDefinition。

回到第一個問題,我們如何將BeanDefinition告訴給BeanFactory?解決這個問題同時我們還要考慮可擴展性。這個解決方案是註冊模式。

public interface BeanDefinitionRegistry {
  /**
   * 註冊bean定義
  * @param beanName bean名稱(bean的唯一標識)
  * @param beanDefinition bean定義
  * @throws BeanDefinitionRegistException 註冊異常
  */
  void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException;
}

 

每個bean通過BeanDefinitionRegistry介面通知給BeanFactory。

目前只剩下一個問題:BeanDefinition如何定義,通俗點就是BeanDefinition裡面都有什麼。

Bean生成都有哪些情況,

1是否單例

2bean類名

3生成方式:

  3.1指定初始化方法

    必須的信息:bean的類名

  3.2通過工廠模式

    3.2.1靜態的

    

public class PersonFactory{  

  public static Person getPerson(){
   return new Person();
  }
}

 

    3.2.2成員方法

public class PersonFactory{  
    public Person getPerson(){
       return new Person();  
    } 
} 

   工廠模式下,必須獲取的信息如下:工廠bean的命,工廠方法名

  3.3new的方式

4銷毀的方法

具體介面如下:

public interface BeanDefinition {

    String SCOPE_SINGLETION = "singleton";

    String SCOPE_PROTOTYPE = "prototype";

    /**
     * 類
     */
    Class<?> getBeanClass();

    /**
     * Scope
     */
    String getScope();

    /**
     * 是否單例
     */
    boolean isSingleton();

    /**
     * 是否原型
     */
    boolean isPrototype();

    /**
     * 工廠bean名
     */
    String getFactoryBeanName();

    /**
     * 工廠方法名
     */
    String getFactoryMethodName();

    /**
     * 初始化方法
     */
    String getInitMethodName();

    /**
     * 銷毀方法
     */
    String getDestroyMethodName();

    /**
     * 校驗bean定義的合法性
     */
    default boolean validate() {
        // 沒定義class,工廠bean或工廠方法沒指定,則不合法。
        if (this.getBeanClass() == null) {
            if (StringUtils.isBlank(getFactoryBeanName()) || StringUtils.isBlank(getFactoryMethodName())) {
                return false;
            }
        }

        // 定義了類,又定義工廠bean,不合法
        if (this.getBeanClass() != null && StringUtils.isNotBlank(getFactoryBeanName())) {
            return false;
        }

        return true;
    }

}

 

 

ioc容器的主要設計均已設計完成。

簡單的實現源代碼如下:

package core.ioc.impl;


import core.exception.BeanDefinitionRegistException;
import core.ioc.BeanDefinition;
import core.ioc.BeanDefinitionRegistry;
import core.ioc.BeanFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


import javax.xml.ws.WebServiceException;
import java.io.Closeable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {

    private final Log logger = LogFactory.getLog(DefaultBeanFactory.class);

    //存在beanDefinition的緩存
    private Map<String,BeanDefinition> beanDefinitionMap= new ConcurrentHashMap<>(256);
    //存放單例bean實例的緩存
    private Map<String,Object> beanMap = new ConcurrentHashMap<>(256);

    //獲取bean實例
    public Object getBean(String name) throws Exception {
        return this.doGetBean(name);
    }

    protected Object doGetBean(String beanName) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //驗證bean不能為空
        Objects.requireNonNull(beanName,"beanName不能為空");

        //查看是否已經創建,如果已經創建則直接從緩存中取得返回
        Object instance = beanMap.get(beanName);
        if(null!=instance){
            return instance;
        }

        //如果沒有創建,則需要創建,
        BeanDefinition bd = this.getBeanDefinition(beanName);
        Objects.requireNonNull(bd,"beanDefinition 不能為空");

        Class<?> type=bd.getBeanClass();
        if(type!=null){
            if(StringUtils.isBlank(bd.getFactoryMethodName())){
                //構造方法來構造對象
                instance =this.createInstanceByConstructor(bd);
            }else{
                //通過靜態工廠方法創建對象
                instance=this.createInstanceByStaticFactoryMethod(bd);

            }
        }else{
            //通過工廠bean方式來構造對象
            instance=this.createInstanceByFactoryBean(bd);
        }


        //執行初始化方法,比如說給屬性賦值等
        this.doInit(bd,instance);

        //如果是單例,則將bean實例放入緩存中
        if(bd.isSingleton()){
            beanMap.put(beanName,instance);
        }

        return instance;
    }


    /**
     * 通過構造方法來構造對象
     * @param bd dean定義
     * @return bean實例
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private Object createInstanceByConstructor(BeanDefinition bd) throws IllegalAccessException, InstantiationException {
        return bd.getBeanClass().newInstance();
    }

    /**
     * 通過靜態工廠方法創建bean
     * @param bd bean定義
     * @return bean 實例
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<?> type=bd.getBeanClass();
        Method m=type.getMethod(bd.getFactoryMethodName(),null);
        return m.invoke(type,null);
    }

    /**
     * 通過工廠bean 方式來構造對象
     * @param bd
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     */
    private Object createInstanceByFactoryBean(BeanDefinition bd) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
        Object factoryBean =this.doGetBean(bd.getFactoryBeanName());
        Method m=factoryBean.getClass().getMethod(bd.getFactoryMethodName(),null);

        return m.invoke(factoryBean,null);

    }




    /**
     * 初始化
     * @param bd
     * @param instance
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private void doInit(BeanDefinition bd,Object instance) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        if(StringUtils.isNotBlank(bd.getInitMehtodName())){
            Method m=instance.getClass().getMethod(bd.getInitMehtodName(),null);
            m.invoke(instance,null);
        }
    }




    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException {
        Objects.requireNonNull(beanName,"註冊bean需要給入beanName");
        Objects.requireNonNull(beanDefinition,"註冊bean需要給入beanDefinition");

        //檢驗給如的bean是否合法
        if(!beanDefinition.validata()){
            throw new BeanDefinitionRegistException(String.format("名字為[%s]的bean的定義不合法:%s",beanName,beanDefinition));
        }

        //驗證beanDefinition已經存在
        if(this.containBeanDefinition(beanName)){
            throw new BeanDefinitionRegistException(String.format("名字為[%s]的bean定義已經存在:%s",
                    beanName,this.getBeanDefinition(beanName)));
        }

        this.beanDefinitionMap.put(beanName,beanDefinition);
    }

    /**
     * 獲取beanDefinition
     * @param beanName bean的名稱 唯一標識
     * @return beanDefinition
     */
    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        return this.beanDefinitionMap.get(beanName);
    }


    /**
     * 驗證beanDefinition是否已經存在
     * @param beanName bean的名稱 唯一標識
     * @return true:已存在 false:不存在
     */
    @Override
    public boolean containBeanDefinition(String beanName) {
        return this.beanDefinitionMap.containsKey(beanName);
    }

    /**
     * 執行指定的銷毀方法
     * @throws WebServiceException
     */
    @Override
    public void close() throws WebServiceException {
        //執行單例實例的銷毀方法
        for(Map.Entry<String,BeanDefinition> e:this.beanDefinitionMap.entrySet()){
            String beanName=e.getKey();
            BeanDefinition bd=e.getValue();

            if(bd.isSingleton() && StringUtils.isNotBlank(bd.getDestroyMethodName())){
                Object instance = this.beanMap.get(beanName);
                try {
                    Method m = instance.getClass().getMethod(bd.getDestroyMethodName());
                    m.invoke(instance,null);
                } catch (NoSuchMethodException e1) {
                    logger.error(String.format("執行bean[%s] %s 的 銷毀方法異常!",beanName,bd), e1);
                    e1.printStackTrace();
                } catch (IllegalAccessException e1) {
                    logger.error(String.format("執行bean[%s] %s 的 銷毀方法異常!",beanName,bd), e1);
                    e1.printStackTrace();
                } catch (InvocationTargetException e1) {
                    logger.error(String.format("執行bean[%s] %s 的 銷毀方法異常!",beanName,bd), e1);
                    e1.printStackTrace();
                }
            }
        }

    }
}
DefaultBeanFactory
package core.ioc.impl;

import core.ioc.BeanDefinition;
import org.apache.commons.lang3.StringUtils;

public class GenericBeanDefinition implements BeanDefinition {


    private Class<?> beanClass;

    //是否為單例
    private String scope = BeanDefinition.SCOPE_SINGLETION;

    //bean工廠的名稱
    private String factoryBeanName;
    //bean工廠方法名
    private String factoryMethodName;

    //初始化方法
    private String initMethodName;
    //銷毀方法
    private String destroyMethodName;




    /**
     * 自動生成設置的方法 start
     */
    public void setBeanClass(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

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

    public void setFactoryBeanName(String factoryBeanName) {
        this.factoryBeanName = factoryBeanName;
    }

    public void setFactoryMethodName(String factoryMethodName) {
        this.factoryMethodName = factoryMethodName;
    }

    public void setInitMethodName(String initMethodName) {
        this.initMethodName = initMethodName;
    }

    public void setDestroyMethodName(String destroyMethodName) {
        this.destroyMethodName = destroyMethodName;
    }
    /**
     * 自動生成設置的方法 end
     */


    @Override
    public Class<?> getBeanClass() {
        return this.beanClass;
    }

    @Override
    public String getScope() {

        return this.scope;
    }

    @Override
    public boolean isSingleton() {
        return BeanDefinition.SCOPE_SINGLETION.equals(this.scope);
    }

    @Override
    public boolean isPrototype() {
        return BeanDefinition.SCOPE_PROTOTYPE.equals(this.scope);
    }

    @Override
    public String getFactoryBeanName() {
        return this.factoryBeanName;
    }

    @Override
    public String getFactoryMethodName() {
        return this.factoryMethodName;
    }

    @Override
    public String getInitMehtodName() {
        return this.initMethodName;
    }

    @Override
    public String getDestroyMethodName() {
        return this.destroyMethodName;
    }



    @Override
    public String toString() {
        return String.format("GenericBeanDefinition [beanClass=%s, scope=%s, factoryBeanName=%s, " +
                "factoryMethodName=%s, initMethodName=%s, destroyMethodName=%s]",
                beanClass,scope,factoryBeanName,factoryMethodName,initMethodName,destroyMethodName);
    }

    /**
     * 疑問: 為什麼要重寫equals 方法
     *
     * 重寫equals方法需要註意以下幾點:
     * 1自反性:對於任何非空引用x,x.equals(x)應該返回true
     * 2對稱:對於任何引用x,y,如果x.equals(y) 返回true,那麼 y.equals(x)也應該返回true。
     * 3傳遞性:對於任何引用x,y和z,如果x=y 為true,那麼y=z也一定為true,x=z也一定為true。
     * 4一致性:如果x和y引用的對象沒有發生變化,那麼返回調用x.equals(y),應該返回同樣的結果。
     * 5非空性:對於任意非空引用x,x.equals(null)應該返回false。
     *
     * 重寫equals方法,就必須重寫hashCode
     * 原因是HashMap的需要
     */

    @Override
    public boolean equals(Object obj) {
        if(this==obj){
            return true;
        }

        if(null==obj){
            return false;
        }

        if(getClass() !=obj.getClass()){
            return false;
        }

        GenericBeanDefinition other=(GenericBeanDefinition) obj;

        //驗證每個屬性是否相當,只有當每個屬性均相等時,才是一個對象
        if(beanClass ==null){
            if(other.beanClass!=null){
                return false;
            }
        }else if(!beanClass.equals(other.beanClass)){
            return false;
        }

        if(destroyMethodName== null){
            if(other.destroyMethodName!=null){
                return false;
            }
        }else if(!destroyMethodName.equals(other.destroyMethodName) ){
            return false;
        }

        if(factoryBeanName== null){
            if(other.factoryBeanName!=null){
                return false;
            }
        }else if(!factoryBeanName.equals(other.factoryBeanName) ){
            return false;
        }

        if(factoryMethodName== null){
            if(other.factoryMethodName!=null){
                return false;
            }
        }else if(!factoryMethodName.equals(other.factoryMethodName) ){
            return false;
        }

        if(initMethodName== null){
            if(other.initMethodName!=null){
                return false;
            }
        }else if(!initMethodName.equals(other.initMethodName) ){
            return false;
        }

        if(scope== null){
            if(other.scope!=null){
                return false;
            }
        }else if(!scope.equals(other.scope) ){
            return false;
        }


        return true;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((beanClass == null) ? 0 : beanClass.hashCode());
        result = prime * result + ((destroyMethodName == null) ? 0 : destroyMethodName.hashCode());
        result = prime * result + ((factoryBeanName == null) ? 0 : factoryBeanName.hashCode());
        result = prime * result + ((factoryMethodName == null) ? 0 : factoryMethodName.hashCode());
        result = prime * result + ((initMethodName == null) ? 0 : initMethodName.hashCode());
        result = prime * result + ((scope == null) ? 0 : scope.hashCode());
        return result;
    }
}

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 通常的項目結構 各個目錄詳細介紹: 然後接下來/src/main/resources目錄,裡面主要存放靜態配置文件和頁面靜態資源等東西: 當然,這地方估計有一個很多人都會糾結的關於DTO/VO/DO等數據模型定義的區分。 這在《阿裡巴巴Java開發手冊》中倒是做了一個所謂的嚴格區分,那本書上是這樣去 ...
  • 題目:有1、2、3、4個數字,能組成多少個互不相同且無重覆數字的三位數?都是多少? 程式分析:可填在百位、十位、個位的數字都是1、2、3、4。組成所有的排列後再去 掉不滿足條件的排列。 實例: 1 #include<stdio.h> 2 3 int main() 4 { 5 int i,j,k; 6 ...
  • 前言 眾所周知,maven 實質上是一個插件執行框架,所有的工作都是通過插件完成的。包括我們日常使用到的類似 install、clean、deploy、compiler。。。這些命令,其實底層都是一個一個的 maven 插件。 如何開發自己的插件 1. maven 插件的命名規範 在寫一個項目之前, ...
  • windows10環境下QtCreator中出現skipping incompatible xxx when searching for xxx 我再QtCreator中想導入一個外部庫時,他提示不匹配 出現這種問題是因為QtCreator 和 MinGW 其中一個是32位 ,而另一個是64位, 將 ...
  • 閱讀可能會花上您短短幾分鐘。 大學, 高中沒怎麼花心思讀書,上的是一所三流大學,選擇的電腦專業。 懷著對大學的憧憬,想象著教室里為數不多的馬尾辮,一臺臺電腦前大家熾熱中迸發思考的眼神與鍵盤敲擊聲。 好吧,大家別打了,我說實話還不行嗎,跟大多數同學一樣,基本在混,打游戲,逃課,並寬慰自己大學不經歷這 ...
  • 不足之處,還請海涵,請指出不足。本人發佈過的文章,會不斷更改,力求減少錯誤信息。 Python安裝請借鑒網址https://www.runoob.com/python/python-install.html 安裝註意:(雖然上方鏈接已給出正規(個人認為)安裝過程,但仍有不詳細處。由於本人安裝過一定數 ...
  • 題目:圓圈中最後剩下的數字 0,1,,n-1這n個數字排成一個圓圈,從數字0開始,每次從這個圓圈裡刪除第m個數字。求出這個圓圈裡剩下的最後一個數字。 例如,0、1、2、3、4這5個數字組成一個圓圈,從數字0開始每次刪除第3個數字,則刪除的前4個數字依次是2、0、4、1,因此最後剩下的數字是3。 示例 ...
  • 我的LeetCode:https://leetcode cn.com/u/ituring/ 我的LeetCode刷題源碼[GitHub]:https://github.com/izhoujie/Algorithmcii LeetCode 面試題62. 圓圈中最後剩下的數字 題目 0,1,,n 1這n ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...