1. 程式人生 > >自己實現spring(一) —— ioc容器實現

自己實現spring(一) —— ioc容器實現

前言:加深對spring的理解,把spring ioc的大致流程抽出來,自己動手擼一個簡潔版的

  • 新建一個maven工程,這裡使用idea工具
    這裡寫圖片描述
    pom檔案只依賴一個servlet
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>4.0.0</modelVersion> <groupId>com.fanday</groupId> <artifactId>spring</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId
>
javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> </dependencies> </project>

專案結構:

  • ioc spring的bean工廠,完成定位 註冊 初始化 注入等過程
  • mvc spring的web層mvc框架,負責請求的分發
  • demo 用於測試框架使用

編寫介面定義

  • ApplicationContext主要定義獲取bean的方法,有根據id獲取的,也有獲取所有bean的
package com.fanday.ioc;
import java.util.Map;

public interface ApplicationContext {
    /**
     * 根據id獲取bean
     * @param id
     * @return
     */
    Object getBean(String id);

    /**
     * 根據id獲取特定型別的bean,完成強轉
     * @param id
     * @param clazz
     * @param <T>
     * @return
     */
    <T>T getBean(String id,Class<T> clazz);

    /**
     * 獲取工廠內的所有bean集合
     * @return
     */
    Map<String,Object> getBeans();
}
  • BeanRegister定義向工廠註冊bean 和BeanDefinition的方法
package com.fanday.ioc;
import com.fanday.ioc.support.BeanDefinition;

import java.util.List;

public interface BeanRegister {
    /**
     * 向工廠內註冊BeanDefinition
     * @param bds
     */
    void registBeanDefinition(List<BeanDefinition> bds);

    /**
     * 向工廠內註冊bean例項物件
     * @param id
     * @param instance
     */
    void registInstanceMapping(String id,Object instance);
}

這裡寫圖片描述

support包主要是一些預設的工廠實現,annotation包是一些需要的註解

package com.fanday.ioc.annotation;
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)//執行時註解
@Target(ElementType.TYPE)//作用在類上面
@Documented
public @interface Controller {
    String value() default "";
}
package com.fanday.ioc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Component {
    String value() default "";
}
package com.fanday.ioc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//作用在欄位上面
@Documented
public @interface Autowire {
    String value() default "";
}
package com.fanday.ioc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})//作用在類和方法 仿照springmvc的套路來
@Documented
public @interface RequestMapping {
    String value() default "";
}
package com.fanday.ioc.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)//作用在方法的引數上面
@Documented
public @interface RequestParam {
    String value() default "";
}

resources下新建一個applicationContext.properties檔案用於配置,容器啟動時要掃描的包
scanPackage=com.fanday.demo

AnnotationApplicationContext 容器的具體實現

package com.fanday.ioc.support;

import com.fanday.ioc.ApplicationContext;
import com.fanday.ioc.BeanRegister;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

public class AnnotationApplicationContext implements ApplicationContext,BeanRegister {
    private Map<String,Object> instanceMapping = new ConcurrentHashMap<String, Object>();

    //儲存所有bean的資訊,主要包含bean的型別  id等資訊
    private List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
    //配置檔案的config,這裡為了簡單我們使用properties檔案
    private Properties config = new Properties();

    public AnnotationApplicationContext(String location){
        InputStream is = null;
        try{

            //1、定位
            is = this.getClass().getClassLoader().getResourceAsStream(location);

            //2、載入
            config.load(is);

            //3、註冊
            register();

            //4、例項化
            createBean();

            //5、注入
            populate();

        }catch(Exception e){
            e.printStackTrace();
        }finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 呼叫具體委派的注入類進行注入
     */
    private void populate() {
        Populator populator = new Populator();
        populator.populate(instanceMapping);
    }

    /**
     * 呼叫具體的建立物件建立bean
     */
    private void createBean() {
        BeanCreater creater = new BeanCreater(this);
        creater.create(beanDefinitions);
    }

    /**
     * 呼叫具體的註冊物件註冊bean資訊
     */
    private void register() {
        BeanDefinitionParser parser = new BeanDefinitionParser(this);
        parser.parse(config);
    }

    public Object getBean(String id) {
        return instanceMapping.get(id);
    }
    public Properties getConfig() {
        return this.config;
    }

    public <T> T getBean(String id, Class<T> clazz) {
        return (T)instanceMapping.get(id);
    }

    public Map<String, Object> getBeans() {
        return instanceMapping;
    }

    public void registBeanDefinition(List<BeanDefinition> bds) {
        beanDefinitions.addAll(bds);
    }

    public void registInstanceMapping(String id, Object instance) {
        instanceMapping.put(id,instance);
    }

}

BeanDefinitionParser完成掃描包下bean資訊的解析註冊

package com.fanday.ioc.support;

import com.fanday.ioc.BeanRegister;

import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Properties;

public class BeanDefinitionParser {
    //配置的掃描包的key
    public static final String SCAN_PACKAGE = "scanPackage";
    //容器註冊物件
    private BeanRegister register;
    public BeanDefinitionParser(BeanRegister register){
        this.register = register;
    }

