1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        五分鐘,手擼一個Spring容器!

        共 10252字,需瀏覽 21分鐘

         ·

        2022-06-30 21:10

        大家好,我是魚皮~今天帶大家手擼一個Spring容器,讓我們回歸Spring的本質,揭開Spring神秘的面紗~

        從什么是IOC開始?

        Spring——春天,Java編程世界的春天是由一位音樂家——Rod Johnson帶來的。

        Rod Johnson先后編寫了兩本巨著《Expert One-on-One J2EE Design and Development》、《Expert One-on-One J2EE Development without EJB》,拉起了挑戰(zhàn)正統(tǒng)Java EE框架EJB的大旗。

        Rod Johnson兩大著作-來自百度百科

        Rod Johnson不僅是一名旗手,更是開發(fā)了Spring這一輕量級框架,像一名勇敢的龍騎兵一樣,對EJB發(fā)動了沖鋒,并最終戰(zhàn)勝了EJB,讓Spring成為Java EE事實上的標準。

        Spring Logo

        Spring的兩大內(nèi)核分別是IOC和AOP,其中最最核心的是IOC。

        所謂的IOC(控制反轉):就是由容器來負責控制對象的生命周期和對象間的關系。以前是我們想要什么,就自己創(chuàng)建什么,現(xiàn)在是我們需要什么,容器就給我們送來什么。

        引入IOC之前和引入IOC之后

        也就是說,控制對象生命周期的不再是引用它的對象,而是容器。對具體對象,以前是它控制其它對象,現(xiàn)在所有對象都被容器控制,所以這就叫控制反轉

        控制反轉示意圖

        也許你還聽到另外一個概念DI(依賴注入),它指的是容器在實例化對象的時候把它依賴的類注入給它,我們也可以認為,DI是IOC的補充和實現(xiàn)。

        工廠和Spring容器

        Spring是一個成熟的框架,為了滿足擴展性、實現(xiàn)各種功能,所以它的實現(xiàn)如同枝節(jié)交錯的大樹一樣,現(xiàn)在讓我們把視線從Spring本身移開,來看看一個萌芽版的Spring容器怎么實現(xiàn)。

        Spring的IOC本質就是一個大工廠,我們想想一個工廠是怎么運行的呢?

        工廠運行
        • 生產(chǎn)產(chǎn)品:一個工廠最核心的功能就是生產(chǎn)產(chǎn)品。在Spring里,不用Bean自己來實例化,而是交給Spring,應該怎么實現(xiàn)呢?——答案毫無疑問,反射

          那么這個廠子的生產(chǎn)管理是怎么做的?你應該也知道——工廠模式

        • 庫存產(chǎn)品:工廠一般都是有庫房的,用來庫存產(chǎn)品,畢竟生產(chǎn)的產(chǎn)品不能立馬就拉走。Spring我們都知道是一個容器,這個容器里存的就是對象,不能每次來取對象,都得現(xiàn)場來反射創(chuàng)建對象,得把創(chuàng)建出的對象存起來。

        • 訂單處理:還有最重要的一點,工廠根據(jù)什么來提供產(chǎn)品呢?訂單。這些訂單可能五花八門,有線上簽簽的、有到工廠簽的、還有工廠銷售上門簽的……最后經(jīng)過處理,指導工廠的出貨。

          在Spring里,也有這樣的訂單,它就是我們bean的定義和依賴關系,可以是xml形式,也可以是我們最熟悉的注解形式。

        那對應我們的萌芽版的Spring容器是什么樣的呢?

        mini版本Spring IOC

        訂單:Bean定義

        Bean可以通過一個配置文件定義,我們會把它解析成一個類型。

        Bean定義
        • beans.properties

          為了偷懶,這里直接用了最方便解析的properties,用一個<key,value>類型的配置來代表Bean的定義,其中key是beanName,value是class

          userDao:cn.fighter3.bean.UserDao
        • BeanDefinition.java

          bean定義類,配置文件中bean定義對應的實體

          public class BeanDefinition {

              private String beanName;

              private Class beanClass;
               //省略getter、setter  
           }   

        獲取訂單:資源加載

        接下訂單之后,就要由銷售向生產(chǎn)部門交接,讓生產(chǎn)部門知道商品的規(guī)格、數(shù)量之類。

        資源加載器,就是來完成這個工作的,由它來完成配置文件中配置的加載。

        public class ResourceLoader {

            public static Map<String, BeanDefinition> getResource() {
                Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(16);
                Properties properties = new Properties();
                try {
                    InputStream inputStream = ResourceLoader.class.getResourceAsStream("/beans.properties");
                    properties.load(inputStream);
                    Iterator<String> it = properties.stringPropertyNames().iterator();
                    while (it.hasNext()) {
                        String key = it.next();
                        String className = properties.getProperty(key);
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setBeanName(key);
                        Class clazz = Class.forName(className);
                        beanDefinition.setBeanClass(clazz);
                        beanDefinitionMap.put(key, beanDefinition);
                    }
                    inputStream.close();
                } catch (IOException | ClassNotFoundException e) {
                    e.printStackTrace();
                }
                return beanDefinitionMap;
            }

        }

        訂單分配:Bean注冊

        對象注冊器,這里用于單例bean的緩存,我們大幅簡化,默認所有bean都是單例的??梢钥吹剿^單例注冊,也很簡單,不過是往HashMap里存對象。

        public class BeanRegister {

            //單例Bean緩存
            private Map<String, Object> singletonMap = new HashMap<>(32);

            /**
             * 獲取單例Bean
             *
             * @param beanName bean名稱
             * @return
             */

            public Object getSingletonBean(String beanName) {
                return singletonMap.get(beanName);
            }

            /**
             * 注冊單例bean
             *
             * @param beanName
             * @param bean
             */

            public void registerSingletonBean(String beanName, Object bean) {
                if (singletonMap.containsKey(beanName)) {
                    return;
                }
                singletonMap.put(beanName, bean);
            }

        }

        生產(chǎn)車間:對象工廠

        好了,到了我們最關鍵的生產(chǎn)部門了,在工廠里,生產(chǎn)產(chǎn)品的是車間,在IOC容器里,生產(chǎn)對象的是BeanFactory。

        BeanFactory
        • 對象工廠,我們最核心的一個類,在它初始化的時候,創(chuàng)建了bean注冊器,完成了資源的加載。

        • 獲取bean的時候,先從單例緩存中取,如果沒有取到,就創(chuàng)建并注冊一個bean

          public class BeanFactory {

              private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

              private BeanRegister beanRegister;

              public BeanFactory() {
                  //創(chuàng)建bean注冊器
                  beanRegister = new BeanRegister();
                  //加載資源
                  this.beanDefinitionMap = new ResourceLoader().getResource();
              }

              /**
               * 獲取bean
               *
               * @param beanName bean名稱
               * @return
               */

              public Object getBean(String beanName) {
                  //從bean緩存中取
                  Object bean = beanRegister.getSingletonBean(beanName);
                  if (bean != null) {
                      return bean;
                  }
                  //根據(jù)bean定義,創(chuàng)建bean
                  return createBean(beanDefinitionMap.get(beanName));
              }

              /**
               * 創(chuàng)建Bean
               *
               * @param beanDefinition bean定義
               * @return
               */

              private Object createBean(BeanDefinition beanDefinition) {
                  try {
                      Object bean = beanDefinition.getBeanClass().newInstance();
                      //緩存bean
                      beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
                      return bean;
                  } catch (InstantiationException | IllegalAccessException e) {
                      e.printStackTrace();
                  }
                  return null;
              }
          }

        生產(chǎn)銷售:測試

        • UserDao.java

          我們的Bean類,很簡單

          public class UserDao {

              public void queryUserInfo(){
                  System.out.println("A good man.");
              }
          }
        • 單元測試

          public class ApiTest {
              @Test
              public void test_BeanFactory() {
                  //1.創(chuàng)建bean工廠(同時完成了加載資源、創(chuàng)建注冊單例bean注冊器的操作)
                  BeanFactory beanFactory = new BeanFactory();

                  //2.第一次獲取bean(通過反射創(chuàng)建bean,緩存bean)
                  UserDao userDao1 = (UserDao) beanFactory.getBean("userDao");
                  userDao1.queryUserInfo();

                  //3.第二次獲取bean(從緩存中獲取bean)
                  UserDao userDao2 = (UserDao) beanFactory.getBean("userDao");
                  userDao2.queryUserInfo();
              }
          }
        • 運行結果

          A good man.
          A good man.

        至此,我們一個萌芽版的Spring容器就完成了。

        考慮一下,它有哪些不足呢?是否還可以抽象、擴展、解耦……

        細細想想這些東西,你是不是對真正的Spring IOC容器為何如此復雜,有所理解了呢?


        以上就是本期分享了。

        最后,歡迎加入 魚皮的編程知識星球(點擊了解詳情),和 8600 多名小伙伴們一起交流學習,向魚皮和大廠同學 1 對 1 提問、幫你制定學習計劃不迷茫、跟著魚皮直播做項目(往期項目可無限回看)、領取魚皮原創(chuàng)編程學習/求職資料等。


        往期推薦

        編程導航,火了!

        錯失字節(jié)跳動數(shù)億身價!我有話說

        沒有操作系統(tǒng),程序還能運行嗎?

        全棧初中生,牛皮!

        這些JS題面試時一定要答對

        瀏覽 41
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            日本三级在线视频 | 日韩人妻一区二区三区蜜桃视频密 | AV五十路 | 波多野结衣操逼 | 爱爱网站免费 | 综合色吧 | 粉嫩小穴视频 | 97超碰精品 | 丁香五月天天操天天爽 | 1000部啪啪未满十八勿入软件 |