自己簡單實現Spring的IOC原理

Somersames發表於2017-04-20

控制反轉(Inversion ofControl,縮寫為IoC)

簡單來說就是當自己需要一個物件的時候不需要自己手動去new一個,而是由其他容器來幫你提供;Spring裡面就是IOC容器。
例如:
在Spring裡面經常需要在Service這個裝配一個Dao,一般是使用@Autowired註解:類似如下

 public Class ServiceImpl{
    @Autowired
     Dao dao;
     
    public void getData(){
        dao.getData();
    }

在這裡未初始化Dao直接使用是會報出空指標異常的,那麼在Spring裡面的做法就是通過反射來將需要的類幫你載入進來。

下面是一個例子模擬了Spring的DI和IOC

首先寫兩個註解模擬Spring的註解:

Entity註解代表的是Spring的@Service
@Target(ElementType.TYPE) // 類
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
}

代表的是Spring裡面的@Autowrid
@Target(ElementType.FIELD) //描述方法的
@Retention(RetentionPolicy.RUNTIME) // 僅執行時保留
public @interface Resources {
}

當註解建立完成之後再建立兩個類:
Rain類代表的是需要從其他地方獲取天氣資料(資料庫或者伺服器)

public class Rain {
    public void rain(){
        System.out.println("正在下雨"); // 為了方便直接寫了
    }
}

Weather類代表的是獲取到的天氣資料

@Entity
public class Weather {
    @Resources
    Rain rain;  // 這裡在後面通過反射直接注入rain

    public void weather_rain() {
        rain.rain();
    }

下面是通過反射來直接注入:
首先遍歷指定的包名:這一步先省略,
首先是建立一個List模擬Spring的bean容器,即將已經裝初始化好的帶有Entity註解的類全部初始化

public class Weather_reflec {
    List<Object> objectList ;  // 模擬Spring容器

    public Weather_reflec() {
        objectList= new ArrayList<Object>();
    }
// 在這裡其實最好的做法是先找出帶有註解的類,判斷帶有Entity註解再傳入。但是為了方便直接省略了
    public  void get_ref(Object object) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> clazz =object.getClass();
        if(clazz.isAnnotationPresent(Entity.class)){  //判斷是否帶有Entity註解
            Field[] fields =clazz.getDeclaredFields();  //獲取其變數
            for(Field field :fields){
                if(field.isAnnotationPresent(Resources.class)){  //判斷是否需要注入
                    Class<?> rainClass=Class.forName(field.getType().getName(),false,Thread.currentThread().getContextClassLoader());   // 這裡先將Rain類載入
                    field.set(object,rainClass.newInstance()); //賦給rain
                    objectList.add(object);   //最後將已將賦值後的Weather儲存進容器
                }
            }
        }
    }

    public List<Object> returnList(){
        return objectList;  //返回容器方便以後使用
    }

最後也就是模擬Controller裡面直接使用的

public class WeatherPrediction {
    public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
       WeatherPrediction weatherPrediction =new WeatherPrediction(); 
        Weather weather =(Weather)weatherPrediction.springDo();
        weather.weather_rain();  // 這裡如果是普通呼叫會報空指標異常,而容器卻為其將rain這個變數賦值了,所以可以正常輸出
    }
    /*
    模擬Spring啟動過程,這一步其實可以單獨寫一個類,這一步是容器該做的,而我們並不需要去管
     */
    public Object springDo() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Weather_reflec weather_reflec =new Weather_reflec(); // 啟動的時候就需要載入的
        Weather weather =new Weather();                     //掃描類註解後new操作然後進行下一步
        weather_reflec.get_ref(weather);                    // 將其類裡面的變數進行new操作並放入容器
        Object object =weather_reflec.returnList().get(0);
        return object;
    }


執行後輸出:正在下雨

在WeatherPrediction裡面並沒有對Rain進行一個new操作但是卻可以使用,這應該是最簡單的一個模擬Spring的IOC例子了,當然Spring的IOC容器比這個強大太多了,比如需要考慮執行緒安全,以及各種的細節問題

相關文章