    public void parse(Properties properties){
        //獲取要掃描的包
        String packageName = properties.getProperty(SCAN_PACKAGE);
        //執行註冊
        doRegister(packageName);
    }


    private void doRegister(String packageName) {
        //獲取此包名下的絕對路徑
        URL url = getClass().getClassLoader().getResource("./"+packageName.replaceAll("\\.","/"));
        File dir = new File(url.getFile());
        //迴圈遍歷  遞迴找到所有的java檔案
        for (File file:dir.listFiles()){
            if(file.isDirectory()){
                //資料夾-->遞迴繼續執行
                doRegister(packageName+"."+file.getName());
            }else {
                //處理檔名來獲取類名  執行時獲取到的是class檔案
                String className = packageName+"."+file.getName().replaceAll(".class","").trim();
                //呼叫BeanDefinitionGenerator.generate(className)方法,來處理
                //1.類帶有容器要處理的註解,則解析id生成BeanDefinition集合返回
                //2.不帶有需要處理的註解   直接返回null
                List<BeanDefinition> definitions = BeanDefinitionGenerator.generate(className);
                if(definitions == null)continue;
                //呼叫容器的註冊方法來完成bean資訊的註冊
                this.register.registBeanDefinition(definitions);
            }
        }

    }
}

BeanDefinitionGenerator根據具體的類名來完成BeanDefinition的生成

package com.fanday.ioc.support;

import com.fanday.ioc.annotation.Component;
import com.fanday.ioc.annotation.Controller;

import java.util.ArrayList;
import java.util.List;

public class BeanDefinitionGenerator {

    public static List<BeanDefinition> generate(String className){
        try {
            Class clazz = Class.forName(className);
            String[] ids = generateIds(clazz);
            if(ids==null)return null;
            List<BeanDefinition> list = new ArrayList<BeanDefinition>();
            for (String id:ids){
                list.add(new BeanDefinition(id,clazz));
            }
            return list;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 生成id陣列
     * 1.帶有@Controller 註解但是註解value沒給值,@Controller一般沒有
     * 介面定義,用類的全名作為id返回ids長度為1 
     * [email protected] 沒有value  獲取所有的實現的介面,介面名為id,返貨ids陣列
     * 長度是實現的介面個數
     * [email protected] 有value 返回id=value
     * 4.不帶容器要例項化的註解  null
     */
    private static String[] generateIds(Class clazz) {
        String[] ids = null;
        if (clazz.isAnnotationPresent(Controller.class)) {
            ids = new String[]{clazz.getName()};
        } else if (clazz.isAnnotationPresent(Component.class)) {
            Component component = (Component) clazz.getAnnotation(Component.class);
            String value = component.value();
            if (!"".equals(value)) {
                ids = new String[]{value};
            } else {
                Class<?>[] interfaces = clazz.getInterfaces();
                ids = new String[interfaces.length];
                //如果這個類實現了介面,就用介面的型別作為id
                for (int i = 0; i < interfaces.length; i++){
                    ids[i] = interfaces[i].getName();
                }
                return ids;
            }
        }
        return ids;
    }

}
package com.fanday.ioc.support;

public class BeanDefinition{
    private String id;
    private Class clazz;
    public BeanDefinition(String id, Class clazz){
        this.id = id;
        this.clazz = clazz;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Class getClazz() {
        return clazz;
    }

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

    public Object getInstance(){
        try {
            return clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

BeanCreater比較簡單,建立bean並且新增到容器工廠

package com.fanday.ioc.support;

import com.fanday.ioc.BeanRegister;

import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Properties;

public class BeanCreater {

    private BeanRegister register;
    public BeanCreater(BeanRegister register){
        this.register = register;
    }

    public void create(List<BeanDefinition> bds){
        for (BeanDefinition bd:bds){
            doCreate(bd);
        }
    }

    private void doCreate(BeanDefinition bd) {
        Object instance = bd.getInstance();
        this.register.registInstanceMapping(bd.getId(),instance);
    }
}

bean都初始化完成了,接下來進行bean之間的依賴注入

package com.fanday.ioc.support;

import java.lang.reflect.Field;
import java.util.Map;
import com.fanday.ioc.annotation.Autowire;

public class Populator {

    public Populator(){
    }

    public void populate(Map<String,Object> instanceMapping){
        //首先要判斷ioc容器中有沒有東西
        if(instanceMapping.isEmpty())return;

        //迴圈遍歷每一個容器中得物件
        for (Map.Entry<String,Object> entry:instanceMapping.entrySet()){
            //獲取物件的欄位
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field:fields){
                if(!field.isAnnotationPresent(Autowire.class))continue;
                Autowire autowire = field.getAnnotation(Autowire.class);
                //後去欄位要注入的id value  為空則按類名  介面名自動注入
                String id = autowire.value();
                if("".equals(id))id = field.getType().getName();
                field.setAccessible(true);
                try {
                    //反射注入
                    field.set(entry.getValue(),instanceMapping.get(id));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

至此 ioc的整個流程大致完成,我們來編寫測試

這裡寫圖片描述
這裡寫圖片描述

這裡寫圖片描述

ok,ioc容器實現已基本完成,下次一起來擼mvc

相關推薦

自己實現spring() —— ioc容器實現

前言:加深對spring的理解,把spring ioc的大致流程抽出來,自己動手擼一個簡潔版的 新建一個maven工程,這裡使用idea工具 pom檔案只依賴一個servlet <?xml version="1.0" encodi

Spring原始碼學習之IOC容器實現原理()-DefaultListableBeanFactory

從這個繼承體系結構圖來看,我們可以發現DefaultListableBeanFactory是第一個非抽象類,非介面類。實際IOC容器。所以這篇部落格以DefaultListableBeanFactoryIOC容器為基準進行IOC原理解析。 一.兩個重要介面 前面已經分析了BeanFactor,它的三個直接子

Spring IoC容器實現

127.0.0.1 兩種 [] 作用 隱式調用 ini 告訴 而是 lee 1,Spring的兩種IoC容器 BeanFactory 基礎類型的IoC容器; 采用延遲初始化策略(容器初始化完成後並不會創建bean的對象,只有當收到初始化請求時才進行初始化); 由於延遲初

Spring原始碼--IOC容器實現】(六)Bean的依賴注入

程式碼1.:8:BeanWrapperImpl類的setPropertyValue方法: @SuppressWarnings("unchecked") private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) t

Spring框架原始碼解析 IOC容器實現BeanDefinition(三)

我們找女朋友,首先必須保證是個女的,這是最低要求,生活不易,我們先從最低的要求出發吧。女朋友是一個抽象的概念,我們必須定義一些屬性,年齡,身高,名字,是否漂亮等等來描述她。不過目前這些統統都沒有,有的也就是說我們的最低要求,女的。 public interface Bean

spring技術內幕》學習(二)ioc容器實現

ioc容器和依賴反轉模式 在面向物件領域中,如果相互合作的物件的引用和依賴關係的管理由具體物件來完成,那麼會導致程式碼的高度耦合和可測試性的降低,這是非常不利的。而這些依賴關係可以通過把物件的依賴注入交給框架或者ioc容器來完成,這就是依賴控制反轉。 依賴控

自己實現IOC容器,java程式碼實現簡易版IOC容器IOC容器實現的步驟分解

一、需求   實現一個簡易的IOC容器,管理Bean,從IOC容器的BeanFactory中獲取例項,從而取代自己new例項的做法。 二、實現步驟分析 三、具體程式碼實現   自定義註解類 MyComponent 和 MyAutowired: 1 package MyIOCAnd

Spring源碼分析總結()-IOC容器初始化

Spring源碼分析總結一、IOC容器的初始化過程 IOC容器的初始化是由refresh()方法啟動。經常使用的ApplicationContext 有:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext、XmlWebApp

SpringFramework的核心:IOC容器實現------BeanDefinition在IoC容器中的註冊

之前分析了關BeanDefinition在IoC容器中的載入和解析的過程。但這些動作完成之後,使用者定義的BeanDefinition資訊已經在IoC容器中建立起了自己的資料結構以及相應的資料表示,但此時這些資料還不能讓IoC容器直接使用,需要在IoC容器中對這些BeanDefinition資料

手動實現Spring整合Tomcat容器

  幫助類 /** * @program: Maven * @description: spring整合進tomcat * @author: hw * @create: 2018-12-04 15:18 **/ public class SpringWebUtil {

springIOC模擬實現

        使用Spring框架已經有很長時間了,一直沒有仔細的想過框架的設計思想是什麼樣的,底層到底是怎麼實現的,這幾天除錯程式看了些原始碼,寫下來做個記錄。由於Spring框架博大精深,個人理解的難免有不正確的地方,希望看到的朋友可以指正,不勝感激。 一 什麼

框架漫談之spring()工廠模式實現程式解耦,spring框架的引出

1.程式的耦合度 我們來看如下的程式碼 // 表現層 public class UserController { public static void main(String[] args) { IUserService user

Spring5原始碼之IOC容器實現原理

1、 2、 3、定位、載入、註冊 定位:資源配置import,classpath,url 載入:解析配置檔案,把bean包裝成BeanDefinition物件 註冊:把已經初始化的BeanDefinition物件放到IOC容器中

從零開始寫C# MVC框架之--- 用autofac ioc 容器實現依賴注入

本章查找了一篇對依賴注入解釋得非常不錯的文章為基礎,再加上自己的理解,不然還真不好用語言的方式表達清楚,引用下面這位仁兄的文章 依賴注入產生的背景:         隨著面向物件分析與設計的發展,一個良好的設計,核心原則之一就是將變化隔離,使得變化部分發生變化時,不變部

手寫程式碼實現springioc功能

本篇文章和大家一起交流一下有關spring的ioc知識,並自己通過程式碼實現一個簡單版的spring的IOC功能。首先看一下IOC基礎知識的分享:一、分享Iteye的開濤對Ioc的精彩講解  首先要分享的是Iteye的開濤這位技術牛人對Spring框架的IOC的理解,寫得非常

Spring揭祕 學習筆記 (SpringIoC容器 二)

4.3.5 bean的 scope scope可理解為“作用域”。 scope用來宣告容器中的物件所應該處的限定場景或者說該物件的存活時間,即容器在物件進入其相應的scope之前,生成並裝配這些物件,在該物件不再處於這些scope的限定之後,容器通常會銷燬這

Spring學習() IOC容器學習

ext new 精通 sse lombok 對象 ima 屬性 趙麗穎 spring IOC (反轉控制) ==DI(依賴註入) == BeanFactory DI的概念:DI:既讓調用類對某一接口實現類的依賴由第三方註入,以移除調用類對某一接口實現類的依賴 IOC

異步線程池的實現)-------具體實現方法

fun format 測試 路徑 線程池。 用戶體驗 deb tar clas 本篇是這個內容的第一篇,主要是寫:遇到的問題,和自己摸索實現的方法。後面還會有一篇是總結性地寫線程池的相關內容(偏理論的)。 一、背景介紹 朋友的項目開發到一定程度之後,又遇到

spring源碼學習】springIOC容器之BeanFactoryPostProcessor接口學習

時機 process roc sta 自動 註解 lis nbsp factor 【一】org.springframework.beans.factory.config.BeanFactoryPostProcessor接口==>該接口實現方法的執行時機:該接口void

Spring框架—— IOC容器和Bean的配置

單引號 framework 將不 配置信息 init 字符串連接 生命 release exp 1 IOC和DI ①IOC(Inversion of Control):反轉控制。 在應用程序中的組件需要獲取資源時,傳統的方式是組件主動的從容器中獲取所需要的資源,在這樣的模